8

我正在用 C++/openGL 编写一个类似于 Minecraft 的静态 3d 块世界。我正在努力提高帧速率,到目前为止,我已经使用八叉树实现了截锥剔除。这有帮助,但我仍然看到中等到差的帧速率。下一步是剔除被更近的立方体隐藏在视点之外的立方体。但是,我还没有找到很多关于如何实现这一点的资源。

4

5 回答 5

9

创建启用了 Z 缓冲区(或“深度缓冲区”)的渲染目标。然后确保对所有不透明对象进行排序,使它们从前到后呈现,即最靠近相机的对象首先呈现。在渲染所有不透明对象之后,任何使用 alpha 混合的东西仍然需要从前渲染。

另一种技术是遮挡剔除:您可以廉价地“干渲染”您的几何图形,然后找出有多少像素未能通过深度测试。DirectX 和 OpenGL 中有遮挡查询支持,尽管不是每个 GPU 都能做到。

缺点是您需要在渲染和获取结果之间有延迟 - 取决于设置(例如使用谓词平铺时),它可能是一个完整的帧。这意味着您需要在那里发挥创造力,例如渲染比对象本身更大的边界框,并在相机剪切后消除结果。

还有一件事:一个更传统的解决方案(您可以与遮挡剔除同时使用)是房间/门户系统,您将区域定义为“房间”,通过“门户”连接。如果从您当前的房间看不到某个门户,您就看不到与之相连的房间。即使是这样,您也可以单击您的视口来查看通过门户可见的内容。

于 2011-02-14T18:15:07.150 回答
6

我在这个 Minecraft 级别渲染器中采用的方法本质上是一个视锥体限制的泛洪填充。16x16x128 的块被分成 16x16x16 的块,每个块都有一个具有相关几何形状的 VBO。我在玩家位置的小块网格中开始填充以查找要渲染的小块。填充受限于:

  1. 视锥体
  2. 实心小块 - 如果小块的整个侧面都是不透明的块,那么填充将不会在那个方向进入小块
  3. 方向 - 洪水不会反转方向,例如:如果当前存储块在起始存储块的北边,则不要淹没到南边的存储块中

它似乎工作正常。我在 android 上,所以虽然更复杂的分析(如 Mike Daniels 所指出的反门户)会剔除更多的几何图形,但我已经受到 CPU 的限制,所以没有多大意义。

我刚刚看到您对 Alan 的回答:剔除不是您的问题 - 这是您向 OpenGL 发送的内容和方式,速度很慢。

绘制什么:不要为每个块渲染立方体,渲染与不透明块接壤的透明块的面。考虑一个由石块组成的 3x3x3 立方体:绘制中心块没有意义,因为玩家无法看到它。同样,玩家永远不会看到两个相邻石块之间的面,所以不要画它们。

如何绘制:正如 Alan 所说,使用 VBO 来批处理几何图形。你不会相信他们制造东西的速度有多快。

一种更简单的方法是使用显示列表,只需对现有代码进行最少的更改。这就是我的世界使用的。

于 2011-02-15T13:20:58.010 回答
4

您要渲染多少块以及在什么硬件上?现代硬件非常快,并且很难用几何图形压倒(除非我们谈论的是手持平台)。在任何较新的桌面硬件上,您应该能够以每秒 60 帧的速度每帧渲染数十万个立方体,而无需任何花哨的剔除技巧。

如果您使用单独的绘图调用(glDrawElements/Arrays、glBegin/glEnd 等)绘制每个块(奖励点:不要使用 glBegin/glEnd),那么这将是您的瓶颈。这是初学者的常见陷阱。如果您正在这样做,那么您需要将所有共享纹理和着色参数的三角形批处理到每个设置的单个调用中。如果几何图形是静态的并且不会逐帧更改,则您希望为每批三角形使用一个顶点缓冲区对象。

如果您通常一次在视锥体中只有一小部分整个游戏世界,这仍然可以与使用八叉树的视锥体剔除相结合。顶点缓冲区仍然是静态加载的,没有改变。Frustum 剔除八叉树以仅生成平截头体中三角形的索引缓冲区,并在每帧动态上传这些缓冲区。

于 2011-02-14T21:50:34.233 回答
3

如果您有靠近相机的表面,您可以创建一个表示不可见区域的截锥体,并剔除完全包含在该截锥体中的对象。下图中,C是相机,|是相机附近的一个平面,由所组成的截锥状区域.代表被遮挡区域。该表面称为反门户

        .
       ..
      ...
     ....
    |....
    |....
    |....
    |....
C   |....
    |....
    |....
    |....
     ....
      ...
       ..
        .

(您当然还应该打开其他答案和评论中提到的深度测试和深度写作——这在 OpenGL 中非常简单。)

于 2011-02-14T19:44:40.117 回答
-3

Z-Buffer 的使用可确保多边形正确重叠。

启用深度测试会使每个绘图操作在将像素放置到屏幕上之前检查 Z 缓冲区。

如果你有凸物体,你必须(为了性能)启用背面剔除!

示例代码:

glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE);

您可以更改 glCullFace() 传递 GL_FRONT 或 GL_BACK 的行为...

glCullFace(...);

// 绘制“游戏世界”...

于 2011-02-16T01:31:10.140 回答