gpt4 book ai didi

opengl - 如何实现 "portal rendering"

转载 作者:行者123 更新时间:2023-12-04 12:52:37 26 4
gpt4 key购买 nike

我一直在尝试实现类似游戏 Antichamber 的东西(更准确地说,这个技巧如下所示)过去一周:

trick

这是我希望实现的目标的视频(即使它是使用虚幻引擎 4 完成的;我没有使用它):https://www.youtube.com/watch?v=Of3JcoWrMZs

我查找了执行此操作的最佳方法,并发现了模板缓冲区。之间this文章和 this我在网上找到的代码(“drawPortals()”函数)我几乎实现了它。

它可以很好地与一个传送门连接到另一个房间(不是一个可穿越的传送门,这意味着你不能穿过它并被传送到另一个房间)。在我的例子中,我正在绘制一个通往一个简单方形房间的门户,里面有一个球体;在门户后面有另一个球体,我用来检查深度缓冲区是否正常工作并将其绘制在门户后面:

front

side

当我添加另一个靠近此门户的门户时,就会出现问题。在这种情况下,我设法正确显示了另一个门户(灯光关闭,但右侧的球体颜色不同,以表明它是另一个球体):

enter image description here

但是,如果我转动相机,使第一个入口必须绘制在第二个上方,那么第一个入口的深度就会出错,第二个入口就会绘制在第一个上方,如下所示:

enter image description here

虽然它应该是这样的:

enter image description here

所以,这就是问题所在。我可能在深度缓冲区上做错了什么,但我找不到什么。

我的渲染部分代码几乎是这样的:

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 步得到了这样的结果(原谅那些愚蠢的图画,我很懒惰):
enter image description here

然后在第 2 步之后:
enter image description here

然后我从第二个门户开始:
enter image description here

但是现在,在第 1 步和第 2 步之间,我告诉模板只在位为 1 的地方绘制;由于缓冲区现在已清除,我丢失了第一个传送门的 1 的跟踪,因此如果第二个传送门的部分帧在前一帧之后,则第二帧的 1 仍将与第二个传送门的房间一起绘制,无论深度缓冲区。有点像这张图片:
enter image description here

不知道我解释的对不对...

最佳答案

现在已经有一段时间了。由于不会有答案(可能),我正在写我现在正在使用的解决方法。

我尝试使用不同的模板函数解决问题,并且在渲染新门户时不清空模板缓冲区;这个想法是利用我知道的事实,通过查看之前渲染门户的模板缓冲区。

最后,我没能做到;在我在原始问题中发布的示例中,两个门户中的一个总是掩盖了另一个的部分。

所以,我决定做一些更简单的事情:我检查一个门户是否可见,然后我按照我在问题中发布的代码完全绘制它(因此每次清空模板缓冲区)。

在伪代码中,我这样做:

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 个作为立方体的门户(所以我知道一次最多可以直接看到 2 个门户)并且框架的只有一个面显示门户,而另一个则没有,它完美地工作。

我知道可能有一种更优雅的方法可以通过仅使用模板缓冲区来做到这一点,但现在这对我有用。

如果有人知道更好的方法来做到这一点,我仍然有兴趣知道!

编辑:我发现了其他版本的模板函数(即 glStencilFuncSeparate、glStencilOpSeparate、glStencilMaskSeparate),它们允许对背面和正面做不同的事情。这似乎是我解决我描述的问题所需要的,但我很快就无法尝试了。我只是指出它,以防有同样问题的人在这里徘徊。

关于opengl - 如何实现 "portal rendering",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38287235/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com