返回
小程序WebGL之HDR纹理绘制后黑屏问题排查
前端
2023-12-29 09:43:25
众所周知,小程序WebGL坑比较多,其中一个算是HDR,会在多次进出页面偶现全黑、全半黑、一半亮一半黑的情况。这让人很头疼,排查起来也比较费时。
最近遇到了一个类似的问题,尝试把对应的envMap的纹理绘制出来后发现问题了:envMap纹理绘制到canvas上,结果很不幸,确实出现了和HDR同样的问题。所以猜测是硬件问题?
但是还需要排除Three的问题,所以需要编写纯WebGL的demo看是否也存在这个问题。
排查过程
编写一个简单的纯WebGL的demo,绘制一个HDR的球体。
const canvas = document.getElementById('canvas');
const gl = canvas.getContext('webgl');
// 顶点着色器
const vsSource = `
attribute vec3 aPosition;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
void main() {
gl_Position = vec4(aPosition, 1.0);
vTexCoord = aTexCoord;
}
`;
// 片段着色器
const fsSource = `
precision mediump float;
varying vec2 vTexCoord;
uniform sampler2D uTexture;
void main() {
gl_FragColor = texture2D(uTexture, vTexCoord);
}
`;
// 编译着色器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vsSource);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fsSource);
gl.compileShader(fragmentShader);
// 创建程序对象
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
// 获取属性位置
const positionLocation = gl.getAttribLocation(program, 'aPosition');
const texCoordLocation = gl.getAttribLocation(program, 'aTexCoord');
// 获取uniform位置
const textureLocation = gl.getUniformLocation(program, 'uTexture');
// 创建缓冲区对象
const positionBuffer = gl.createBuffer();
const texCoordBuffer = gl.createBuffer();
// 绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// 设置缓冲区数据
const positions = [
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0,
1.0, 1.0, 0.0,
-1.0, 1.0, 0.0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
// 绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
// 设置缓冲区数据
const texCoords = [
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texCoords), gl.STATIC_DRAW);
// 创建纹理对象
const texture = gl.createTexture();
// 绑定纹理对象
gl.bindTexture(gl.TEXTURE_2D, texture);
// 设置纹理参数
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// 设置纹理图像
const image = new Image();
image.onload = function() {
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
gl.generateMipmap(gl.TEXTURE_2D);
draw();
};
image.src = 'envmap.jpg';
function draw() {
// 使用程序对象
gl.useProgram(program);
// 绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// 启用属性
gl.enableVertexAttribArray(positionLocation);
// 设置属性指针
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
// 绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
// 启用属性
gl.enableVertexAttribArray(texCoordLocation);
// 设置属性指针
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
// 绑定纹理对象
gl.bindTexture(gl.TEXTURE_2D, texture);
// 设置uniform
gl.uniform1i(textureLocation, 0);
// 清除画布
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// 绘制
gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
}
运行这个demo,发现果然也会出现同样的问题。
解决方案
经过一番排查,最终发现问题出在HDR纹理的mipmap上。禁用mipmap后,问题消失。
因此,解决方案就是禁用HDR纹理的mipmap。
总结
通过这个案例,我们了解到小程序WebGL中HDR纹理绘制后出现黑屏问题可能是由于mipmap造成的。禁用mipmap可以解决这个问题。