I'm trying to add environment mapping (using cube mapping) to my WebGL program:
https://roninbar.github.io/mobius/
I tried to follow this example:
https://webglfundamentals.org/webgl/lessons/webgl-environment-maps.html
but for some reason I keep getting this pesky warning:
[.WebGL-00006F8000214F00] GL_INVALID_OPERATION: Texture format does not support mipmap generation.
and the cube mapping doesn't work.
The six cube images are all 512×512 pixels, so WebGL is not supposed to have any problems generating mipmaps for them.
The full source code is on GitHub. Here are the parts I think are relevant, though:
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],
0, // 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], 0, gl.RGBA, 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.CLAMP_TO_EDGE);
gl.texParameteri(gl[kind], 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.TEXTURE_MIN_FILTER, 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.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
});
I figured it out. My mistake was that I created six different
WebGLTextures instead of just one for the whole cube. I changed it to the following: