WebGL:使用帧缓冲区来选择多个对象 [英] WebGL: using framebuffers for picking multiple objects
问题描述
我正在尝试在WebGL中实现选择.我有很多对象(大约500个),我希望每个对象都可以被拾取.为了做到这一点,我做了一个循环,为每个对象分配唯一的颜色(参见拾取原理):
I am trying to implement picking in WebGL. I have a lot of objects (around 500), and I'd like each one to be allowed to be picked. In order to do that, I did a loop which assigns a unique colour to each object (cf. picking principle):
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
来检查每个对象是否具有不同的红色阴影.有效.
framecolors
was then used in a classical buffer to check whether each object had a different shade of red. It worked.
现在,我想使用对象的原始颜色以及背景为红色阴影的帧缓冲区.我已经看过一些代码了,我有点困惑.
Now, I want to use my objects' original colours, AND a framebuffer with the shades of red in the background. I've been through some code, and I am a bit confused.
这是我到目前为止尝试过的.
Here's what I have tried so far.
在选择之前调用的函数:
Function called before picking:
//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
链接到帧缓冲区.有办法吗?
As you can probably understand, I am not very comfortable with framebuffers, I just don't really get how they work, even though I read a lot about them. I have no idea how I can link framecolors
to the framebuffer. Is there a way?
谢谢, R.
推荐答案
帧缓冲区是附件(渲染缓冲区和/或纹理)的集合.就像没有帧缓冲区的渲染一样. (实际上,浏览器在内部使用帧缓冲区来实现WebGL的画布)
A framebuffer is a collection of attachments (renderbuffers and/or textures). It works just like rendering without a framebuffer. (in fact the browser is internally using a framebuffer to implement WebGL's canvas)
就您而言,您缺少了一些东西.您很可能需要连接一个深度缓冲区,否则在渲染场景时,您将不会获得zBuffering并且错误的对象将出现在前面.
In your case you're missing a few things. You most likely need a depth buffer attached otherwise when you render your scene you won't get zBuffering and the wrong objects will appear in front.
// 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
In general you should also check your framebuffer works. After attaching all the attachments you call gl.checkFramebufferStatus
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
alert("this combination of attachments does not work");
return;
}
由于多种原因,帧缓冲区可能不完整.最常见的是附件的大小不一样,或者GPU不支持附件的组合.注意:在WebGL中,必须使用某些组合才能使用,但是您由于以后可能会更改代码以使用不同的格式,因此检查它仍然是一个好主意.
A framebuffer can be incomplete for any number of reasons. The most common is the attachments are not same size or the GPU doesn't support that combination of attachments. Note: In WebGL certain combinations are required to work but you since you might change the code later to use different formats it's probably still a good idea to check.
每当切换帧缓冲区时,您还需要通过调用gl.viewport
来设置视口.
You also need to set the viewport by calling gl.viewport
whenever you switch framebuffers.
gl.bindFramebuffer(gl.FRAMEBUFFER, someFramebuffer);
gl.viewport(0, 0, someFramebufferWidth, someFramebufferHeight);
这包括在将内容放回画布时放回原位
That includes putting it back when setting things back to the canvas
gl.bindFramebuffer(gl.FRAMEBUFFER, null); // render to canvas
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
最后,上面的代码中有一个错误,即您只清除调用gl.clear
的帧缓冲区的深度缓冲区.你想打电话
Finally there's a bug in the code above in that you're only clearing the depth buffer of the framebuffer where you call gl.clear
. You want to call
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
由于您稍后将要阅读颜色,否则将保留旧颜色.
Since you're going to read the colors later otherwise old colors will be left over.
最后,我想你知道这一点.您找出哪个像素对应于鼠标单击并调用
Finally, and I guess you know this. You figure out which pixel corresponds to the mouse click and call
var colorPicked = new Uint8Array(4);
gl.readPixels(pickX, pickY, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, colorPicked);
请注意,在调用gl.readPixels
时,必须将帧缓冲区与gl.bindFramebuffer
绑定,否则将从画布中读取gl.readPixels
.
Note that when you call gl.readPixels
you must have your framebuffer bound with gl.bindFramebuffer
or gl.readPixels
will read from the canvas.
这篇关于WebGL:使用帧缓冲区来选择多个对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!