3、采样器状态:寻址模式、过滤模式与LOD控制
采样器状态,说白了就是告诉GPU「怎么读纹理」的一套参数。很多开发者觉得这玩意儿简单,不就是设置几个枚举值嘛。但我见过太多项目,因为采样器配置不当,导致画面出现奇怪的接缝、闪烁,甚至性能雪崩。今天咱们就把这块彻底讲透。
3.1 寻址模式:纹理坐标超出[0,1]怎么办?
纹理坐标通常范围是0到1。但实际渲染中,坐标超出这个范围太常见了。比如做地形拼接、天空盒、或者UV动画时。GPU怎么处理这些「越界」坐标?这就是寻址模式要解决的问题。
| 模式 | 行为 | 典型场景 |
|---|---|---|
| Wrap(重复) | 坐标取小数部分,无限重复 | 地板、砖墙、重复纹理 |
| Clamp(钳制) | 超出部分取边界像素 | UI、天空盒、单张贴图 |
| Mirror(镜像) | 像照镜子一样翻转重复 | 对称图案、角色贴图 |
| Border(边框色) | 超出部分返回固定颜色 | 裁剪、遮罩、调试 |
Wrap模式是最常用的。比如一块草地纹理,你铺100米长,UV坐标从0到100,GPU自动取小数部分,纹理就无缝重复了。但这里有个坑——如果纹理本身左右边缘颜色不一致,Wrap模式会产生明显的接缝。我曾在项目里遇到一个地形,远处看有一条条竖线,排查了半天才发现是纹理边缘没做无缝处理。
Clamp模式适合那些「只出现一次」的纹理。比如UI上的按钮,或者天空盒。坐标超出1.0时,GPU直接取最边缘的像素。嗯,这里要注意:如果纹理边缘是纯色还好,如果是渐变,Clamp会导致边缘出现一条硬边。我曾经用Clamp做水面反射,结果岸边出现了一条亮线,后来改成Mirror才解决。
Mirror模式比较有意思。它像镜子一样,0到1正常显示,1到2是镜像翻转,2到3又正常。这种模式特别适合做对称图案,比如角色的左右脸贴图。我个人习惯在做角色皮肤贴图时用Mirror,这样左右脸纹理完全对称,省去手动调整的麻烦。
Border模式用得少,但关键时刻能救命。比如做裁剪效果,或者调试时想看看哪些像素超出了纹理范围。我曾经用它来排查UV溢出问题——把Border设成亮红色,一眼就能看到哪些地方坐标越界了。
性能提示:寻址模式本身几乎不影响性能。但Wrap模式会导致纹理缓存命中率下降,因为相邻像素的纹理坐标可能跳到很远的地方。Clamp模式缓存最友好,因为相邻像素大概率访问同一块纹理数据。
3.2 过滤模式:像素与纹素之间的博弈
纹理采样时,一个屏幕像素往往对应多个纹素(缩小),或者一个纹素对应多个像素(放大)。怎么从离散的纹素中计算出连续的像素颜色?这就是过滤模式的工作。
3.2.1 点过滤(Point)
最简单粗暴。取最近的纹素颜色。速度快,但效果嘛……放大时全是马赛克,缩小时会闪烁。我建议只在像素艺术风格或者调试时用。比如你做《我的世界》风格的游戏,点过滤反而更有味道。
3.2.2 双线性过滤(Bilinear)
取周围4个纹素做加权平均。效果比点过滤好很多,放大时边缘平滑。但缩小时依然会闪烁,因为GPU只用了最近一层的Mipmap。这是大多数游戏的最低配置。我个人习惯在移动端用双线性,性能开销小,效果也能接受。
3.2.3 三线性过滤(Trilinear)
在两层Mipmap之间再做一次插值。说白了就是「双线性 + Mipmap层间插值」。效果比双线性好,缩小时不会闪烁。但开销也大了一倍。我一般在PC端默认用三线性,移动端看情况。
3.2.4 各向异性过滤(Anisotropic)
这是最高级的过滤模式。它考虑了视角方向。比如你斜着看地面,纹理在屏幕空间是拉伸的。普通过滤会模糊,各向异性过滤会沿着拉伸方向多采样几个点,保持远处纹理的清晰度。
各向异性过滤的采样数通常是2x、4x、8x、16x。数值越大,效果越好,开销也越大。我实测过,4x和8x在视觉上差异不大,但性能开销差了一倍。所以我的建议是:PC端用4x或8x,移动端用2x或者干脆关掉。
我的经验:各向异性过滤对性能的影响,其实比很多人想象的小。因为GPU硬件专门优化过。但要注意,它和Mipmap配合使用效果最好。没有Mipmap的各向异性过滤,性能会差很多。
3.3 LOD偏置与Clamp:控制纹理细节的阀门
Mipmap是纹理的「金字塔」,从大到小一层层预计算好。GPU根据像素在屏幕上的大小,自动选择用哪一层。但有时候自动选择的结果并不理想,这时候就需要LOD偏置和Clamp来干预。
3.3.1 LOD偏置(LOD Bias)
LOD偏置就是在GPU自动计算的LOD层级上,加上一个偏移量。正值让GPU用更模糊的层(更小的Mipmap),负值让GPU用更清晰的层(更大的Mipmap)。
举个例子:你做一个雪地场景,远处雪地纹理自动选了第4层Mipmap,看起来有点模糊。你希望远处也能保持一些细节,就可以把LOD偏置设为-0.5或-1.0,让GPU偏向使用更清晰的层。
但要注意,负偏置会导致远处纹理闪烁,因为Mipmap层级切换更频繁了。我曾在项目里为了追求清晰度,把LOD偏置设成-2.0,结果远处地面一直在闪,像水波纹一样。后来改成-0.5才稳定下来。
3.3.2 LOD Clamp
LOD Clamp限制了GPU能使用的Mipmap层级范围。比如你设置MinLOD=0, MaxLOD=3,那GPU最多用到第3层Mipmap,再远也不会用更模糊的层。
这个参数在两种场景下特别有用:
- 防止过度模糊:有些纹理在远处会模糊成一团,你可以用MaxLOD限制一下,让远处至少保留一些细节。
- 性能优化:如果你知道某些纹理永远不会被近看,可以设置MinLOD=2,跳过最大的几层Mipmap,节省显存和带宽。
避坑指南:我曾经在一个VR项目里,为了节省显存,把所有纹理的MaxLOD都设成了4。结果用户转头时,远处的纹理突然变清晰,产生了明显的「弹入」效果,非常出戏。后来我根据纹理的重要程度,分别设置不同的Clamp值,才解决了这个问题。
3.4 实战建议:采样器配置的黄金法则
说了这么多,总结几条我自己的经验法则:
- 寻址模式:默认用Wrap,UI和天空盒用Clamp,对称贴图用Mirror,调试用Border。
- 过滤模式:移动端用Bilinear,PC端用Trilinear,需要远处清晰度时开Anisotropic 4x。
- LOD偏置:默认0,需要更清晰时设-0.5,不要超过-1.0。
- LOD Clamp:不限制时性能最好,但显存占用大。根据纹理使用频率,适当设置MaxLOD。
最后说一句:采样器状态不是设一次就完事的。不同纹理、不同场景、不同平台,最优配置都不一样。我建议你在项目初期就建立一个采样器配置表,针对每种纹理类型(漫反射、法线、遮罩等)分别设置。这样后期调优时,改一个参数就能全局生效,省心很多。