【问题标题】:3d Occlusion Culling3d 遮挡剔除
【发布时间】:2011-06-27 02:37:13
【问题描述】:

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

【问题讨论】:

  • 您了解过 z-buffers 吗? de.wikipedia.org/wiki/Z-Buffer
  • @Thomas:你的意思可能是en.wikipedia.org/wiki/Z-Buffer :)
  • 我已经简要了解了它们,但还不足以知道如何使用它们。我将不得不阅读更多内容。
  • 抱歉,谢谢@Matt :D 刚刚复制,不用担心语言问题。
  • Z-buffering 并不能帮助您减少要泵送至显卡的几何图形数量。事实上,除非你是从后到前排序,否则你已经在使用 Z 缓冲了。

标签: opengl 3d culling occlusion


【解决方案1】:

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

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

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

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

【讨论】:

  • 请注意,z-buffering 是避免进行不必要的片段(像素)操作(计算片段的颜色、进行纹理查找、照明等)的好方法,但如果可能的话,最好只需跳过不可见的几何体(并避免对不会影响最终图像的顶点进行转换),就像您使用视锥体剔除所做的那样。
  • 为此,有遮挡剔除。我应该编辑我的答案以触及该主题。
【解决方案2】:

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

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

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

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

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

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

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

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

    • 我在现代系统(Gefore 8800 GT,双核,2GB 内存)上在截锥体剔除之前绘制 ~ 250,000 个 1x1x1 立方体。使用截锥体剔除,我得到 17.83fps 的帧速率,而没有截锥体剔除的帧速率约为 2fps。立方体的实际绘制是通过提供的接口完成的,但据我所知,它们是使用 glPush/PopMatrix 和 glutSolidCube 绘制的。
    • glutSolidCube 是你的问题。您受到所有状态更改的限制,每次绘制 250,000 个立方体。以合理速度运行的典型限制是少于 1000 个对象。
    【解决方案4】:

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

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

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

    【讨论】:

    • 这可能是一个很棒的技术,但我认为重要的是要补充一点,它应该只用于遮挡您提前知道的对象可能会遮挡视锥体中的大部分世界.在反传送门上过度使用只会让事情变慢。
    【解决方案5】:

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

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

    如果您有凸面对象,则必须(为了性能)启用背面剔除!

    示例代码:

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

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

    glCullFace(...);

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

    【讨论】:

      猜你喜欢
      • 2014-09-22
      • 1970-01-01
      • 2011-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多