第 5 章 纹理与材质:给积木贴上”皮肤”
上一章我们讲完 3D 世界的”骨架”——Mesh,一堆三角形拼出形状。但你想想,光有骨架的模型,画出来是啥样?灰白、平板、像个石膏翻模。真人、真车、真石头,那漂亮的”皮”是从哪来的?
这一章就讲”皮”:怎么让一个三角形面,看起来像木头、像金属、像水、像皮肤的毛孔。它分两半:纹理(Texture,贴上去的那张图) 和 材质(Material,决定光打上去什么反应)。还有个关键配角 UV——上一章埋的钩子,这章解。
先抛个问题引你入戏:
你小时候玩过那种”立体拼图”吗?硬纸板剪出房子的六个面,再往每个面上贴一张印好的”砖墙图”。贴之前,图是平的;贴上去,房子就”有了墙”。游戏里给 3D 模型”贴图”,和这事有哪点像、又有哪点不一样?
带着这个类比,我们开始。
5.1 贴图是什么:一张 2D 图,靠 UV 把 3D 表面”展开包裹”
纹理 / 贴图(Texture),最朴素的理解就是”一张 2D 图片”。第 3 章你学过,图片就是一堆像素格。区别是:普通图片贴屏幕上给人看,而游戏贴图,是”贴到 3D 模型表面”给人看。
问题来了:模型是 3D 的(弯的、扭的),图是 2D 的(平的)。怎么把平的图,严丝合缝裹到弯的表面上,还不皱、不歪、不错位?
5.1.1 UV 展开:像剥橘子皮、像包礼物
你想给一个橘子拍照存档它的”皮纹”,但橘子是圆的,相机拍出来是平的——信息必然丢。反过来,你要把一张平面的图裹回橘子上,就得先决定”橘子皮的每一小块,对应图上的哪一小块”。
工程师的做法叫 UV 展开(UV Unwrapping):想象你拿把刀,沿某些缝把橘子皮割开、摊平成一整张 2D 的皮。摊平后的这张”皮平面图”,就是 UV 图——上面每个点,都能映射回橘子上的对应位置。
- 橘子 = 3D 模型表面。
- 摊平的皮 = UV 空间(一张 2D 图,坐标叫 u、v,范围 0~1)。
- 割开的缝 = 模型上”UV 接缝”,美术要尽量藏在看不见的地方(比如头发里、衣服褶皱里),免得贴图在缝处对不上。
所以 UV,就是上一章说的”顶点带着的 (u,v) 坐标”,它干的事就是:标好”模型这个顶点,对应贴图上哪个点”。 所有顶点都标好,三角形面内再插值,整张贴图就被”包”上了。
生活类比:UV 展开像给礼物包纸。礼物(3D 模型)是方的或奇形的,包装纸(2D 贴图)是平的。你沿着棱把纸折、剪、粘,让纸服帖地裹住礼物。包得好的,棱角对齐、图案连续;包砸了的,纸皱、图案错位、接缝露馅。美术做 UV,就是”包纸的师傅”。
5.1.2 为什么非得有 UV:没有它图就贴歪
你可能会想:能不能让引擎”自动”把图贴上去,不用美术手调 UV?理论上能(比如按位置投影),但自动贴会出问题:
- 拉伸(Stretching):一个细长的面,若 UV 比例和它实际长宽不一致,图被拉长成条纹(像把正方形图硬塞进竖长条窗口)。
- 错位/重复:没标 UV,引擎不知道哪块对哪块,图要么全贴同一角,要么乱飞。
- 接缝:复杂模型(人、树)不展开直接贴,图案会在不该断的地方断。
所以 UV 是”贴图能对准”的必要地图。它本质是美术在回答:“这个 3D 点,吃贴图上的哪一口。” 这也圆了上一章 4.3.3 的钩子——UV 解决”贴图定位”。
补充一个你常会用的小技巧:平铺(Tiling / Repeat)。很多表面(砖墙、草地、地板)图案是重复的。美术不必把整面墙画一张超大图,只需画一小块砖纹,再把 UV 坐标”超出 0~1 范围重复映射”,引擎就自动把小图平铺满整面。这既省贴图内存(一张小图铺全场),又靠重复避开”单张大图被拉糊”。Tiling 和 Offset(偏移)是材质面板里最常拧的两个 UV 旋钮——它们本质上就是”在 UV 空间里缩放、平移那张地图”。你调 Tiling,就是在调”这张包装纸在礼物上重复铺几遍”。
5.1.3 一张贴图能存不止”颜色”:颜色贴图只是其中一种
新手常以为”贴图 = 表面颜色照片”。其实贴图是一类”数据图”,UV 把它们贴到表面,图里的数值可以被材质拿来干不同事:
- 颜色贴图(Albedo / Diffuse):最直观,存”这表面本来是什么颜色”。你脑子里默认的”贴图”就是它。
- 法线贴图(Normal Map):存”表面微观朝向的扰动”,让平地看起来有凹凸划痕(配合上一章 4.3.4 讲的切线)。它不真加三角形,纯靠骗光照。
- 粗糙度贴图(Roughness)、金属度贴图(Metallic):告诉材质”这处多粗糙、多像金属”,服务 5.2 的光照。
- AO 贴图(Ambient Occlusion):存”哪些缝里更暗”,增加体积感。
所以”贴图”是一摞数据图层,靠同一套 UV 对齐,各自喂给材质的不同计算。你看到模型又精致又有质感,往往是五六张贴图叠出来的,不是一张照片那么简单。
5.1.4 Mipmap:贴图也玩”远小近大”的把戏
我再补一个你以后必会碰到、而且能再次印证主线的重要概念:Mipmap(多级渐远纹理)。
想象一个远处的小兵,他身上的 4K 贴图(4096×4096)在屏幕上只占十几像素。问题来了:你拿一张 4096 的图去填十几像素,GPU 得从图里”跳着采样”——结果因为图太密、屏幕太疏,采样点不公平,出现闪烁、噪点(aliasing,和第 3 章讲的锯齿同源)。而且每帧还硬读 64MB 大图,纯浪费带宽。
工程师的解法优雅:导入贴图时,引擎自动生成一串”缩小版”——原图、半大、四分之一大、八分之一……一路缩到 1×1。远处的物体,就用小一号的版本去贴。这串多分辨率图,就是 Mipmap。
它一举解决两件事:
- 画质:远处用小图,采样公平,不闪不噪。
- 性能:远处不读 64MB 大图,读几 KB 小图,带宽骤省。
你看,又是一刀”连续→离散”的补丁:真实物体远看细节本就糊,计算机用”预先生成不同清晰度版本、按距离挑合适的”去近似,利用的正是人眼”远处看不清”的弱点。 和第 8 章雾化(远处该糊)、第 3 章动态分辨率(运动时可降),同构到极点。
生活类比:你手机里存了照片的”原图”和”缩略图”。聊天发小图用缩略图(快、省流量),点开才加载原图(清)。Mipmap 就是给每张贴图预存了一整套”缩略图”,GPU 按物体离镜头的远近,挑对应尺寸的用。
5.1.5 纹素密度(Texel Density):贴图清晰度的”PPI”
把第 3 章的 PPI 概念搬过来,正好收尾贴图这半章。纹素密度,就是”模型表面每单位面积,摊到多少纹素”。它决定贴图看起来细不细。
- 同一张 4K 贴图,贴在一辆大卡车上(面积大),每平米摊到的纹素少 → 看着糊。
- 贴在一个小戒指上(面积小),每平米摊到的纹素多 → 看着极清晰。
这和第 3 章”同样 1080p,小屏清晰大屏发糙”完全同构:都是”总格子数固定,铺的物理面积越小密度越高越清晰”。所以美术做贴图有个铁律:按物体实际大小分配贴图分辨率——大物件给大贴图(或多张拼接),小物件给小贴图,让全场景”纹素密度”均匀,不会出现”近处戒指清晰、远处卡车马赛克”的违和。
你调游戏画质时常见的”纹理质量:高/中/低”档位,底层就是统一缩放所有贴图的分辨率(进而影响纹素密度和显存)。低端机选”低”,所有贴图变小、纹素密度下降、显存省下,代价是近看略糊——又是一次”用感知弱点换性能”的公开交易,和你第 3 章学的渲染分辨率、第 1 章学的”偷工”一脉相承。
这也解释了为什么 Mipmap 之外还要控制”贴图本身做多大”:Mipmap 管”远处的版本”,纹素密度管”近处铺多细”。两者一起,决定你眼睛最终看到的清晰度。又是”连续→离散”的粒度问题——只不过这次粒度叫”纹素密度”,和第 3 章”像素密度(PPI)“是亲兄弟。
5.2 材质:光的脾气——金属/木头/水区别在哪
贴图给了”皮的图案”,但还差最后一步:光打上去,这皮是什么反应? 这就是 材质(Material) 管的事。
你先感受一个事实:同样一张”灰色”,涂在金属上锃亮能照人,涂在木头上哑光温润,涂在水里通透流动。区别不在图,在材料对光的脾气。材质,就是这份”光的脾气说明书”。
5.2.1 反光、粗糙度、透明:材质的几个核心”脾气”
我用最直觉的几个维度拆材质,每个都对应现实里你见过的现象:
- 反照率 / 基础色(Albedo / Base Color):这东西”本身”是什么颜色(不受光影响的底色)。红苹果底子是红,铁底子是灰。对应 5.1.3 的颜色贴图。
- 金属度(Metallic):是金属还是非金属?金属几乎不”漫反射”(不把光 diffuse 成柔光),而是把光镜面反射成高光、且高光带自身颜色(金子是金色的反光,银子是银色)。木头、塑料是非金属,漫反射为主。
- 粗糙度(Roughness):表面多光滑?光滑→反光聚成锐利亮点(金属球、镜子);粗糙→反光散成模糊光晕(磨砂、墙壁、橘子皮)。粗糙度是”高光锐不锐”的旋钮。
- 透明 / 不透明度(Opacity / Alpha):透不透?玻璃、水、纱巾是半透或全透;石头、木头是不透。透明物体渲染起来更麻烦(要算前后遮挡、折射),所以引擎常把它单独标记。
现实对照:你摸一块大理石(低粗糙、非金属)vs 一块毛巾(高粗糙、非金属)vs 一块不锈钢(低粗糙、金属)。三者颜色可能都灰,但光照上去高光天差地别——这就是材质参数的物理来源。
5.2.2 Shader:材质的”配方”(程序)
那材质这些”脾气”,引擎拿去干啥?最终都要变成一个程序,去算”这个像素最后显示什么颜色”。这个程序叫 Shader(着色器)。
Shader,你可以理解为”材质的配方 + 厨师”。你给材质设定:金属度 0.8、粗糙度 0.2、贴图是这张——引擎(具体是 Shader)就按这套配方,结合光照(第 6、7 章细讲光源),算出表面每点最终该显示的颜色。
类比:材质像一份”菜品规格单”(金属度、粗糙度、用什么贴图),Shader 是”按规格单炒菜的厨师程序”。你改规格单上的数(参数),菜(画面)就变;换厨师(换 Shader 类型,如标准 PBR Shader、卡通 Shader),炒法(算法)就变,风格也变。
重点:Shader 不是”一个固定效果”,是一类你可编程的小程序。 默认材质用引擎内置 Shader(PBR,基于物理的渲染);你想做卡通描边、做水面波光,就写/改 Shader。但本章你只需懂:材质 = 一堆”光的脾气参数” + 一张配方(Shader),它决定”光打上来长啥样”。具体怎么写 Shader,是进阶级,先不碰。
5.2.3 PBR:为什么现代材质都讲”基于物理”
你可能会问:这些参数(金属度、粗糙度)是拍脑袋定的,还是有什么依据?现代游戏主流用 PBR(Physically Based Rendering,基于物理的渲染)——意思是这些参数尽量对应现实物理,让”金属就该像金属、木头就该像木头”,且在不同光照下都合理。
PBR 的好处:美术调一次材质,换到白天、夜晚、室内,都还像那么回事(因为参数是物理的,不是为某一张图定制的)。这正是”用现实规律代替手调特效”的思路——又回到第 1 章哲学:别糊弄,按真实物理来,反而省事又通用。
所以你以后在 Unity 里看到 Standard Material(标准材质),它就是一套 PBR 参数面板:Albedo / Metallic / Smoothness(光滑度,=1-粗糙度)/ Normal Map / … 你拧这些,就是在调”这块皮对光的脾气”。
给你一个上手直觉,省得你第一次面对材质面板发懵:拿到一个真实物体想还原,先问三问——“它是金属吗?“(定 Metallic 0 还是 1)、“它表面多光?“(定 Smoothness,光则高、毛则低)、“它的颜色图案长啥样?“(定 Albedo 贴哪张图)。三个回答一填,八成质感就出来了。剩下的法线、AO、粗糙度贴图,是锦上添花。你会发现调材质不是艺术创作玄学,是”把你对真实物体的观察,翻译成几个物理参数”——这恰恰是本教程一直教你的”溯源”思维。
5.2.4 纹理 vs 材质:别再混了
我把这两个词彻底分清,这是本章最容易糊的地方:
- 纹理(Texture) = 那张 2D 数据图(颜色、法线、粗糙度…),是”素材”,是”图案”。
- 材质(Material) = 把纹理 + 光的脾气参数 + Shader 配方,打包成”这块皮怎么显示”的说明书。
关系:材质引用纹理(“我的颜色用这张图,我的粗糙度用那张图”),材质引用 Shader(“按这个配方算”),材质带参数(“金属度 0.8”)。模型(Mesh)引用材质(“我这几个面穿这件皮”)。
一句话记忆:Mesh 是骨架,Texture 是图案素材,Material 是用图案+光脾气把骨架扮成实物的说明书。 三者各归各位,引擎把它们缝成一帧画面。
5.2.5 材质和光照是”合伙关系”:没有光,材质也瞎
我点破一个你容易孤立理解的点:材质自己不产生颜色,它要等”光”来激发。 一个纯金属材质,关在黑屋(无光)里就是黑的——因为金属只反射光,没光可反,就全黑。材质定义的”脾气”,是”有光打来时怎么反应”,不是”自己发什么光”。
这解释了为什么你调材质时,必须同时看场景里的灯光:同一个木头材质,正午直射光下亮黄温润,阴天漫射光下灰暗哑光,夜晚只有一盏暖灯时一半亮一半黑。材质是”配方”,光是”火候”,两者合起来才出”菜”。这也是为什么本教程把材质放这章、把光照留到后面单独讲——它们密不可分,但你得先懂”皮的脾气”才能懂”光怎么激发它”。
所以你排错时记住:模型发暗,不一定是材质调错了,可能是”光没打对”或”UV 接缝漏了光”或”法线翻了”。材质、光照、UV、法线,四个常常要一起看。这恰恰说明:图形是系统,不是单旋钮。
5.2.6 风格不止一种:卡通、像素风,都是”换 Shader+换材质”
顺带开阔下眼界:PBR(写实)不是材质唯一答案。你看到的:
- 卡通渲染(Toon / Cel Shading):故意把颜色量化成几档(像上色本),边缘加描边,高光画成一块硬邦邦的亮斑。它换了一套 Shader,材质参数变成”色阶数、描边粗细”。
- 像素风(Pixel Art):贴图本身就是低分辨率手绘像素图,再配合”不平滑缩放(nearest 采样)“,硬邦邦的格子感就是风格。
- 全息/发光风:材质设成”自发光(Emissive)“——不靠外来光,自己亮,常做霓虹、能量盾。
这些说明:“皮长啥样”完全由材质+Shader 决定,你可以写实也可以抽象。 理解了材质是”光的脾气说明书”,你就知道风格切换本质是”换一套说明书”,不是换模型。
5.3 为什么贴图要压缩:显存是金子
最后一节,连回你第 1 章就听过的”显存是金子”,以及第 3 章 3.4.4 算的那笔账:每个像素都是真金白银的显存。贴图,是显存最大的消耗户之一,所以它必须压缩。
5.3.1 一张未压缩贴图多大:吓人的账
你第 3 章算过:一张 1920×1080、RGBA 四字节的图 ≈ 8.3 MB。游戏贴图常见 2048×2048 甚至 4096×4096:
- 2048×2048 × 4 字节 ≈ 16 MB(一张图!)。
- 4096×4096 × 4 字节 ≈ 64 MB(一张图!!)。
一个角色可能用:颜色图 4K(64MB)+ 法线图 4K(64MB)+ 粗糙度图 2K(16MB)+ … 轻松 150 MB+ 一张皮。场景几百个物体,贴图总量轻松上 GB。而中端显卡显存才 4~8 GB,全塞贴图瞬间爆满,后面的帧直接崩。
所以贴图压缩不是”可选项”,是”活下去的必须”。
5.3.2 压缩思想:又利用感知弱点
贴图压缩,和你在第 9、10 章会学的音频/图片压缩,同一个祖宗:现实连续 → 离散 → 利用人眼/人耳弱点砍掉”看不出来/听不出来”的信息。
具体到贴图,有两类压缩:
第一类:CPU 侧通用压缩(如 JPEG / PNG)。 你熟悉的图片格式。JPEG 靠”人眼对颜色细节不敏感”砍高频;PNG 无损但体积大。问题是:它们解压后还是整张 RGBA 摊在内存里,没省显存;而且 GPU 不能直接吃 JPEG,得先解压。
第二类:GPU 专用压缩格式(如 DXT / BC / ASTC)。 这是游戏的关键。它们专为显卡设计,压缩后直接存在显存里、GPU 能当场读取不解码,显存占用骤降(常压到原来的 1/4 ~ 1/8),且读取快。
思想(你不需记具体格式):把贴图切成小块,每块用”几个代表色 + 每块内混合系数”近似——和”像素只存一个代表色”一脉相承,又是用有限的代表值近似连续细节。DXT/BC 是老一代(PC 为主),ASTC 是新一代(手机为主,更灵活)。你只需知道:引擎导入贴图时让你选”压缩格式”,选对了显存和带宽都救回来,选错要么爆显存要么手机不支持。
5.3.3 压缩的代价:瑕疵与权衡
没有免费午餐。压缩会引入瑕疵:
- 颜色断层、细节丢失(尤其渐变天空、细腻皮肤)。
- 某些格式不支持透明度,或透明质量糙。
所以美术的常态是按用途选格式:
- 颜色图:有透明用支持 Alpha 的格式,否则用更高压比的。
- 法线图:对精度敏感,选保真好的格式。
- 小 UI 图:可能干脆不压缩(量小、要清晰)。
这又回到老套路:每个参数/选择,都对应”对付某个具体问题”。压缩格式这旋钮,对付的是”显存不够 + 带宽不够”,代价是”可能有点糙”,美术按”这块图糙不糙得出来”去权衡。
5.3.5 纹理过滤:当”贴图像素”和”屏幕像素”对不齐时
我最后补一个和”像素”本章主线紧紧相扣的概念:纹理过滤(Texture Filtering)。它回答一个问题——贴图上的”纹素(texel,贴图的一个像素)“,和屏幕上的”像素”,大小、角度经常对不齐,GPU 怎么取色?
回想第 3 章:画面被切成像素格,每格一个颜色。贴图也是格(纹素)。当一块 3D 面贴了图、投到屏幕,一个屏幕像素可能”压”在贴图的好几个纹素之间,或反过来一个纹素被拉成好几个屏幕像素。取色取哪格?取错了就锯齿、闪烁。
GPU 用”插值”抹平:
- 最近邻(Nearest / Point):直接取最近的纹素。快,但放大贴图出现硬邦邦方块(像素风故意用这个)。
- 双线性(Bilinear):取周围四格纹素做加权平均,得到中间色。放大时柔化,不方块。绝大多数游戏默认。
- 三线性(Trilinear):在双线性基础上,再在 Mipmap 的相邻两级之间插值,远近切换不跳变。
- 各向异性(Anisotropic):专门对付”斜着看的地面/地板”——这种面在屏幕上一个方向被拉得很长,普通过滤会糊,各向异性按拉长方向补偿采样,地面远处依然清晰。你开车游戏里看向前延伸的公路特别需要它。
你看出门道没?过滤又是一次”离散纹素 → 用插值骗成连续”的补丁,和第 3 章”格子细到看不出”、第 9 章”采样点密到听不出”,同一条哲学的不同面孔。GPU 默认开双线性/三线性,就是替你做了这层”连续化”——你几乎无感,但关掉它(强迫最近邻)画面立刻变锯齿马赛克,你就知道它多重要。
生活类比:贴图像素像瓷砖,屏幕像素像你要铺的地面。瓷砖和地面格子不对齐时,与其硬切(最近邻,露出方块边),不如把相邻瓷砖颜色揉一揉再铺(双线性,接缝柔化)。过滤就是”铺砖师傅的揉色手艺”。
5.3.6 连回全教程的哲学:三章一条绳
你走到第 5 章末尾,应该能把 3、4、5 章和更早的 9 章,拧成一根绳了:
| 章节 | 现实(连续) | 离散化手段 | 留下的代价 | 利用的弱点 |
|---|---|---|---|---|
| 第 9 章 声音 | 连续声波 | 采样+量化 | 底噪、发闷 | 奈奎斯特:高频听不到 |
| 第 3 章 画面 | 连续颜色/运动 | 像素+帧 | 马赛克、卡顿 | 细格子/快翻看不出 |
| 第 4 章 形状 | 连续曲面 | 三角化 | 棱角(或 Low-Poly 风) | 密三角看不出 |
| 第 5 章 贴图 | 连续纹理细节 | UV 贴图+GPU 压缩 | 压缩瑕疵、接缝 | 颜色/细节不敏感 |
一模一样的骨架:现实是连续的 → 计算机只能存离散 → 工程师用”人感知不到”当掩护,把代价藏起来 → 落进引擎成一堆旋钮。 你从第 1 章餐厅比喻起,到坐标、雾化、声音、像素、三角形、纹理,全是这一种思维的不同化身。
这就是本教程想给你的”概念地图”:以后你在 Unity 材质面板、纹理导入设置里看到任何旋钮,都先问三句——它对应现实什么?没它出什么岔子?它利用了我哪个感知弱点?问完,旋钮就不再吓人。
下一章(如果你继续)会进”光从哪来、怎么打”——你会发现光照也是同一套:真实世界光无限复杂连续,引擎用离散光源 + 近似算法去逼近。绳还会接着牵。
【思考题】
- UV 是干什么的?用”剥橘子皮”或”包礼物”类比,解释为什么 3D 模型需要它。
- 纹理(Texture)和材质(Material)有什么区别?别混为一谈,各用一句话定义。
- 金属度和粗糙度,分别决定材质”对光的什么反应”?举一个”同样灰色、不同材质”的例子。
- 为什么游戏贴图非得用 GPU 专用压缩格式(如 DXT/BC/ASTC),而不能直接用 JPEG/PNG 就完事?
【参考答案】
- UV 是顶点带的 (u,v) 坐标,标好”模型这个 3D 点,对应贴图上哪个 2D 点”。类比:橘子(3D 模型)皮是弯的,要裹上平面包装纸(2D 贴图),得先沿缝把皮割开摊平(UV 展开),记下每小块皮对应纸上的哪块;没有 UV 这张”对应地图”,平图就不知往 3D 表面哪贴,会拉伸、错位、接缝乱。UV 就是”贴图定位地图”。
- 一句话分清:纹理(Texture)= 那张 2D 数据图(颜色/法线/粗糙度等),是图案素材;材质(Material)= 把纹理 + 光脾气参数 + Shader 配方打包成的”这块皮怎么显示”的说明书。 模型引用材质,材质引用纹理。
- 金属度决定”是不是金属”:金属几乎只镜面反光、高光带自身色;非金属漫反射为主。粗糙度决定”反光锐不锐”:低粗糙→锐利高光(镜子、金属球),高粗糙→弥散光晕(墙、橘子皮)。例子:同样灰色——涂不锈钢(金属度 1、低粗糙)锃亮能照人;涂灰木头(金属度 0、高粗糙)哑光温润。区别在材质参数,不在底图。
- 因为 JPEG/PNG 解压后仍是整张 RGBA 摊在内存、且 GPU 不能直接吃,要先解码占满显存又慢;而 GPU 专用格式(DXT/BC/ASTC)压缩后直接存显存、GPU 当场读取不解码,显存降到 1/4~1/8、带宽也省。游戏贴图是显存大户(一张 4K 图 64MB),不压缩直接爆显存崩帧,所以必须用专用格式。