WebGL 纹理过滤与环绕模式
一句话结论:过滤决定纹理放大缩小怎么取色,环绕决定 UV 越界怎么取色,两者配错就会出现锯齿、闪烁或边缘杂色。
纹理过滤方式选择
是什么
纹理过滤是 GPU 在采样时根据放大与缩小倍数决定像素取值的规则,分为放大过滤(MAG_FILTER)与缩小过滤(MIN_FILTER)两组,常用取值为 NEAREST 与 LINEAR,缩小过滤还可叠加 mipmap 层级。
解决什么问题
它解决纹素与屏幕像素数量不匹配的问题。一个屏幕像素可能对应纹理的多个纹素(缩小)或一个纹素的一部分(放大),若不做处理就会出现锯齿、摩尔纹与动画闪烁。
为什么这样做
采样本质是重采样信号处理。NEAREST 相当于零阶保持,速度最快但会产生硬边;LINEAR 做双线性插值,平滑但会略糊。mipmap 预先生成金字塔层,按像素覆盖的纹素范围选择层级,用少量显存换取远处画面的稳定与更好的缓存命中。
怎么用
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
// 带 mipmap 的缩小过滤(纹理必须是 2 的幂)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
gl.generateMipmap(gl.TEXTURE_2D);
| 取值 | 适用 | 表现 |
|---|---|---|
| NEAREST | 像素风、数据纹理 | 硬边、无插值、最快 |
| LINEAR | 照片、渐变 | 平滑过渡 |
| NEAREST_MIPMAP_NEAREST | 像素风远景 | 保持硬边且无闪烁 |
| LINEAR_MIPMAP_LINEAR | 三维场景远景 | 平滑且稳定,最常用 |
常见坑
- 生成 mipmap 却用非 2 的幂纹理 → 原因:WebGL 1.0 限制 → 处理:改用 CLAMP_TO_EDGE 加 LINEAR,或补到 2 的幂
- 只设 MIN_FILTER 忽略 MAG_FILTER → 原因:放大时画质差 → 处理:两组都显式设置
- 数据纹理被插值 → 原因:把数值当颜色采样 → 处理:数据纹理必须用 NEAREST 且禁用 mipmap
- 每帧重复生成 mipmap → 原因:误放在渲染循环 → 处理:只在纹理内容变化后生成一次
纹理环绕模式配置
是什么
环绕模式(WRAP_S 与 WRAP_T)决定 UV 超出 0 到 1 范围时如何取色,可选 REPEAT、MIRRORED_REPEAT 与 CLAMP_TO_EDGE。S 对应横向 U,T 对应纵向 V。
解决什么问题
它解决纹理坐标越界的取值问题。地面、墙面需要平铺重复,而图标、人物贴图则应当约束在边缘,否则会出现跨边缘的插值杂色。
为什么这样做
重复与钳制都是对采样坐标的取模或截断处理,由固定硬件实现,零额外开销。把规则交给环绕模式,着色器里就无需手写取模逻辑,也让图集与细节贴图可以复用同一份采样代码。
怎么用
gl.bindTexture(gl.TEXTURE_2D, tex);
// 地面与墙面:平铺
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
// 图标与角色:钳制到边缘
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
| 模式 | 行为 | 适用 |
|---|---|---|
| REPEAT | 对 1 取模后平铺 | 地面、墙面、天空盒 |
| MIRRORED_REPEAT | 镜像平铺,接缝连续 | 需要无接缝的装饰图案 |
| CLAMP_TO_EDGE | 取边缘像素 | 图标、角色、图集、阴影贴图 |
常见坑
- 图集出现边缘串色 → 原因:REPEAT 下插值到对侧 → 处理:改为 CLAMP_TO_EDGE 并给子图留边距
- 非 2 的幂纹理使用 REPEAT → 原因:WebGL 1.0 不支持 → 处理:改用 CLAMP_TO_EDGE 或补齐尺寸
- 平铺纹理接缝明显 → 原因:贴图边缘不连续 → 处理:使用可平铺贴图或 MIRRORED_REPEAT
- 只配置了 S 忽略 T → 原因:纵向仍为默认值 → 处理:两个方向都要显式设置
要点总结
- 放大用 MAG_FILTER,缩小用 MIN_FILTER,两组都要显式设置
- NEAREST 硬边适合像素风与数据纹理,LINEAR 平滑适合照片
- mipmap 解决远景闪烁,WebGL 1.0 下要求 2 的幂纹理
- 平铺用 REPEAT,图标与图集必须 CLAMP_TO_EDGE
- 环绕与过滤在绑定纹理后、绘制前一次性设置,不要每帧重复
