问题描述
我正在尝试向我的 WebGL 程序添加环境映射(使用立方体映射):
https://roninbar.github.io/mobius/
我试着按照这个例子:
https://webglfundamentals.org/webgl/lessons/webgl-environment-maps.html
但出于某种原因,我不断收到这个讨厌的警告:
[.WebGL-00006F8000214F00] GL_INVALID_OPERATION: Texture format does not support mipmap generation.
并且立方体映射不起作用。
六个 cube images 都是 512×512 像素,因此 WebGL 为它们生成 mipmap 应该不会有任何问题。
完整的源代码是on GitHub。不过,以下是我认为相关的部分:
function loadTextureAsync(
gl: WebglrenderingContext,which: number,url: string,{ kind,target }: {
kind: 'TEXTURE_2D' | 'TEXTURE_CUBE_MAP';
target: 'TEXTURE_2D' | `TEXTURE_CUBE_MAP_${'POSITIVE' | 'NEGATIVE'}_${'X' | 'Y' | 'Z'}`;
} = { kind: 'TEXTURE_2D',target: 'TEXTURE_2D' }
): Promise<WebGLTexture> {
return new Promise(function (resolve,reject) {
const texture = gl.createTexture();
if (!texture) {
return reject(new Error('Failed to create texture object.'));
}
gl.activeTexture(which);
gl.bindTexture(gl[kind],texture);
gl.texImage2D(
gl[target],// level
gl.RGBA,// internalFormat
1,// width
1,// height
0,// border
gl.RGBA,// format
gl.UNSIGNED_BYTE,// type
null,);
const image = new Image();
image.src = url;
image.crossOrigin = '';
image.addEventListener('load',function () {
gl.activeTexture(which);
gl.bindTexture(gl[kind],texture);
gl.texImage2D(gl[target],gl.RGBA,gl.UNSIGNED_BYTE,image);
gl.texParameteri(gl[kind],gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);
gl.texParameteri(gl[kind],gl.TEXTURE_WRAP_T,gl.TEXTURE_MAG_FILTER,gl.LINEAR);
// WebGL1 has different requirements for power of 2 images
// vs non power of 2 images so check if the image is a
// power of 2 in both dimensions.
if (kind === 'TEXTURE_2D' && isPowerOf2(image.width) && isPowerOf2(image.height)) {
// Yes,it's a power of 2. Generate mips.
gl.generateMipmap(gl[kind]);
gl.texParameteri(gl[kind],gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR);
} else {
// No,it's not a power of 2. Turn off mips and set
// wrapping to clamp to edge.
gl.texParameteri(gl[kind],gl.LINEAR);
}
return resolve(texture);
});
return texture;
});
}
const loadAllTexturesAsync = async function () {
const promises: Promise<WebGLTexture>[] = [];
for (const which of [gl.TEXTURE20,gl.TEXTURE21,gl.TEXTURE22,gl.TEXTURE23]) {
promises.push(loadTextureAsync(gl,which,`${process.env.PUBLIC_URL}/texture/hours${which - gl.TEXTURE20}.bmp`));
}
promises.push(loadTextureAsync(gl,gl.TEXTURE10,`${process.env.PUBLIC_URL}/texture/mobius.png`));
for (const axis of ['X','Y','Z']) {
for (const sign of ['NEGATIVE','POSITIVE']) {
promises.push(loadTextureAsync(gl,gl.TEXTURE0,`https://webglfundamentals.org/webgl/resources/images/computer-history-museum/${sign.slice(0,3).toLowerCase()}-${axis.toLowerCase()}.jpg`,{
kind: 'TEXTURE_CUBE_MAP',target: `TEXTURE_CUBE_MAP_${sign as 'POSITIVE' | 'NEGATIVE'}_${axis as 'X' | 'Y' | 'Z'}`,}));
}
}
return Promise.all(promises);
};
loadAllTexturesAsync().then(function () {
gl.activeTexture(gl.TEXTURE0);
gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
gl.texParameteri(gl.TEXTURE_CUBE_MAP,gl.LINEAR_MIPMAP_LINEAR);
});
解决方法
我想通了。我的错误是我为整个多维数据集创建了六个不同的 WebGLTexture
而不是一个。我将其更改为以下内容:
function loadTextureAsync(
gl: WebGLRenderingContext,url: string,target: 'TEXTURE_2D' | `TEXTURE_CUBE_MAP_${'POSITIVE' | 'NEGATIVE'}_${'X' | 'Y' | 'Z'}` = 'TEXTURE_2D'
): Promise<void> {
return new Promise(function (resolve) {
const unit = gl.getParameter(gl.ACTIVE_TEXTURE);
const image = new Image();
image.src = url;
image.addEventListener('load',function () {
gl.activeTexture(unit);
gl.texImage2D(gl[target],gl.RGBA,gl.UNSIGNED_BYTE,image);
return resolve();
});
});
}
const loadAllTexturesAsync = async function () {
const promises: Promise<void>[] = [];
// Environment
gl.activeTexture(gl.TEXTURE0);
const texEnv = gl.createTexture();
if (!texEnv) {
throw new Error('Failed to create texture.');
}
for (const axis of ['X','Y','Z']) {
for (const sign of ['NEGATIVE','POSITIVE']) {
gl.bindTexture(gl.TEXTURE_CUBE_MAP,texEnv);
promises.push(
loadTextureAsync(gl,`${process.env.PUBLIC_URL}/texture/env/${sign.slice(0,3).toLowerCase()}-${axis.toLowerCase()}.jpg`,`TEXTURE_CUBE_MAP_${sign as 'POSITIVE' | 'NEGATIVE'}_${axis as 'X' | 'Y' | 'Z'}`),);
}
}
return Promise.all(promises);
};
loadAllTexturesAsync().then(function () {
gl.activeTexture(gl.TEXTURE0);
gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
gl.texParameteri(gl.TEXTURE_CUBE_MAP,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_CUBE_MAP,gl.TEXTURE_WRAP_T,gl.TEXTURE_MAG_FILTER,gl.LINEAR);
gl.texParameteri(gl.TEXTURE_CUBE_MAP,gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR);
});