很多人在学到 LearnOpenGL 光照这一章时最容易产生一个错觉觉得前面的光照模型已经挺“像样”了于是到“光照贴图”这一节就有点不以为然——无非就是把漫反射颜色换成纹理采样有什么可讲的但当你真的把纯色立方体换成一块贴图木箱再把鼠标拖到视口里转一圈那种从“渲染Demo”到“真实物体”的差异会立刻把你拉回来光照贴图不是简单的“贴图”而是把材质从三组常量升级成一组随 UV 坐标变化的数据源。这篇文章我就以 LearnOpenGL 中光照贴图这一节为主线配合我实际跑通过的项目代码聊聊漫反射贴图、镜面反射贴图的原理与实现以及那些教程没明说、但非常容易卡住你的坑。1. 为什么纯色材质“一看就是塑料”光照模型与真实感之间的鸿沟在学会使用光照贴图之前我们几乎每个人都会经历这样一个阶段用材质类里的 ambient、diffuse、specular 三个 vec3 常量加上一个 float 型的 shininess把整个物体在光照计算里的反应定义为“一坨固定数值”。在 LearnOpenGL 前几节的例子里那个旋转的立方体看起来挺唬人有高光、有暗部、有环境光影响但当你看久了就会发现它越来越像一块塑料甚至像一块被刷了油漆的积木。问题出在哪并不是光照模型本身错了而是材质参数被“全局化”了。真实世界里几乎不存在一整块表面上到处都用同一组颜色和同一强度高光的物体。一块木箱的箱体是哑光木头但边角可能包着一圈金属条一块砖墙砖和砖缝的反光性质完全不同一个陶瓷杯白色釉面和底部未上釉的粗坯之间对高光的反应天差地别。而光照贴图要做的事正是把“物体表面每个位置的渲染属性”交给一张二维图像去描述。普通“贴图”与光照贴图的区别也在这里最早你在模型上贴一张颜色纹理那只是把颜色画上去而光照贴图的核心是让“渲染方程里的参数”随像素位置变化这才是它叫“Lighting Maps”而不是“Texture Mapping”的原因。1.1 回顾此前的光照模型把材质当成三个常数如果你回看 LearnOpenGL 前面的代码在“材质”那一节片段着色器里会有这样一个结构体struct Material { vec3 ambient; vec3 diffuse; vec3 specular; float shininess; };然后光照计算时直接拿material.diffuse去和光源的漫反射分量相乘拿material.specular去乘镜面反射分量。这套模型本质上是 Phong 或 Blinn-Phong 的一个简化版它假设物体表面每一处的材质响应都完全相同。对于一只抛光的金属球、一堵刷了腻子的白墙来说这种假设勉强够用一旦物体表面有纹理、有区域差异模型立刻露馅。所以当你在 LearnOpenGL 官网或任何教程里看到“材质”那一节时请先建立这样一个意识那只是材质系统的“常量版本”。接下来要做的所有工作都是把常量变成变量把“一个值管整个面”变成“每个像素查各自的值”。1.2 真实物体表面的“空间差异”如何打破这种空间差异怎么打破思路很直接我们在片段着色器里为这个片元拿到它对应的 UV 坐标然后用一张纹理图去查询该位置的属性值。这样做的好处很明显图是美术同学或设计师提前绘制好的物体表面每一个点的反光、颜色、粗糙度等属性都已经被编码在这张图里。运行时 GPU 只需要在采样器里做一次纹理读取就能拿到比常量丰富得多的材质信息渲染效果立刻拉高一个档次。注意这里说的“空间差异”不仅体现在颜色上还体现在高光强弱、自发光、法线偏移等各个方面。后面我们会看到镜面反射贴图利用的就是这个思路——只是它存储的不是颜色而是高光强度的“灰度图”。从实现角度讲你其实只需要记住一个核心公式任何一个sampler2D采样结果都可以替换掉材质结构体中对应的一个标量或向量。这是光照贴图这条线的所有变化的基础。我第一遍学的时候没意识到这一点结果在法线贴图、视差贴图那一节又重新绕回了同一个 개념白白浪费了不少时间。2. 漫反射贴图用一张“颜色查表”替换固定的漫反射常量漫反射贴图是所有光照贴图里最直观、最容易上手的一种。简单说它就是一张普通的颜色纹理存储的是物体表面每个位置对漫反射光线的基本响应——也就是“这地方的固有色是什么”。在代码层面你要做的核心修改是把片段着色器中原本作为vec3的material.diffuse换成sampler2D类型然后在计算光照前用texture(material.diffuse, uv)去采样。这看起来只是数据类型变化但背后有一个很重要的转变材质不再是一个常量而是一个按坐标查询的函数。以 LearnOpenGL 里经典的木箱为例木箱本身是木纹边角包着深色金属边。如果用纯色材质你只能把 diffuse 设成一个偏木色的 vec3但整个物体都会是那个颜色木纹完全不存在。而漫反射贴图只需一张木箱纹理图每个片元按自己的 UV 去采样木纹、金属边的颜色差异就全出来了而且光照计算还能根据每个像素不同的 base color 计算不同的明暗结果。2.1 采样漫反射贴图时的环境光处理有一个容易被新手忽略的细节环境光ambient怎么处理。教程里的常见做法是vec3 ambient light.ambient * texture(material.diffuse, uv).rgb;这里直接用漫反射贴图的采样结果去乘环境光颜色和强度。逻辑上是合理的环境光作为全局间接光的近似在 Phong 模型里也应该受到表面固有色调制。一个粗糙木头在暗环境里反射的环境光当然和一块绿布的 ambient 表现不同所以拿漫反射贴图采样结果调制环境光分量是对的。但要小心一点如果环境光强度偏大采样结果会直接透出“纹理原色”让物体像是一盏灯失去阴影感和立体感。实际项目中我会把环境光控制在很弱的水准或者在美术资源里把漫反射贴图整体压暗一点这样开出来更自然。教程代码里那个0.1、0.2的环境光系数不是随便写的它其实是在模拟“去掉强高光后依然保留暗部细节”的心理预期。2.2 纹理采样细节mipmap、重复模式与滤波漫反射贴图虽然概念简单但“用起来不卡”需要关注几个纹理参数。第一是生成 mipmap。在 LearnOpenGL 中加载纹理后都会调用glGenerateMipmap。当物体距离摄像机很近时GPU 会读取像素密度很高的纹理层级距离远时则使用缩小的 mipmap 层级避免闪烁。不生成 mipmap远处的小木箱会出现严重的摩尔纹和抖动。第二是环绕方式。对于木箱这类 UV 都在[0,1]范围内的贴图使用GL_REPEAT或GL_CLAMP_TO_EDGE都可以但如果是地板、墙面这类会平铺的场景就要刻意把 UV 放大并使用GL_REPEAT。我建议对每张纹理单独设置合适的参数别用一套参数打天下。很多初学者把GL_REPEAT和GL_CLAMP_TO_EDGE混着用结果贴图接缝处出现一条颜色异常的线排查起来还挺费劲。第三是滤波方式。如果追求画质用GL_LINEAR_MIPMAP_LINEAR如果在意性能或风格化效果可以选点采样加 mipmap。同时可以开启各向异性过滤对倾斜视角下的纹理清晰度提升非常明显尤其在木地板这类大平面场景里。关于各向异性过滤我后面会专门提一下因为它的参数设置同样有讲究。3. 镜面反射贴图用灰度图控制高光的“区域开关”漫反射贴图解决了“物体不同位置颜色不同”的问题但另一个问题立刻浮现高光怎么办用同一组 specular 常量的话你会遇到一个很尴尬的情况木箱的木纹部分和金属边框有着一模一样的高光强度。于是木头在光源照射下会像塑料一样泛出一大片刺眼的白光。这显然不对真实木头几乎不产生强烈的镜面高光而金属边则会有集中的亮斑。镜面反射贴图Specular Map就是为这个场景准备的。它通常是一张灰度图图中每个像素的亮度值对应物体表面该位置的镜面反射强度。亮度越高的地方比如金属边上高光越强亮度越低甚至为黑色的地方比如木纹表面几乎不产生高光。在片段着色器里代码只比漫反射贴图多一步vec3 specular texture(material.specular, uv).rgb;然后用它去乘以光照计算里的镜面反射分量。这里的uv和漫反射贴图使用的是同一套纹理坐标。也就是说你需要为同一个物体准备两张图一张存颜色一张存高光强度。两张图的 UV 空间要完全对应否则会出现高光位置和贴图纹理错位的诡异效果。3.1 为什么镜面反射贴图通常是灰度图镜面反射强度在物理意义上是个标量理论上用一个通道就能表示。所以很多美术资源里镜面贴图直接存成 R 通道或干脆存成一张灰图。你在着色器里采样.rgb也行只取.r通道也可以。不过要注意现代渲染工具里“高光贴图”这个概念正在被“粗糙度贴图”取代。PBR 工作流中一般不再使用 specular map而是用 roughness map 来反推高光分布。但理解镜面反射贴图的思想对你后续理解 roughness 贴图、金属度贴图非常有帮助——本质上都是用贴图按像素控制某个光照参数。我甚至建议你在项目里把镜面贴图命名为_spec.png而不是_specular.png因为后面切换 PBR 时你会发现这个文件可能要改成_rough.png提前养成资源命名习惯能省很多事。3.2 shininess 是全局的镜面贴图是局部的两者的分工还有一个小坑值得强调在 LearnOpenGL 的材质结构体里shininess仍然是float是全局的。很多初学者会想既然高光强弱由镜面贴图控制那 shininess 是不是没用了不是。shininess 控制的是高光的聚拢程度——指数越大高光越锐利越小指数越小高光越发散越大。而 specular 贴图控制的是不同区域“愿意反射多少高光”。你可以把 shininess 当成一个全局的“高光锐利度”参数把 specular 贴图当作局部的“高光遮罩”。一个金属边框区域即使反射率高如果 shininess 很小高光也会糊成一片反过来如果 shininess 很大则是一颗清晰的星点。两者是乘积关系不要混为一谈。这里有个很常见的调试误区当你发现木箱的金属边高光太强或者太弱时第一反应是改shininess。但如果你把 shininess 从 32 改成 128金属边和木纹的高光会一起变化反而破坏了原本想要的对比。正确做法是回去改镜面反射贴图本身把金属边区域画得更亮或更暗。shininess 只在所有材质区域需要同时变锐利或变模糊时才动它。4. 完整代码演进从 uniform 材质到纹理材质的每一步现在我跟你走一遍实际改动。以下代码基于 LearnOpenGL 的示例工程但我把关键点做了提纯。如果你已经跑通了前几节改起来其实很快但有几个细节值得单独拎出来说。4.1 顶点着色器纹理坐标要送到片段着色器顶点着色器在原有position、normal属性基础上需要新增一个纹理坐标属性#version 330 core layout (location 0) in vec3 aPos; layout (location 1) in vec3 aNormal; layout (location 2) in vec2 aTexCoord; out vec2 TexCoord; out vec3 FragPos; out vec3 Normal; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { FragPos vec3(model * vec4(aPos, 1.0)); Normal mat3(transpose(inverse(model))) * aNormal; TexCoord aTexCoord; gl_Position projection * view * vec4(FragPos, 1.0); }这里有个细节法线变换要使用逆转置矩阵处理非等比缩放但这一步对光照贴图本身没有直接影响。纹理坐标则直接传递到片段着色器由插值机制自动计算每个片元的 UV。如果你的模型在远处出现纹理扭曲第一步就该检查这个TexCoord插值是否正常而不是怀疑光照计算。4.2 片段着色器光照计算中的贴图采样片段着色器中材质的定义从 vec3 改为 sampler2Dstruct Material { sampler2D diffuse; sampler2D specular; float shininess; };主函数里的光照计算中原来写material.diffuse的地方改成texture(material.diffuse, TexCoord).rgb原来写material.specular的地方改成texture(material.specular, TexCoord).rgb。环境光同理vec3 ambient light.ambient * texture(material.diffuse, TexCoord).rgb; vec3 diffuse light.diffuse * diff * texture(material.diffuse, TexCoord).rgb; vec3 specular light.specular * spec * texture(material.specular, TexCoord).rgb;需要注意的是texture()函数接收的是sampler2D与 UV 坐标。如果你的顶点着色器里把 UV 命名成了别的名字记得保持一致。同时texture的返回值是vec4我们这里取.rgb作为颜色alpha 通道暂不参与计算。如果后续你要做透明度裁剪或混合alpha 通道会在那里派上用场。4.3 C 端的纹理加载和 uniform 设置C 端要做三件事加载两张纹理、绑定纹理单元、给 uniform sampler 赋值。加载纹理的方法与普通纹理一致unsigned int diffuseMap loadTexture(wood.png); unsigned int specularMap loadTexture(wood_specular.png);loadTexture内部用 stb_image 加载并设置GL_TEXTURE_MIN_FILTER、GL_TEXTURE_MAG_FILTER、GL_TEXTURE_WRAP_S/T等参数。有一点我踩过坑图片加载后要记得调用glGenerateMipmap否则缩小采样时会出现严重噪点。绘制循环中在绑定 VAO 之后需要给每个采样器指定纹理单元glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, diffuseMap); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, specularMap); glUseProgram(shaderProgram); glUniform1i(glGetUniformLocation(shaderProgram, material.diffuse), 0); glUniform1i(glGetUniformLocation(shaderProgram, material.specular), 1);很多人会犯一个错误忘了调用glActiveTexture直接往GL_TEXTURE0上绑第二张纹理结果第二张图覆盖了第一张。正确做法是每绑定一张纹理前先切换到下一个纹理单元。glUniform1i设置的是“我要用第几个纹理单元”不是纹理对象 ID初学者经常在这两个概念上绕晕。4.4 纹理单元与 GPU 状态机的理解关于纹理单元我想多花两段讲清楚因为这是光照贴图代码里最容易写错、但教程里往往快速带过的部分。在 OpenGL 中纹理单元可以理解成 GPU 上一排编号的“插槽”。你的着色器里声明了uniform sampler2D diffuse但这个 uniform 本身不直接持有纹理对象 ID它只记录一个整数索引表示“我要从这个编号的插槽里取纹理”。所以运行时你必须先在 CPU 端把纹理对象绑定到某个插槽glActiveTexture(GL_TEXTURE0)glBindTexture再把这个插槽编号告诉 uniform。还有一个很多人不知道的小细节glActiveTexture在当前上下文里是“有状态”的。如果你在绑定第二张纹理前忘了切换插槽第二张纹理就会被绑到第一个插槽上覆盖掉第一张。我曾经因为这段代码在帧首尾重复执行而产生过诡异的“贴图闪烁”后来才发现是绘制调用之间纹理单元状态没有 reset。建议你在每个绘制循环开头显式地把用到的纹理单元按固定顺序绑定一次不要依赖上一帧的状态。5. 光照贴图最容易翻车的现场与完整排查链路光照贴图这一节真正让新手抓狂的往往不是概念而是“代码逻辑看起来完全没问题画面却总是不对”。我把自己踩过、以及帮别人排查过的几类高频问题列在这里并给你一套排查思路。5.1 物体整个变成黑色或完全看不见最常见的元凶有三个。第一个是纹理没有成功绑定到对应的纹理单元。症状是物体纯黑或者只显示环境光因为texture()对一个没有绑定数据的sampler2D采样返回的是(0,0,0,1)所有漫反射和镜面反射结果都变成 0。排查时我在代码里加一个断言或打印检查glGetError()是否有GL_INVALID_OPERATION或者直接简化先只采样一张图确认纹理能显示再逐步加第二张。第二个是纹理加载失败。路径写错、图片损坏、stb_image 加载失败后没有退出导致绑定了一个0号纹理。排查时输出加载结果确认图片宽高和通道数正常。我在项目里习惯写一个loadTexture函数内部加载失败直接printf错误信息并exit(-1)省得后面产生一堆莫名其妙的黑屏。第三个是 UV 坐标没有被真正从顶点着色器传过去。如果顶点数据和属性指针里没有配置layout(location 2)顶点着色器收到的纹理坐标可能是垃圾数据采样出来的颜色自然不对。这种情况尤其阴险因为物体表面还会显示颜色只是整体错位或扭曲。你可以在片段着色器里临时写一句FragColor vec4(TexCoord, 0.0, 1.0);看看屏幕上的 UV 分布是否合理。5.2 高光一大片“塑料反光”镜面贴图像没生效这种往往不是没采样而是镜面贴图被“整张采样”后又乘了一个过大的全局系数。比如把light.specular设成(1.0, 1.0, 1.0)且强度高而镜面贴图虽然是灰度图亮部却很多那么高光范围依然会很大。另一种可能是你的镜面贴图本身做反了。很多美术同学会从网上随便找一张图当 specular map但那些图可能有倒置关系——亮的地方对应木头暗的地方对应金属。排查方法是把采样结果直接输出到屏幕看看高光区域是否和 UV 上金属边框的位置匹配。我通常会在调试时临时写一句FragColor texture(material.specular, TexCoord);如果屏幕显示的高光强度和纹理细节一致说明采样链路没问题问题在光照强度参数上。如果是反的把贴图在图片编辑器里反色一次就行。5.3 纹理上下颠倒或者远处纹理疯狂闪烁Y 轴翻转是最经典的问题。stb_image 默认图片第一行是顶部而 OpenGL 纹理坐标 UV 的原点通常在左下角所以很多教程会在加载前调用stbi_set_flip_vertically_on_load(true);如果你用的是 LearnOpenGL 的纹理加载封装这一步已经内置了。但如果你自己在某个封装上二次开发忘了加就会出现纹理上下颠倒。记住这不是渲染数据问题是约定差异。另外如果你把翻转用到了 UI 贴图或某些特殊通道图上反而可能造成双翻转所以要按资源约定来。我见过有人把 DDS 纹理和 PNG 纹理混用一个需要翻转一个不需要结果整个项目里的贴图东倒西歪。远处的纹理闪烁十有八九是没生成 mipmap。没有 mipmap 的纹理在降采样时会走最粗糙的插值导致高频细节混叠看起来就是一片毛刺。解决办法是加载后调用glGenerateMipmap并把GL_TEXTURE_MIN_FILTER设为GL_LINEAR_MIPMAP_LINEAR。注意如果GL_TEXTURE_MIN_FILTER没有设置成 mipmap 版本即使生成了 mipmap 也不会使用。5.4 颜色发灰、发暗或过曝sRGB 与伽马空间隐患光照贴图本身不强制引入伽马校正但只要你的漫反射贴图是美术画出来的 sRGB 图片渲染在默认的线性空间里就会偏亮或偏暗因为纹理里的 0.5 并不是线性光照计算里的 0.5。教程为了简单通常先跳过伽马校正但如果你在实做时发现颜色始终不太对并且你的漫反射贴图明显是“给显示器看的图”而不是“给光照计算用的数据”那你就要考虑要么把纹理格式指定为GL_SRGB8_ALPHA8让硬件在采样时自动做 sRGB 到线性的转换要么在校色阶段做一次 gamma 校正。这块会和后处理管线联动牵一发动全身所以建议先跑通光照贴图再回头处理色彩空间。还有一点很隐蔽镜面反射贴图通常不需要做 sRGB 转换因为它存的是强度值而不是颜色。如果你把两张贴图都设置成GL_SRGB8_ALPHA8specular 贴图会被错误地解算一遍 sRGB导致高光强度整体变暗。这是“同一套加载函数引起两种故障”的典型案例我在项目里会把漫反射贴图和法线/高光数据贴图分开用两个加载函数。5.5 各向异性过滤和纹理压缩当物体表面是倾斜的大平面时默认的 mipmap 会让远处的纹理变得模糊这时候各向异性过滤能明显改善。启用方式是在纹理参数里设置glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f);但要注意有的驱动实现下GL_TEXTURE_MAX_ANISOTROPY_EXT是 4、8、16 这样的量化值设太大不见得有效反而可能增加显存带宽开销。我的做法是先确认GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT的上限再按项目期望设一个 4x 或 8x。实机项目中4x 和 16x 的画质差异通常不明显但性能差距却很实在。纹理压缩是另一个经常被问起的点。光照贴图通常数量不多单张几 MB 无所谓但到了大场景或者后续做 PBR 需要多张贴图albedo、normal、roughness、metallic、AO显存和带宽压力就上来了。常见方案是压缩纹理格式BC1/BC3/BC7 等或者在美术侧把贴图合理图集化。不过压缩格式有平台差异桌面 OpenGL 一般用GL_COMPRESSED_RGBA_S3TC_DXT5_EXT等扩展移动端则更多用 ETC2/ASTC。这块建议在进入项目后期再做前期先保证正确性。6. 光照贴图之后的路视差、PBR 与 GPU 资源控制学完光照贴图我觉得你其实已经掌握了一个很重要的抽象能力用纹理去参数化物体表面的材质属性。这个思路可以一直延伸到很多高级特性。6.1 法线贴图与视差贴图从“颜色和反光”到“几何细节”光照贴图解决的是颜色与高光的空间差异但物体的表面凹凸细节比如墙上的砖缝、地面的裂缝仍然是靠几何模型来支撑的。如果每个砖缝都建摸顶点数会爆炸。法线贴图Normal Map的思路是在光照计算时用一张法线贴图替代几何法线欺骗光照模型让低模表面呈现高模的明暗起伏。而视差贴图Parallax Mapping则更进一步根据视角方向对 UV 进行偏移让凹凸表面产生更真实的遮挡与位移感。LearnOpenGL 后续章节里有完整的视差贴图教程建议看完光照贴图后立刻衔接。当初我学到这里时最大的感悟是光照贴图、法线贴图、视差贴图、粗糙度贴图本质上都是“用纹理数据作为光照计算参数”区别只是参数不同。你一旦把“texel 查表”这套思维建立起来后面会轻松很多。视差贴图尤其依赖 UV 的正确性——如果光照贴图阶段的 UV 链路有问题到了视差贴图会被放大成更严重的扭曲和穿帮。6.2 从 Lighting Maps 到 PBR物理参数替代经验参数LearnOpenGL 的另一个重要章节是 PBR。它会告诉你传统的 Phong/Blinn-Phong 光照模型里diffuse 和 specular 参数是经验的、不物理的而 PBR 则用 albedo、metallic、roughness 这样更贴近物理的参数来描述表面。但它在工程上的实现依然是“用贴图按像素控制这些参数”——albedo 贴图、金属度贴图、粗糙度贴图就是光照贴图思想的延续。所以我的建议是不要觉得光照贴图只是入门章节。如果你能把下面这个问题想透彻——为什么漫反射贴图可以影响环境光而金属度贴图不能直接混合到漫反射颜色里——你对整个 PBR 工作流的理解就会扎实很多。在 PBR 工作流里资源数量会从两张图暴增到五张甚至更多。很多人第一反应是“这也太吃显存了”但实际上 PBR 算法本身对纹理的利用率更高因为每个通道都被赋予了明确物理含义R 通道存 roughnessG 通道存 metallicB 通道存 AO一张纹理可以打包三个参数这比传统光照贴图里单独用一张灰度图存 specular 要高效得多。你从光照贴图阶段就开始养成“一图多通道”的思维对后期优化很有帮助。6.3 贴图数量增加后如何降低 GPU 占用随着后续纹理种类变多漫反射、法线、金属度、粗糙度、AO、自发光GPU 的显存带宽和采样压力会明显上升。在不算激烈的实机场景下常见优化方案有以下几类使用合适的纹理压缩格式桌面用 S3TC/BC移动端用 ETC2/ASTC可以显著降低显存占用。正确使用 mipmap 和各向异性过滤能提升渲染质量同时减少带宽浪费。合并 UV 图集或使用 texture array减少绘制调用时的纹理切换次数。在阴影/后处理等低需求的 pass 中尽量复用低分辨率或低 mip 层级的贴图。对静态物体做 AO/光照烘焙Lightmap运行时只做一次查询不再每帧动态计算。这些话术虽然听起来像优化清单但“降低 GPU 占用”的核心永远不是某一个技巧而是先量清楚瓶颈在谁的带宽。建议先在 profiler 里看纹理采样次数和显存带宽再决定压缩还是降分辨率。很多人在光照贴图阶段就开始焦虑性能其实没必要——这阶段一个场景里可能只有两三个物体、四五张纹理瓶颈根本不在这里等场景规模上去了你自然会发现该砍哪一刀。最后说点私人的体会。光照贴图是 LearnOpenGL 里我最喜欢的一节不是因为它的效果有多炫而是因为它让“材质”这个概念变得立体起来材质不再是一组写死在代码里的参数而是与美术资源、UV 布局、纹理滤波、色彩空间深度绑定的数据通道。你从这一节开始才算真正进入“渲染工程”的门槛。如果你刚刚学到这里别急着追求后面那些高级技术先把漫反射贴图和镜面反射贴图这一套流程亲手跑通尤其是把“纹理单元”和“采样器绑定”这两件事搞明白后面会少踩很多坑。