2014-02-17 226 views
0

我试图在WebGL中实现拾取。我有很多物品(大约500),我希望每个人都被允许被选中。为了做到这一点,我做了一个环,其独特的颜色分配给每个对象(参见采摘原理):WebGL:使用帧缓冲区来拾取多个对象

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

甲帧缓冲器是附件(renderbuffers和/或纹理)的集合。它的工作原理类似于没有帧缓冲区的渲染。 (实际上浏览器在内部使用framebuffer来实现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); 

一般来说,你也应该检查你的framebuffer的作品。连接所有的附件后,您拨打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势必会从画布读取。

1

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

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

现在,determing我选择得益于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?