WebGL绘制三角形后调用readPixels无法获取对应像素的原因
WebGL2中drawArrays后readPixels无法获取像素的问题解决
我在绘制三角形后尝试用readPixels获取像素信息,但以下代码无法实现预期效果;但改用clearColor这类清屏方法时,readPixels却能正常工作。
有问题的代码
JavaScript
const gl = document.querySelector("canvas").getContext("webgl2", { preserveDrawingBuffer: true }); render(); read(); // read in other event function render() { const fragment_center = `#version 300 es precision highp float; in vec3 HSV; out vec4 fragColor; vec3 hsv2rgb(vec3 c) { vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } void main() { vec3 color = hsv2rgb(HSV); fragColor = vec4(color, 1.0); } `; const vertex_center = `#version 300 es precision highp float; in vec2 position; in vec2 offset; in float hue; out vec3 HSV; void main() { float x = offset.x + position.x + 0.15; float y = offset.y + position.y + 0.3; HSV = vec3(hue, y, x); gl_Position = vec4(position, 1.0, 1.0); } `; const program = initProgram(gl, vertex_center, fragment_center); const radius = 1; const x1 = Math.cos((30 / 180) * Math.PI) * radius; const y1 = Math.sin((30 / 180) * Math.PI) * radius; const v_triangle = new Float32Array([0, radius, x1, -y1, -x1, -y1]); let buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, v_triangle, gl.STATIC_DRAW); let gl_position = gl.getAttribLocation(program, "position"); gl.vertexAttribPointer(gl_position, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(gl_position); gl.drawArrays(gl.TRIANGLE_STRIP, 0, v_triangle.length / 2); } function read() { const pixel = new Uint8Array(4); gl.readPixels(0, 0.5, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel); log(pixel); } function log(...args) { const elem = document.createElement("pre"); elem.textContent = [...args].join(" "); document.body.appendChild(elem); } function initProgram(gl, v, f) { const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, v); gl.compileShader(vertexShader); let status = gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS); if (!status) alert(gl.getShaderInfoLog(vertexShader)); const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, f); gl.compileShader(fragmentShader); status = gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS); if (!status) alert(gl.getShaderInfoLog(fragmentShader)); const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); gl.useProgram(program); return program; }
HTML
<canvas></canvas>
可正常工作的对比代码(使用clearColor)
JavaScript
const gl = document.querySelector("canvas").getContext("webgl"); render(); read(); // read in same event function render() { gl.clearColor(.25, .5, .75, 1); gl.clear(gl.COLOR_BUFFER_BIT); } function read() { const pixel = new Uint8Array(4); gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel); log(pixel); } function log(...args) { const elem = document.createElement("pre"); elem.textContent = [...args].join(' '); document.body.appendChild(elem); }
HTML
<canvas></canvas>
问题原因及修复方案
核心问题分析
- 绘制模式错误:使用
gl.TRIANGLE_STRIP绘制3个顶点时无法生成有效三角形,该模式需要至少4个顶点,应改用gl.TRIANGLES。 - 未初始化的属性变量:顶点着色器中的
offset和hue属性未绑定数据,WebGL会为未初始化的属性提供默认值,但这会导致渲染逻辑异常,甚至无法渲染图形。 - readPixels坐标错误:WebGL的像素坐标原点在左下角,且坐标必须为整数,你传入的
0.5是浮点数,且该位置可能不在三角形渲染区域内。
修复后的完整代码
const gl = document.querySelector("canvas").getContext("webgl2", { preserveDrawingBuffer: true }); // 设置canvas尺寸,确保像素坐标对应 gl.canvas.width = 400; gl.canvas.height = 400; render(); read(); function render() { const fragment_center = `#version 300 es precision highp float; in vec3 HSV; out vec4 fragColor; vec3 hsv2rgb(vec3 c) { vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } void main() { vec3 color = hsv2rgb(HSV); fragColor = vec4(color, 1.0); } `; // 移除未使用的属性,避免未初始化问题 const vertex_center = `#version 300 es precision highp float; in vec2 position; out vec3 HSV; void main() { // 直接使用position生成HSV值 HSV = vec3(0.5, position.y + 0.5, position.x + 0.5); gl_Position = vec4(position, 0.0, 1.0); } `; const program = initProgram(gl, vertex_center, fragment_center); // 三角形顶点(NDC坐标,确保在画布可见区域) const v_triangle = new Float32Array([ 0, 0.5, // 顶部 0.5, -0.5, // 右下 -0.5, -0.5 // 左下 ]); let buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, v_triangle, gl.STATIC_DRAW); let gl_position = gl.getAttribLocation(program, "position"); gl.vertexAttribPointer(gl_position, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(gl_position); // 清除画布背景 gl.clearColor(0, 0, 0, 1); gl.clear(gl.COLOR_BUFFER_BIT); // 使用正确的绘制模式gl.TRIANGLES gl.drawArrays(gl.TRIANGLES, 0, v_triangle.length / 2); } function read() { const pixel = new Uint8Array(4); // 读取画布中心的像素(整数坐标,对应三角形区域) gl.readPixels(200, 200, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel); log(pixel); } function log(...args) { const elem = document.createElement("pre"); elem.textContent = [...args].join(" "); document.body.appendChild(elem); } function initProgram(gl, v, f) { const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, v); gl.compileShader(vertexShader); let status = gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS); if (!status) alert(gl.getShaderInfoLog(vertexShader)); const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, f); gl.compileShader(fragmentShader); status = gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS); if (!status) alert(gl.getShaderInfoLog(fragmentShader)); const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); gl.useProgram(program); return program; }
内容的提问来源于stack exchange,提问作者Link
相关产品推荐
相关产品推荐

