跳到主要内容

纹理采样与上传

纹理是 GPU 上的采样源,不是 <img> 句柄。生产上传走 WebGL2 的 不可变存储texStorage2D 一次定尺寸,再 texSubImage2D),不要每帧 texImage2D 重分配。图像原点在左上,drawing buffer 在左下——几乎总会碰到 UNPACK_FLIP_Y_WEBGL

1. 创建与采样

export function getMaxTextureSize(gl: WebGL2RenderingContext): number {
const value: unknown = gl.getParameter(gl.MAX_TEXTURE_SIZE);
if (typeof value !== "number") throw new TypeError("MAX_TEXTURE_SIZE");
return value;
}

export function createTexture2d(
gl: WebGL2RenderingContext,
width: number,
height: number,
): WebGLTexture {
const max = getMaxTextureSize(gl);
if (width > max || height > max) {
throw new RangeError(`texture ${width}x${height} > MAX_TEXTURE_SIZE ${max}`);
}
const tex = gl.createTexture();
if (!tex) throw new Error("createTexture failed");
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, width, height);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
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);
gl.bindTexture(gl.TEXTURE_2D, null);
return tex;
}

mip:texStorage2D 的 levels > 1,再逐级 texSubImage2D,或完整 0 级后 generateMipmap。缩小采样却关 mip,远处闪烁。重复纹元用 REPEAT,但非 2 次幂在 WebGL2 已合法。

各向异性是扩展,不是核心:

export function enableAnisotropy(gl: WebGL2RenderingContext, cap = 4): void {
const ext = gl.getExtension("EXT_texture_filter_anisotropic");
if (!ext) return;
const raw: unknown = gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
const max = typeof raw === "number" ? raw : 1;
gl.texParameterf(
gl.TEXTURE_2D,
ext.TEXTURE_MAX_ANISOTROPY_EXT,
Math.min(cap, max),
);
}

适用:斜视地面、瓦片地图、远处 UI 图集。
不适用:1:1 屏幕空间 blit、像素风(会把近邻滤掉)。

2. 图像 / canvas / 视频

export function uploadImage(
gl: WebGL2RenderingContext,
tex: WebGLTexture,
source: TexImageSource,
flipY: boolean,
): void {
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY ? 1 : 0);
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, source);
gl.bindTexture(gl.TEXTURE_2D, null);
}
做法不要做的事
<img> / ImageBitmap一次 texSubImage2D先画进 2D canvas 再上传(多一次拷)
2D canvas可以,但每帧都是 CPU→GPU 整张拷用 2D 当暂存再进 GL 做滤镜热路径
&lt;video>创建时按 videoWidth 分配;每帧 texSubImage2D每帧 texImage2D / texStorage2D 重建

视频要等到有当前帧(readyState >= 2),否则上传空帧。跨源图 / 视频没 CORS 会 污染 上下文,之后 readPixels / toBlob 失败,见 08。

自己填的 Uint8Array 若已经是 GL 左下原点,不要再开 UNPACK_FLIP_Y_WEBGL。DOM 图和自产缓冲不要共用同一套 unpack 状态。

3. 尺寸与格式

MAX_TEXTURE_SIZE 常见 4096–16384,移动端按 探测值 夹紧,不要写死 16k。超了 texStorage2D 失败或驱动黑纹理。压缩格式(S3TC / ASTC / ETC2)按扩展探测,iOS 不要假设 DXT。

WebGPU:device.createTexture({ size, format, usage }),从 canvas/video 用 copyExternalImageToTexture;Y 翻转在 copy 的 flipY,默认纹理 (0,0) 在左上,见 NDC 与 Y 轴

4. 失败形态

症状原因
贴图上下颠倒忘了或重复了 UNPACK_FLIP_Y_WEBGL
远处闪、近处糊MIN 要 mip 却 levels=1,或没 generateMipmap
斜视地面发虚没开各向异性(扩展为 null 则接受)
真机黑图桌面正常超过 MAX_TEXTURE_SIZE,或压缩格式不支持
视频只显示第一帧每帧没 texSubImage2D,或暂停后还以为会自己更新
readPixels SecurityError跨源纹理污染了 drawing buffer

权威资料

核对日期:2026-08-26