5.3 滤波与 mipmap:采样质量裁判


5.3 滤波与 mipmap:采样质量裁判

本节摘要:屏幕像素与纹理单元很少一一对应,滤波决定"取一个点还是插一片",mipmap 决定"查原版还是查缩版"。本节把放大、缩小两类问题分开讲透,解释 mip 金字塔的运作机制,并给出各向异性过滤与手动选层的实战用法。

学习目标

读完本节,你应当能够:解释邻近过滤与线性过滤的视觉差异及适用场景;说出 mipmap 链的构造与选层原理;面对远景闪烁与摩尔纹给出正确的采样配置;用 textureLod 手动控制模糊程度。

放大与缩小是两类不同的问题

把一张 256 见方的贴图贴到一个只占屏幕 64 像素的物体上,平均每个像素要代表 16 个纹理单元——信息太多了,只能挑。反过来,把 4K 贴图怼到一个占满全屏的物体上,像素又比纹理单元多——信息不够,只能猜。滤波就是在解决"挑"与"猜":

场景 问题 邻近过滤 线性过滤
放大(像素多) 需要插值补信息 色块感、马赛克 平滑、略糊
缩小(像素少) 需要概括降信息 闪烁、摩尔纹 好一些但远景仍乱

放大场景里,邻近过滤(NEAREST)取离 UV 最近的单个纹理单元,得到复古像素风;线性过滤(LINEAR)插值周围四个纹理单元,得到柔和平滑。两种都是合法风格,像素游戏刻意选前者。

缩小场景才是重灾区:单个像素代表一大片纹理单元时,"挑最近一个"完全是抽签——相机每挪一点,抽中的纹理单元就换一批,画面高频闪烁;规则条纹还会与像素网格干涉出摩尔纹。线性过滤帮一点忙但不够,因为问题不在插值而在"根本该换一张更小的图"。这正是 mipmap 登场的地方。

图:mipmap 金字塔与按距离选层

图:mipmap 金字塔与按距离选层

mipmap:用三成空间换稳定

mip 链的构造朴素而有效:把原图逐级减半预生成(1024、512、256……直到 1),加载时全部放进显存。采样时 GPU 根据该片元附近 UV 的变化率判断"这个像素大概代表多大一片纹理",直接取对应层级——远处取高层的模糊小图,近处取低层的清晰大图。闪烁与摩尔纹的根源(大图硬塞给少像素)从机制上消失。

显存代价约多三成(金字塔总体积是原图的 1.5 倍),换来的是远景稳定与缓存命中率上升,这笔账在几乎所有 3D 场景里都划算——mipmap 默认开几乎是行业共识。相邻层级直接切换会有接缝感,三线性过滤(在两个 mip 层之间再插值)让过渡不可察觉,通常与 mipmap 搭配出厂。

选层的依据是 UV 的屏幕空间导数——正是 4.2 节讲过的 fwidth 同族机制。这也解释了 5.2 节的提醒:旋转、极坐标这类打乱 UV 梯度的变换会误导选层,偏糊偏闪都要先想到这里。

各向异性过滤与手动选层

默认的 mip 选层按"最陡方向"取保守值,斜视地面这类场景会过度模糊——你斜着看一条长路,近处路面明明清晰却发糊。各向异性过滤(anisotropic filtering)允许沿纹理各方向分别采样多个 mip 层,斜视时保住该清晰的维度。它的开销随倍率上升,16x 是桌面平台司空见惯的设置,移动端酌情降档。

另一件趁手的工具是手动选层。texture 函数自动选层,textureLod 让你指定层级:

// 自动选层:常规采样 vec4 sharp = texture(uTex, vUv); // 手动指定第 3 层:等于预置模糊,做景深近似、光晕扩散都好用 vec4 blurry = textureLod(uTex, vUv, 3.0);

第三个参数是浮点数,允许小数(在两层之间取插值)。"用 lod 换模糊"是后处理章节会复用的技巧:泛光的高斯模糊要跑多趟,而廉价场景里一趟 textureLod 就能糊出雏形。lod 参数本身还能用噪声扰动,做出手绘抖动风格的采样。

⚠️ 常见坑:把贴图缩小寄望于"加载时省内存"是反向操作——mip 链只在加载原尺寸图时才有意义。给远景用的装饰贴图直接出小图即可,别用 4K 图加满级 mip 充数。

动手验证:把 mip 层级画成颜色

机制讲再多不如亲眼看一次选层。用一个调试技巧把"GPU 实际选了哪层"直接显示出来——用图层序号做色带:

#version 330 core in vec2 vUv; out vec4 FragColor; uniform sampler2D uTex; void main() { // textureQueryLod 返回驱动为该 UV 建议的 mip 层级(x 分量) float lod = textureQueryLod(uTex, vUv).x; // 调试色带:蓝(近,层级 0)渐变到红(远,高层级) vec3 dbg = mix(vec3(0.1, 0.3, 0.9), vec3(0.9, 0.2, 0.1), clamp(lod / 8.0, 0.0, 1.0)); FragColor = vec4(dbg, 1.0); // 正式输出注释备用:FragColor = texture(uTex, vUv); }

对着这个调试视图移动相机,能亲眼看到层级随距离爬升;换成 5.2 节的极坐标采样再看,会发现层级被虚高——那就是旋转 UV 干扰选层的实锤。textureQueryLod 属于较新的查询接口,环境不支持时可退而求其次:用 textureLod 手动遍历各层级采样对比,效果同等直观。

两问两答

问:为什么我的远景贴图看起来"水波纹"一样闪? 典型的 mip 缺失或选层不足症状。检查三项:贴图导入时 mip 链有没有生成;采样器有没有启用 mip 过滤;贴图尺寸是不是 2 的幂(部分移动环境缺一不可)。三项都正常还闪,上各向异性过滤。

问:UI 贴图需要 mipmap 吗? 多数不需要。UI 元素基本按像素一比一显示(不缩小),mip 链白白多占三成显存。反过来,3D 场景内所有会被透视缩小的贴图——墙面、地面、角色——都必须有。判断标准就一条:这张图会不会被缩小显示。

本节要点回顾

  • 两类问题分开治:放大靠插值补信息(像素风选邻近、平滑选线性),缩小靠降采样保稳定;
  • mip 链机制:逐级减半预生成,按 UV 变化率自动选层,显存加三成换远景稳定;
  • 三线性搭配:层级间再插值,切换无感,默认与 mipmap 成对出现;
  • 各向异性:斜视场景保住清晰维度,桌面 16x 常见,移动端按带宽降档;
  • textureLod:手动指定层级即预置模糊,是廉价模糊与风格化采样的入口。

素材质量问题收尾。下一章进入表现力的高地:光照模型,把贴图、法线、点积全部串成一条完整的光照链。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U