为什么自定义的纹理没有显示出来?这是因为 WebGL 对纹理有一种严格的限制,在两个维度上都不是 2 的幂。2 的幂是1,2,4,8,16,32,64,128,256,512,1024,2048,等等。假如 3D 物体的面长宽是 256 x 256。256 是 2 的幂,而自定义的纹理图的长宽为 320 x 240。这两个都不是2的幂,因此尝试显示纹理时会显示失败。在着色器中,当调用 texture2D 并且引用的纹理设置不正确时,WebGL 将使用黑色的颜色(0,0,0,1)代替纹理。
解决方案:
要解决这个问题,我们需要设置 wrap mode 为 CLAMP_TO_EDGE,并且设置 filtering 为 LINEAR 或者 NEAREST,这样就关闭了 mip mapping。代码如下:
import fTextureImg from '../../assets/f-texture.jpg'
// ... 中间其他代码 ...
// Create a texture.
const texture = gl.createTexture()
gl.bindTexture(gl.TEXTURE_2D, texture)
// Fill the texture with a 1x1 blue pixel.
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([0, 0, 255, 255]))
AsynLoadImage(fTextureImg, (image) => {
// Now that the image has loaded make copy it to the texture.
gl.bindTexture(gl.TEXTURE_2D, texture)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image)
/*** 下面是解决纹理渲染失败的关键代码 ***/
// Check if the image is a power of 2 in both dimensions.
if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
// Yes, it's a power of 2. Generate mips.
gl.generateMipmap(gl.TEXTURE_2D)
} else {
// No, it's not a power of 2. Turn off mips and set wrapping to clamp to edge
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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
}
drawScene()
})
// 下面是用到的自定义的函数
function isPowerOf2 (value) {
return (value & (value - 1)) === 0
}
function AsynLoadImage (url, cb) {
const img = new Image()
img.src = url
if (img.complete) {
cb(img)
return
}
img.onload = function () {
img.onload = null
cb(img)
return
};
img.onerror = function () {
img.onerror = null
cb(img)
}
}
附录:
WebGL API 的 WebGLRenderingContext.texParameter[fi]() 方法用于设置纹理参数.
void gl.texParameterf(GLenum target, GLenum pname, GLfloat param);
void gl.texParameteri(GLenum target, GLenum pname, GLint param);
target 是 GLenum 指定绑定点(目标)。可能的值:
gl.TEXTURE_2D : 二维纹理.
gl.TEXTURE_CUBE_MAP : 立方体纹理.
当使用 WebGL 2 context 时,还可以使用以下值
gl.TEXTURE_3D : 三维贴图.
gl.TEXTURE_2D_ARRAY : 二维数组贴图.
pname 参数是 Glenum 指定要设置的纹理参数. param 参数是 GLfloat 或 GLint 已指定的 pname 参数的值。
pname |
描述 | 参数 |
|---|---|---|
| Available in WebGL 1 | ||
gl.TEXTURE_MAG_FILTER |
纹理放大滤波器 | gl.LINEAR (默认值), gl.NEAREST. |
gl.TEXTURE_MIN_FILTER |
纹理缩小滤波器 | gl.LINEAR, gl.NEAREST, gl.NEAREST_MIPMAP_NEAREST, gl.LINEAR_MIPMAP_NEAREST, gl.NEAREST_MIPMAP_LINEAR (默认值), gl.LINEAR_MIPMAP_LINEAR. |
gl.TEXTURE_WRAP_S |
纹理坐标水平填充 s |
gl.REPEAT (默认值),gl.CLAMP_TO_EDGE, gl.MIRRORED_REPEAT. |
gl.TEXTURE_WRAP_T |
纹理坐标垂直填充 t |
gl.REPEAT (默认值),gl.CLAMP_TO_EDGE, gl.MIRRORED_REPEAT. |
Additionally available when using the EXT_texture_filter_anisotropic extension |
||
ext.TEXTURE_MAX_ANISOTROPY_EXT |
纹理最大向异性 | GLfloat 值. |
| Additionally available when using a WebGL 2 context | ||
gl.TEXTURE_BASE_LEVEL |
纹理映射等级 | 任何整型值. |
gl.TEXTURE_COMPARE_FUNC |
纹理对比函数 | gl.LEQUAL (默认值), gl.GEQUAL, gl.LESS, gl.GREATER, gl.EQUAL, gl.NOTEQUAL, gl.ALWAYS, gl.NEVER. |
gl.TEXTURE_COMPARE_MODE |
纹理对比模式 | gl.NONE (默认值), gl.COMPARE_REF_TO_TEXTURE. |
gl.TEXTURE_MAX_LEVEL |
最大纹理映射数组等级 | 任何整型值. |
gl.TEXTURE_MAX_LOD |
纹理最大细节层次值 | 任何整型值. |
gl.TEXTURE_MIN_LOD |
纹理最小细节层次值 | 任何浮点型值. |
gl.TEXTURE_WRAP_R |
纹理坐标r包装功能 |
gl.REPEAT (默认值), gl.CLAMP_TO_EDGE, gl.MIRRORED_REPEAT. |
参考引用:
https://webglfundamentals.org/webgl/lessons/webgl-3d-textures.html
https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/texParameter