You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

问题原因及修复方案

核心问题分析

  1. 绘制模式错误:使用gl.TRIANGLE_STRIP绘制3个顶点时无法生成有效三角形,该模式需要至少4个顶点,应改用gl.TRIANGLES。
  2. 未初始化的属性变量:顶点着色器中的offset和hue属性未绑定数据,WebGL会为未初始化的属性提供默认值,但这会导致渲染逻辑异常,甚至无法渲染图形。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 11:15:28