本节摘要:纹理映射给每个像素一个"查表"的机会:模型顶点携带 UV 坐标,光栅化插值到像素,拿它去贴图里取颜色,几何复杂度不变而表面细节暴增。本节讲 UV 的展开与寻址、双线性过滤为什么比最邻近平滑、mipmap 如何解决"远处贴图闪烁"、以及法线贴图用三个通道假装几何起伏的戏法。像素在这一节获得"皮肤"。
阅读完本节,你应当能够:
纹理是一张二维图片,UV 坐标是它在归一化空间的地址:U 沿宽度、V 沿高度,范围通常 0 到 1。建模软件里的"展 UV"就是把三维表面剪开摊平到这张方形画布上,让每个顶点领到一个地址,三角形内的像素按重心坐标插值得到自己的地址,再去贴图查颜色。
import numpy as np def sample_uv(u, v, tex_w, tex_h, wrap="repeat"): # 归一化UV → 纹理像素坐标;wrap 决定超界行为 if wrap == "repeat": u, v = u % 1.0, v % 1.0 # 环绕:砖墙地板无缝平铺 elif wrap == "mirror": u = 1 - abs(u % 2 - 1) # 镜像:避免接缝方向突变 else: u = min(max(u, 0.0), 1.0) # 钳制:天空盒边界 return int(u * (tex_w - 1)), int(v * (tex_h - 1)) print(sample_uv(1.25, -0.1, 256, 256)) # 重复寻址下自动回卷 # 输出: (64, 230) 1.25 回卷为 0.25,-0.1 回卷为 0.9
重复寻址让一张小砖纹贴满整面墙,是地面、墙面材质的省料神器。建模侧的展开质量直接影响渲染:UV 岛拉伸过度,贴图像素密度不均,远看会"一块清晰一块糊"。
最邻近采样直接取最近像素,放大出现色块、缩小混叠闪烁(3.3 节已演示过)。双线性过滤对相邻四个纹素做加权混合,权重是 UV 落点的小数部分:
def bilinear_sample(tex, u, v): # tex 为 float 数组的 4 邻域双线性采样 h, w = tex.shape[:2] x, y = u * (w - 1), v * (h - 1) x0, y0 = int(np.floor(x)), int(np.floor(y)) fx, fy = x - x0, y - y0 top = tex[y0, x0] * (1-fx) + tex[y0, min(x0+1, w-1)] * fx bot = tex[min(y0+1, h-1), x0] * (1-fx) + tex[min(y0+1, h-1), min(x0+1, w-1)] * fx return top * (1-fy) + bot * fy tex = np.array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1.0, 1.0], [0, 0, 1.0, 1.0]]) # 右下角亮块的四乘四贴图 print(round(bilinear_sample(tex, 0.72, 0.72), 3), round(bilinear_sample(tex, 0.5, 0.5), 3)) # 输出: 0.688 0.0 靠近亮块的点拿到渐变亮度,正中心仍为 0——边界平滑过渡
双线性解决放大,解决不了缩小:当屏幕一个像素对应贴图几十个纹素时,逐像素取一个样本等于剧烈降采样,高频图案闪烁成噪点(1.3 节的混叠原理解释)。mipmap 的方案是预处理:贴图逐层减半,原尺寸、1/2、1/4……直到 1×1,渲染时按"屏幕上这个像素盖住多少纹素"选对应层级再采样:
def pick_mip_level(u0, v0, u1, v1): # 一条屏幕像素步长对应的UV跨度越大,说明贴图在屏幕上越小,用越高层级 du, dv = abs(u1-u0), abs(v1-v0) d = max(du, dv) import math return max(0, math.floor(math.log2(d))) if d > 0 else 0 print(pick_mip_level(0.50, 0.50, 0.503, 0.503)) # 一像素跨约 3‰ 的贴图 # 输出: 8 对应 256 分之一的层级——地面延伸到远处时自动换粗层级 # 相邻层级再三线性混合,切换处无跳变
代价是约三分之一额外显存,换来地面远端从"波光乱闪"变成"安静渐隐"。手机端常见带宽优化 ASTC 压缩格式,把 mipmap 与块压缩一起做进硬件。
颜色贴图骗眼睛说"这里有花纹",法线贴图更进一步:RGB 三个通道分别编码扰动后的法线 x/y/z,像素读到它就拿去替代插值法线参与光照——几何还是平的,光影却是起伏的。一面几百三角形的砖墙,配一张法线贴图,斜光下砖缝的立体感以假乱真。
法线贴图通道:R 存 x 偏移,G 存 y 偏移,B 存 z(平坦处接近 (128,128,255) 的蓝紫色) 坐标空间:切线空间——以顶点切线、副切线、法线为基,贴图法线描述"相对表面怎么歪" 好处:同一张贴图可贴到任何朝向的表面;物体旋转时法线跟随基变换
法线贴图管"近看的光影起伏",视差贴图再加视角位移,位移贴图(高度场)真的改动几何——细节层次逐级加码,显存与算力也逐级加码,按观感预算取用。
纹理问题的排查在工程里高频出现,把症状与根因对上号能省大量时间:整体马赛克块,是最邻近过滤(忘了开双线性);地面远处波纹乱闪,是 mipmap 没生成或被禁用;贴图在透视中扭曲抖动,是软件渲染器漏了透视校正插值(6.3 节);接缝处一条明显的错位线,是 UV 超界走了钳制而不是重复,或镜像方向没对齐;同一面墙近处清楚远处糊成一片色,是各向异性过滤(沿屏幕倾斜方向采多层 mipmap)没开——普通三线性在斜视角地面上的模糊是它的专属症状。
颜色与皮肤齐了,但灯下还缺一样东西——影子。5.4 节补上这块拼图,并把视野推向全局光照。