过去一周,我一直在尝试实施类似游戏Antichamber(更准确地说是下面显示的这个技巧):
这是我希望实现的视频(尽管它是使用虚幻引擎4完成的;我没有使用它):https://www.youtube.com/watch?v=Of3JcoWrMZs
我查找了最好的方法,我发现了模板缓冲区。在this文章和this代码之间(“drawPortals()”函数)我在网上发现我几乎实现了它。
它可以很好地与一个门户到另一个房间(不是一个可交叉的门户,这意味着你无法穿过它并被传送到另一个房间)。在我的例子中,我正在绘制一个门户到一个简单的方形房间,里面有一个球体;在门户网站后面还有另一个球体,我用来检查深度缓冲区是否正常工作并将其绘制在门户网站后面:
当我添加另一个接近此门户的门户时,会出现问题。在这种情况下,我设法正确显示另一个门户(灯光关闭,但右侧的球体颜色不同,表明它是另一个球体):
但是如果我转动相机以便第一个门户必须被绘制在第二个门户上,那么第一个门户的深度就会变错,而第二个门户就会被吸引到第一个门户上,如下所示:
虽然它应该是这样的:
所以,这就是问题所在。我可能在深度缓冲区做错了,但我找不到。
渲染部分的代码几乎就是这样:
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_STENCIL_TEST);
// First portal
glPushMatrix();
// Disable writing to the color and depht buffer; disable depth testing
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_FALSE);
glDisable(GL_DEPTH_TEST);
// Make sure that the stencil always fails
glStencilFunc(GL_NEVER, 1, 0xFF);
// On fail, put 1 on the buffer
glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);
// Enable writing to the stencil buffer
glStencilMask(0xFF);
// Clean the buffer
glClear(GL_STENCIL_BUFFER_BIT);
// Finally draw the portal's frame, so that it will have only 1s in the stencil buffer; the frame is basically the square you can see in the pictures
portalFrameObj1.Draw();
/* Now I compute the position of the camera so that it will be positioned at the portal's room; the computation is correct, so I'm skipping it */
// I'm going to render the portal's room from the new perspective, so I'm going to need the depth and color buffers again
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glClear(GL_DEPTH_BUFFER_BIT);
// Disable writing to the stencil buffer and enable drawing only where the stencil values are 1s (so only on the portal frame previously rendered)
glStencilMask(0x00);
glStencilFunc(GL_EQUAL, 1, 0xFF);
// Draw the room from this perspective
portalRoomObj1.Draw();
glPopMatrix();
// Now the second portal; the procedure is the same, so I'm skipping the comments
glPushMatrix();
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glDepthMask(GL_FALSE);
glDisable(GL_DEPTH_TEST);
glStencilFunc(GL_NEVER, 1, 0xFF);
glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);
glStencilMask(0xFF);
glClear(GL_STENCIL_BUFFER_BIT);
portalFrameObj2.Draw();
/* New camera perspective computation */
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glClear(GL_DEPTH_BUFFER_BIT);
glStencilMask(0x00);
glStencilFunc(GL_EQUAL, 1, 0xFF);
portalRoomObj2.Draw();
glPopMatrix();
// Finally, I have to draw the portals' frames once again but this time on the depth buffer, so that they won't get drawn over; first off, disable the stencil buffer
glDisable(GL_STENCIL_TEST);
// Disable the color buffer
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glClear(GL_DEPTH_BUFFER_BIT);
// Draw portals' frames
portalFrameObj1.Draw();
portalFrameObj2.Draw();
// Enable the color buffer again
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
/* Here I draw the rest of the scene */
更新
我设法找出问题所在,但我仍然无法解决问题。它实际上与深度缓冲区无关,而与模板无关。
基本上,我绘制第一个门户的方式是这样的: 1)用1s填充模板缓冲区中的门户帧的位;在门户网站外面只有0 2)绘制模板具有1s的门户室(以便将其绘制到框架的门户上
我在第二个门户网站重复这个。
对于第一个门户网站,我在步骤1得到这样的东西(原谅愚蠢的图纸,我很懒):
但是现在,在步骤1和2之间,我告诉模板仅绘制位为1的位置;由于缓冲区现在被清除,我丢失了第一个门户网站1s的跟踪,所以如果第二个门户网站框架的一部分位于前一个框架的后面,第二个框架的1s仍将与第二个门户网站的房间一起绘制,无论深度缓冲区。有点像这张图片:
我不知道我是否设法解释它......
答案 0 :(得分:0)
现在已经有一段时间了。既然不会出现问题(可能)我正在写关于我现在正在使用的解决方法。
我尝试使用不同的模板函数解决问题,而不是在渲染新门户时移除模板缓冲区;我的想法是通过查看模板缓冲区来利用我所知道的事实,其中前面的门户网站正在被渲染。
最后,我无法做到;在我在原始问题中发布的示例中,2个门户中的一个总是遮挡了另一个门户的部分。
所以,我决定做一些更简单的事情:我检查一个门户是否可见,然后我就像我在问题中发布的代码一样绘制它(每次都清空模板缓冲区)。
在伪代码中,我这样做:
for(var i = 0; i < PORTALS_NUMBER; i++)
{
// I get the normal to the portal's frame and its position
var normal = mPortalFramesNormals[i];
var framePos = mPortalFrames[i].GetPosition();
// I compute the scalar product between the normal and the direction vector between the camera's position and the frame's position
var dotProduct = normal * (currentCameraPosition - framePos);
// If the dot product is 0 or positive, the portal is visible
if(dotProduct >= 0)
{
// I render the portal
DrawPortal(mPortalFrames[i], mPortalRooms[i]);
}
}
glDisable(GL_STENCIL_TEST);
// Now I draw the portals' frames in the depth buffer, so they don't get overwritten by other objects in the scene
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glClear(GL_DEPTH_BUFFER_BIT);
for(var i = 0; i < PORTALS_NUMBER; i++)
{
mPortalFrames[i].Draw();
}
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
在我的情况下,我需要显示总共4个门户位置作为一个立方体(所以我知道一次最多可以直接看到两个门户)并且只有一个面部显示门户,而另一个donesn&#t; t,它完美无缺。
我知道通过仅使用模板缓冲区可能有更优雅的方法,但现在这对我有用。
如果有人知道更好的方法,我仍然有兴趣知道!
编辑:我发现了其他版本的模板功能(即glStencilFuncSeparate,glStencilOpSeparate,glStencilMaskSeparate),它们允许对后面和前面做不同的事情。这似乎是我需要解决我所描述的问题,但我很快就无法尝试这个。我只是指出它,以防有同样问题的人在这里徘徊。