纹理就是一张可以被插值读取的数组
纹理是一块二维的像素数据。本例不加载任何图片,而是用 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把 MAG_FILTER 切到 NEAREST,近看圆环的像素块。
- 2把纹理尺寸 N 改成 16,再对比 LINEAR 与 NEAREST。
- 3在片元着色器里用 textureLod(uPattern, vUV, 4.0) 强制读第 4 级 mipmap。
还没有输出。console.log 的内容会显示在这里。
运行中