WebGL 从离屏画布中正确读取像素

6
我知道有很多相关资源,但是没有一个能够帮助我。
其中包括:webgl readpixels is always returning 0,0,0,0,
还有这个:https://dev59.com/PKPia4cB1Zd3GeqPsxTH\ 以及这个:Read pixels from a WebGL texture , 但都没有成功或有用。 目标:使用WebGL着色器渲染离屏画布,然后将其作为纹理在单独的WebGL着色器中使用。 备注:
  • 对于这些WebGL着色器,我正在使用通用顶点着色器用于像素着色器,具体来说就是光线追踪器/光线行走器。 这是:attribute vec2 a_position; void main() { gl_Position = vec4(a_position.xy, 0.0, 1.0); }。这个顶点着色器输入了覆盖整个屏幕的两个三角形,因此基本上片段着色器处理所有工作。
问题:为了从离屏画布中获取图像数据,我尝试了以下方法:
  1. 使用WebGL的gl.readPixels函数
var capturedImageData = new Float32Array(screenWidth * screenHeight * 4);
gl.readPixels(0, 0, screenWidth, screenHeight, gl.RGBA, gl.FLOAT, capturedImageData);
  1. 使用另一个canvas与getContext('2d')一起使用
var offscreenCanvas = document.createElement("canvas");
offscreenCanvas.width = screenWidth;
offscreenCanvas.height = screenHeight;
var ctx = offscreenCanvas.getContext('2d');
ctx.drawImage(glCanvas, 0, 0);
var capturedImageData = ctx.getImageData(0, 0, screenWidth, screenHeight);

这两种方法都会导致capturedImageData数组被填充为
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, . . .,这明显是不正确的。
如果有人对这个问题有经验,帮助将会非常感激。谢谢!
程序的直接链接是: https://www.khanacademy.org/cs/-/6289631977619456

你有检查 JavaScript 控制台吗?你应该会收到一个错误。 - gman
欢迎来到Stack Overflow!很高兴您找到了解决问题的方法。然而,实际的答案/解决方案不应该被编辑到您的问题中。通常情况下,您应该[编辑]问题以澄清它,但不要在其中包含答案。您应该创建自己的答案(如果不存在),并使用您用于解决问题的代码/解决方案,然后接受它(系统可能需要48小时延迟才能这样做)。当您自己解决了问题时,鼓励回答自己的问题 - double-beep
啊,好的,感谢你的帮助!我现在就去做。 - xacer
2个回答

4

您应该始终查看浏览器的JavaScript控制台(按F12)或从菜单中选择。我应该为您的代码显示错误。

var capturedImageData = new Float32Array(screenWidth * screenHeight * 4);
gl.readPixels(0, 0, screenWidth, screenHeight, gl.RGBA, gl.FLOAT, capturedImageData);

const gl = document.createElement('canvas').getContext('webgl');
const screenWidth = 300;
const screenHeight = 150;
var capturedImageData = new Float32Array(screenWidth * screenHeight * 4);
gl.readPixels(0, 0, screenWidth, screenHeight, gl.RGBA, gl.FLOAT, capturedImageData);

当我运行您的代码时,我遇到了这个错误。
js:16 WebGL: INVALID_ENUM: readPixels: invalid type

使用FLOAT无法进行读取操作。请参阅这个答案

但是,您正在尝试的操作,将一个渲染纹理并将其用作另一个着色器的输入,不应使用readPixels来完成。而应该使用framebuffers直接将渲染结果输出到纹理中。

请参考这篇文章,或者也可以查看这些


谢谢你的回答!我一直在阅读关于帧缓冲区的内容,它们似乎是一个更好的解决方案。 - xacer

2

由于某些原因,使用Uint8Array会使gl.readPixels函数表现更好。

var capturedImageData = new Uint8Array(screenWidth * screenHeight * 4);
gl.readPixels(0, 0, screenWidth, screenHeight, gl.RGBA, gl.UNSIGNED_BYTE, capturedImageData);

这里有一个演示:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />

  </head>
  <canvas></canvas>
  <body>
    <script>
      var gl = document.querySelector("canvas").getContext("webgl");    

    
     gl.clearColor(1, 0, 1, 1);
     gl.clear(gl.COLOR_BUFFER_BIT);
    
    
      var pixels = new Uint8Array(4 * window.innerWidth * window.innerHeight);
      gl.readPixels(0, 0, window.innerWidth, window.innerHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
      
      
      console.log(pixels[0]);
    

  
    </script>
  </body>
</html>

我在发布几个小时后解决了这个问题,而这正是解决方案。感谢您的帮助 :) - xacer
没问题!我会关注最终版本的 :) - Ollie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接