WebGL:使用framebuffers选择多个对象

时间:2014-02-17 23:36:30

标签: javascript webgl framebuffer picking

我正在尝试在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上。

2 个答案:

答案 0 :(得分:2)

帧缓冲是附件的集合(渲染缓冲区和/或纹理)。它的工作方式就像没有帧缓冲区的渲染一样。 (实际上浏览器在内部使用帧缓冲来实现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.bindFramebuffer绑定,否则gl.readPixels将从画布中读取。

答案 1 :(得分:1)

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

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 display framebuffer?

中的说明显示缓冲区