1

我正在尝试在 WebGL 中实现拾取。我有很多物品(大约 500 个),我希望每个物品都可以被挑选出来。为了做到这一点,我做了一个循环,为每个对象分配一个唯一的颜色(参见拾取原则):

for (var i = 0, len = objects.length; i < len; i++) {
   framecolors[count++] = i % 256 / 256; //Red
   framecolors[count++] = Math.floor(i/256) / 256; //Green
   framecolors[count++] = Math.floor(i/(256*256)) / 256; //Blue
}

framecolors然后在经典缓冲区中使用以检查每个对象是否具有不同的红色阴影。有效。

现在,我想使用对象的原始颜色和背景中带有红色阴影的帧缓冲区。我已经通过了一些代码,我有点困惑。

这是我到目前为止所尝试的。

采摘前调用的函数:

//Creates texture
colorTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, colorTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 400, 400, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);

//Creates framebuffer
fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorTexture, 0);
gl.bindTexture(gl.TEXTURE_2D, colorTexture);
gl.enable(gl.DEPTH_TEST);

gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.clear(gl.DEPTH_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, vertexPositionBuffer.numItems);

之后调用的函数:

gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.bindTexture(gl.TEXTURE_2D, colorTexture);
gl.drawArrays(gl.POINTS, 0, vertexPositionBuffer.numItems);

正如您可能理解的那样,我对帧缓冲区不太满意,我只是不太了解它们是如何工作的,尽管我阅读了很多关于它们的信息。我不知道如何链接framecolors到帧缓冲区。有办法吗?

谢谢,R。

4

2 回答 2

3

帧缓冲区是附件(渲染缓冲区和/或纹理)的集合。它就像没有帧缓冲区的渲染一样工作。(实际上浏览器在内部使用一个帧缓冲来实现 WebGL 的画布)

在您的情况下,您缺少一些东西。您很可能需要附加一个深度缓冲区,否则在渲染场景时您将无法获得 zBuffering,并且错误的对象将出现在前面。

// create renderbuffer
depthBuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);

// allocate renderbuffer
gl.renderbufferStorage(
      gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);  

// attach renderebuffer
gl.framebufferRenderbuffer(
      gl.FRAMEBUFFER,
      gl.DEPTH_ATTACHMENT,
      gl.RENDERBUFFER,
      depthBuffer);

一般来说,您还应该检查帧缓冲区的工作情况。附加您调用的所有附件后gl.checkFramebufferStatus

if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
   alert("this combination of attachments does not work");
   return;
}

由于多种原因,帧缓冲区可能不完整。最常见的是附件大小不同或 GPU 不支持该附件组合。注意:在 WebGL 中,需要某些组合才能工作,但您可能稍后会更改代码以使用不同的格式,因此检查一下可能仍然是个好主意。

您还需要在gl.viewport切换帧缓冲区时通过调用来设置视口。

 gl.bindFramebuffer(gl.FRAMEBUFFER, someFramebuffer);
 gl.viewport(0, 0, someFramebufferWidth, someFramebufferHeight);

这包括在将内容放回画布时将其放回原处

 gl.bindFramebuffer(gl.FRAMEBUFFER, null);  // render to canvas
 gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

最后,上面的代码中存在一个错误,即您只清除调用的帧缓冲区的深度缓冲区gl.clear。你想打电话

 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

由于您稍后将阅读颜色,否则旧颜色将被留下。

最后,我想你知道这一点。您找出哪个像素对应于鼠标单击并调用

 var colorPicked = new Uint8Array(4);
 gl.readPixels(pickX, pickY, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, colorPicked);

请注意,当您调用时,gl.readPixels您的帧缓冲区必须与画布绑定gl.bindFramebuffergl.readPixels将从画布中读取。

于 2014-02-18T04:36:44.617 回答
2

非常感谢gman。只是为了完成你的答案,关于如何使用framecolors我的帧缓冲区,这很简单。在着色器中,我添加了一个专用于帧缓冲区颜色的变量:

if (!offscreen) {
  gl_FragColor =  normalColors;
}
else {
  gl_FragColor =  frameColors;
}

现在,在通过该函数确定我选择了哪个对象之前readPixels(),我使用以下命令切换回帧缓冲区:

function renderFrame() {
  gl.bindFramebuffer(gl.FRAMEBUFFER, fb); //fb = framebuffer
  gl.uniform1i(shaderProgram.uOffscreen, true); //uOffscreen = uniform boolean in shader
  draw(); //function called to draw objects (contains gl.clear and gl.viewport)
}

同样,我在调用该函数后切换回通常的缓冲区。

单击鼠标时,我试图显示帧缓冲区而不是通常的缓冲区。我遇到了麻烦,但如果我找到它,我会在稍后发布解决方案。

编辑:解决方案:只需删除与帧缓冲区关联的深度缓冲区(渲染缓冲区)。然后按照以下说明显示缓冲区:WebGL 显示帧缓冲区?

于 2014-02-18T22:44:18.547 回答