用于渲染大量立方体的剔除技术

问题描述 投票:21回答:8

我正在制作个人学习项目,以制作Minecraft克隆。除了一件事,它的工作非常好。类似于我的世界,我的地形有很多立方体堆叠在Y上,所以你可以挖掘。虽然我做了截头剔除,但这仍然意味着我无用地在我下面画出所有层的立方体。立方体是X,Y和Z有序的(虽然只在1个方向,所以从技术上讲,它不是Z订购到相机)。我基本上从玩家的位置只添加指向玩家周围的立方体的指针。然后,我对这些进行了截击。我不做oct树细分。我想只是没有渲染玩家下面的图层,除非玩家向下看到一个洞时这不起作用。鉴于此,我怎么能避免在我下面渲染我看不到的立方体,或者还有其他立方体隐藏的立方体。

谢谢

void CCubeGame::SetPlayerPosition()
{
PlayerPosition.x = Camera.x / 3;
PlayerPosition.y = ((Camera.y - 2.9) / 3) - 1;
PlayerPosition.z = Camera.z / 3;
}

void CCubeGame::SetCollids()
{

SetPlayerPosition();

int xamount = 70;
int zamount = 70;
int yamount = 17;

int xamountd = xamount * 2;
int zamountd = zamount * 2;
int yamountd = yamount * 2;
PlayerPosition.x -= xamount;

PlayerPosition.y -= yamount;

PlayerPosition.z -= zamount;


collids.clear();
CBox* tmp;

    for(int i = 0; i < xamountd; ++i)
    {
        for(int j = yamountd; j > 0; --j)
        {
            for(int k = zamountd; k > 0; --k)
            {

                tmp = GetCube(PlayerPosition.x + i, PlayerPosition.y + j, PlayerPosition.z + k);



                if(tmp != 0)
                {
                    if(frustum.sphereInFrustum(tmp->center,25) != NULL)
                    {
                        collids.push_back(tmp);
                    }
                }

            }
        }

}
c++ c algorithm rendering
8个回答
13
投票

从前到后渲染。为此,您不需要排序,使用八叉树。叶子不是单独的立方体,而是更大的群体。

每个这样的叶子的网格应该缓存在显示列表中(如Bobmitch建议的那样),或者在顶点缓冲区中更好(更新更便宜)。生成此网格时,不要以强力方式生成所有立方体。相反,对于每个立方体面,检查它是否在同一个叶子中有一个不透明的邻居,如果是这样,你根本不需要生成这个面。您还可以将具有相同材质的相邻面统一到一个长矩形中。您还可以将网格分成六组,每个主方向一组:+/- XYZ面。仅绘制可能面向相机的那些面部组。

从前到后渲染本身并没有帮助。但是,您可以使用现代硬件提供的occlusion culling从此订购中受益。在渲染八叉树叶之前,检查其bbox是否通过了遮挡查询。如果它没有通过你根本不需要绘制它。

遮挡查询的替代方法可以是光线跟踪。光线追踪是good for rendering such environment。您可以投射一组稀疏的光线来近似可见的叶子并仅绘制那些叶子。但是,这会低估可见性集。


16
投票

这是我在编写自己的克隆时学到的:

  1. 不要只是将每个多维数据集转储到OpenGL中,也不要担心自己完成所有可见性修剪。如另一个答案所述,检查所有6个面以查看它们是否被相邻块完全遮挡。仅渲染可见的面。这大致减少了从立方项(立方体体积n * n * n)到平方项(表面仅约n * n)的面数。
  2. OpenGL可以比你更快地查看截头剔除。将所有曲面面渲染到显示列表或VBO后,只需将整个blob发送到OpenGL即可。如果你将几何体分成切片(或Minecraft称之为块),你可能会避免绘制你可以很容易地确定在相机背后的块。
  3. 将整个几何体渲染到显示列表(或列表)中,并每次重绘。如果您使用立即模式,这是一个简单的步骤,因为您只需将现有代码包装在glNewList / glEndList中并使用glCallList重绘。减少OpenGL调用计数(每帧)将比减少要渲染的多边形总量产生更大的影响。
  4. 一旦你看到生成显示列表需要多长时间而不是绘制它们,你将开始考虑如何将更新放入一个线程。这是转换为VBO的结果:线程呈现为普通的旧数组(例如,向数组添加3个浮点数而不是调用glVertex3f)然后GL线程只需要使用glBufferSubData将它们加载到卡中。你赢了两次:代码可以在一个线程中运行,并且可以用3个数组写入而不是3个函数调用“绘制”一个点。

我注意到的其他事情:

VBO和显示列表具有非常相似的性能。给定的OpenGL实现很可能在内部使用VBO来存储显示列表。我跳过了顶点数组(一种客户端VBO)所以我不确定那些。使用ARO扩展版本的VBO而不是GL 1.5标准,因为英特尔驱动程序仅实现扩展(尽管声称支持1.5)并且nvidia和ATI驱动程序不关心。

纹理地图集规则。如果您在每张脸上使用一个纹理,请查看地图集的工作原理。

如果你想看我的代码,请在github上找到我。


5
投票

和其他人一样,我一直在玩一个使用Ogre的块世界“引擎”并且在我去的时候写一些文章(参见Block World Articles)。我一直在采取的基本方法是:

  • 仅创建块的可见面(不是块之间的面)。
  • 将世界分成更小的块(仅用于更快地更新单个块)。
  • 将块纹理组合到一个纹理文件(纹理图集)中。

只需使用这些就可以在大型简单块世界中获得非常好的性能(例如,在体面的硬件上为1024x1024x1024)。


2
投票

我目前正在使用python / pyglet进行一个Minecraft克隆,仅仅是为了好奇。

我将数据分解成块,就像在minecraft中一样,然后为每个块创建一个基于立方体可见性的opengl displaylist。然后,我在这些块上执行简单的2d视锥体剔除,并在播放器的一定距离内调用每个显示列表。

在添加/删除多维数据集时,我重新创建块的displaylist。

除了完全被其他立方体包围的立方体之外,没有遮挡剔除。

对于简单的场景,这可以在适度的gfx卡上达到600fps以上,视距约为200立方。


1
投票

Octtree等肯定会起作用,但解决特定问题的另一种可能方法可能是存储以向每个立方体对象添加usigned char visiblevisible字段的值计算如下:

  • 如果右侧(沿x轴看)没有邻居,则将设置第一位(1)。
  • 如果左侧(沿负x轴看)没有邻居,则第二位(2)将被置位。
  • 如果正面(沿z轴看)没有邻居,那么将设​​置第3位(4)
  • ...等等,这样你就可以为立方体的6个边各占1位

每当玩家挖掘一个立方体时,你必须更新所有邻居立方体的visible字段。

那么,这会有什么帮助呢?如果一个立方体的visible值为0,则很容易 - 该立方体将永远不会显示。但是,假设立方体的visible值为1.然后,如果Xplayer < Xcube,立方体(可能)仅可见。其他方面的工作方式类似,我认为决定一个多维数据集是否可见的这样一个函数会非常快,并且能够跳过很多隐藏的多维数据集。

缺点是,此测试只是每个多维数据集测试,您不能跳过完整的组。因此,可能是一个octtree(用于跳过完整区域)和像这里描述的这样的可见字段,用于跳过大量隐藏的立方体(由于这些立方体被堆叠,隐藏立方体的数量将远远高于可见的数量在该地区内部可能是一个很好的解决方案。


1
投票

你可以使用PVS(Potentially visible set),虽然它通常用于地形,同样的原则适用,剔除无法看到的。 Gamedev.net还有一个地形变形文章也涵盖了这一点。


0
投票

如果只有绘图是问题(而不是未使用的顶点的旋转),不能像c-buffer那样有用吗?我使用它非常成功,它需要排序多边形(例如通过画家的算法)和几乎零内存(与z缓冲区相反)。


-1
投票

仅跟踪描述表面的立方体。您可以通过一个简单的数据结构来实现这一点,其中每个多维数据集都保持对它的邻居的引用。在任何现代图形卡上推动所有这些三角形应该不是问题。从后到前渲染。此外,只渲染立方体,然后距观察者特定距离。 “世界”可以从巨大的“立方体立方体”开始,立方体的外壳由立方体制成。如果有人挖掘,你可以检查邻居位置是否已包含多维数据集,如果不是,则创建这些多维数据集并将其链接。

示例:向下挖掘一个平面:移除位于挖掘位置的立方体,在下面添加9个新立方体(但检查这些位置是否已在使用中,以防使用这些位置),将表面链接在一起但将新立方体链接到删除的邻居多维数据集。

所以:对于包含1000x1000x1000多维数据集的世界,您将获得:1000 * 1000 * 2 + 998 * 999 * 4 = 5988008而不是1000 * 1000 * 1000 = 1000000000,或者因子数量减少167个。

当然你不应该绘制所有这些立方体,从与观察者的简单距离开始做一个子选择。

您还可以将8个多维数据集(组)组合在一起作为1组,然后继续这样,直到最高级别您只有一个多维数据集组(已提到的oct-tree)。此树可用于光线跟踪您需要绘制而不是绘制的世界的哪个部分。如果一个组包含对其他8个多维数据集组的引用,那么后面的那些不显示。后面是这些立方体组,它们不会从用户开始与光线跟踪锥体相交或放置,并且仅通过用于测试的组的边缘。 (不好描述,但我希望你能得到一些关于可以为优化做些什么的提示)。无论如何,今天的显卡可能不需要这样做。

祝你的项目好运。

© www.soinside.com 2019 - 2024. All rights reserved.