跳到主要内容
3D Playground
WebGL 与着色器第 5 / 12 节

纹理与采样:过滤、环绕与 Mipmap

进阶WebGL 2

纹理就是一张可以被插值读取的数组

纹理是一块二维的像素数据。本例不加载任何图片,而是用 Uint8Array 逐像素画出棋盘格、金色圆环和紫色网格线,再用 gl.texImage2D 上传。片元着色器用 UV 坐标读取它:

glsl
uniform sampler2D uPattern;
in vec2 vUV;
void main() {
  outColor = texture(uPattern, vUV);
}

纹理单元与 sampler

GPU 有若干个纹理单元(texture unit),像一排插槽。流程是:gl.activeTexture(gl.TEXTURE1) 选中插槽,gl.bindTexture 把纹理插进去,再用 gl.uniform1i(loc, 1) 告诉 sampler 去 1 号插槽读。sampler uniform 里存的是单元编号,不是纹理对象。本例同时用了两个单元:0 号是重复的图案,1 号是一条只用一次的色带。

环绕模式(wrap)

UV 超出 0~1 时怎么办?

  • REPEAT:平铺,最常用。
  • MIRRORED_REPEAT:每隔一块镜像翻转,接缝更不明显。
  • CLAMP_TO_EDGE:一直重复边缘像素。

把 uv repeat 调到 2 左右再切换 wrap,差别一目了然:CLAMP_TO_EDGE 下只有一块图案,其余全是拉长的边缘像素。

过滤:放大与缩小

  • 放大(一个纹素覆盖多个像素,近处):NEAREST 呈现锐利的像素块,LINEAR 在 4 个纹素间双线性插值,变得平滑。
  • 缩小(多个纹素挤进一个像素,远处):只取一个样本会产生闪烁和摩尔纹。解决办法是 mipmap:gl.generateMipmap 预先生成 1/2、1/4……直到 1×1 的缩略图,GPU 按距离挑合适的层级。LINEAR_MIPMAP_LINEAR(三线性过滤)在两层之间再插值一次,效果最好。

各向异性过滤

地面以掠射角远去时,像素在纹理上的足迹是一个细长条,普通 mipmap 会过度模糊。EXT_texture_filter_anisotropic 扩展沿长轴多采几次,远处依然清晰。

把 MIN_FILTER 切到 NEAREST 并开着 scroll,盯着地平线附近:那些闪烁的噪点就是没有 mipmap 时的走样。

核心 API

gl.createTexturegl.texImage2Dgl.activeTexturegl.texParameterigl.generateMipmapsampler2Dtexture()EXT_texture_filter_anisotropic

动手试试

  1. 1把 MAG_FILTER 切到 NEAREST,近看圆环的像素块。
  2. 2把纹理尺寸 N 改成 16,再对比 LINEAR 与 NEAREST。
  3. 3在片元着色器里用 textureLod(uPattern, vUV, 4.0) 强制读第 4 级 mipmap。
运行中