openGL中不正确的遮挡,正面剔除

Posted

技术标签:

【中文标题】openGL中不正确的遮挡,正面剔除【英文标题】:Incorrect occluded, front-face culling in openGL 【发布时间】:2014-04-27 06:10:21 【问题描述】:

我正在使用我自己的算法来剔除不应该显示的被遮挡的正面。当我不剔除面时,我有一套完美的块。当我启用我的面部剔除时,我得到了意想不到的结果。我检查了我的剔除代码,我相信它可以正确剔除合适的面孔。

为简单起见,我将输出减少到 9 个盒子的平面。例如,平面中间的盒子只需要一个顶面和一个底面。所有其他面孔都可以被丢弃,因为它们根本不会出现在任何地方。

我检查了代码中的每个框,以确定它们是否剔除了正确的面孔,我相信它们确实如此。这让我觉得这是我的顶点、索引或法线的问题。

我正在使用 python 的 Pyglet,它会自动创建和管理 VAO 和 VBO。

OpenGL 设置:

    glEnable(GL_DEPTH_TEST)
    glEnable(GL_CULL_FACE)

OpenGL 绘制:

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

顶点代码:

        v0 = [1,  1,  1]
        v1 = [-1,  1,  1]
        v2 = [-1, -1,  1]
        v3 = [1, -1,  1]
        v4 = [1, -1, -1]
        v5 = [1,  1, -1]
        v6 = [-1,  1, -1]
        v7 = [-1, -1, -1]
        pt = self.point
        s = getattr(self, 'scale', 0.5)
        faces = ((k, v) for k, v in (
            ('front', [v * s + p for vert in [v0, v1, v2, v3]
                       for v, p in zip(vert, pt)]),
            ('right', [v * s + p for vert in [v0, v3, v4, v5]
                       for v, p in zip(vert, pt)]),
            ('top', [v * s + p for vert in [v0, v5, v6, v1]
                     for v, p in zip(vert, pt)]),
            ('left', [v * s + p for vert in [v1, v6, v7, v2]
                      for v, p in zip(vert, pt)]),
            ('bottom', [v * s + p for vert in [v7, v4, v3, v2]
                        for v, p in zip(vert, pt)]),
            ('back', [v * s + p for vert in [v4, v7, v6, v5]
                      for v, p in zip(vert, pt)]))
            if getattr(self, k)
        )

法线代码:

        verts_per_face = 4
        faces = ((k, v) for k, v in (
            ('front', [0, 0, 1] * verts_per_face),
            ('right', [1, 0, 0] * verts_per_face),
            ('top', [0, 1, 0] * verts_per_face),
            ('left', [-1, 0, 0] * verts_per_face),
            ('bottom', [0, -1, 0] * verts_per_face),
            ('back', [0, 0, -1] * verts_per_face))
            if getattr(self, k)
        )

指数代码:

        t0 = [0,  1,  2]
        t1 = [2,  3,  0]
        t2 = [4,  5,  6]
        t3 = [6,  7,  4]
        t4 = [8,  9, 10]
        t5 = [10, 11,  8]
        t6 = [12, 13, 14]
        t7 = [14, 15, 12]
        t8 = [16, 17, 18]
        t9 = [18, 19, 16]
        t10 = [20, 21, 22]
        t11 = [22, 23, 20]
        triangles = ((k, v) for k, v in (
            ('front', [t for triangle in [t0, t1] for t in triangle]),
            ('right', [t for triangle in [t2, t3] for t in triangle]),
            ('top', [t for triangle in [t4, t5] for t in triangle]),
            ('left', [t for triangle in [t6, t7] for t in triangle]),
            ('bottom', [t for triangle in [t8, t9] for t in triangle]),
            ('back', [t for triangle in [t10, t11] for t in triangle]))
            if getattr(self, k)
        )

剔除代码:

                for face, neighbor_point in block.neighbors():
                    # import pdb; pdb.set_trace()
                    if neighbor_point in pts:
                        neighbor = self.blocks.get(neighbor_point)
                        if neighbor:
                            setattr(block, face, False)
                        else:
                            setattr(block, face, True)
                    else:
                        setattr(block, face, True)

剔除其中一个框的正面和左侧面后的示例输出:

<Voxel (1,0,1)  # top right corner box in images /w center point = (1, 0, 1)
[f r t l o a]   # front, right, top, left, bottom, back
[ |+|+| |+|+]   # + = face available,  ' ' = face culled
(1, 0, 0) (1, 0, 0) (1, 0, 0) (1, 0, 0)  # right normals
(0, 1, 0) (0, 1, 0) (0, 1, 0) (0, 1, 0)  # top normals
(0, -1, 0) (0, -1, 0) (0, -1, 0) (0, -1, 0)  # bottom normals 
(0, 0, -1) (0, 0, -1) (0, 0, -1) (0, 0, -1)  # back normals
[ 1.50| 0.50| 1.50|  # right verts
  1.50|-0.50| 1.50| 
  1.50|-0.50| 0.50| 
  1.50| 0.50| 0.50| 
  1.50| 0.50| 1.50|  # top verts
  1.50| 0.50| 0.50| 
  0.50| 0.50| 0.50| 
  0.50| 0.50| 1.50| 
  0.50|-0.50| 0.50|  # bottom verts
  1.50|-0.50| 0.50| 
  1.50|-0.50| 1.50| 
  0.50|-0.50| 1.50| 
  1.50|-0.50| 0.50|  # back verts
  0.50|-0.50| 0.50| 
  0.50| 0.50| 0.50| 
  1.50| 0.50| 0.50]>

【问题讨论】:

我有点懒得看你所有的代码。但我认为你对面部剔除有一个根本的误解。您在“剔除”下发布的代码没有任何意义。 gl 侧 GL_CULL_FACE 的剔除是完全自动的。理解它的最简单方法是它是 2d。计算每个结果二维三角形的面积。如果是负数,则为背面。你可以很容易地画出这个。 3个顶点,绘制三角形0,1,2和2,1,0。形状相同,但该地区的标志不同。它与邻居或正常人无关!只是二维缠绕。 整个 3d 东西都可以工作,因为在投影下缠绕不会改变。但这确实是一个二维的东西。作为一个练习,弄清楚如何计算具有 3 个顶点的 2d 三角形的面积,这些顶点由三个索引 0、1、2 定义。然后对索引 2,1,0 执行相同操作。 我正在尝试删除从未见过的正面三角形。举个简单的例子,中间的盒子只需要一个顶面和一个底面——所有其他面都被另一个盒子共享并且永远不会被看到。 您可以进行仅深度预传递以消除线框中的内部面。从技术上讲,它不会剔除任何东西,只是通过使它们第二次未能通过深度测试来防止这些面被遮蔽。由于听起来您更像是在尝试这样做以减少顶点处理负载,因此我认为这不是您真正想要的。但老实说,在这个简单的示例中,识别被遮挡的面部并没有任何好处,它需要的处理时间比它节省的时间要多。即使您将其放大到更多的三角形(在合理范围内),这也是正确的。 您是否针对特定平台?由于您使用的是 Python,我认为它不是移动类 GPU,它在一定程度上具有非常不同的架构。大多数传统 GPU 在片段着色器之前进行深度测试。如果你可以从前到后渲染你的几何体,这对于网格中的块可能相当容易,隐藏的面将在片段着色器之前被消除。这可能是你能做的最好的。我怀疑在 CPU 上应用逻辑,尤其是在 Python 中,会比让 GPU 消除不可见的面孔更有效。 【参考方案1】:

请参阅here 了解如何实现剔除算法。在您的剔除代码中,您正在做一些完全不相关的事情。要确定是否应该剔除一个面,您只需检查它的缠绕顺序(假设您提供所有三角形相对于它们的法线的 CW 或 CCW 顺序)。如果您的三角形没有排序,那么为了确定剔除,您必须检查三角形顶点的 z 顺序,这是显卡自动执行的操作。如果您决定自己实现此功能,您将有效地实现软件渲染(尽管渲染为三角形片段,而不是单个像素)。

【讨论】:

实际上,当我以后有大约 350,000 个额外的面孔时,它变得很重要。我不是要删除“背面”三角形。正如您所指出的,这已经自动完成。但是,我正在尝试剔除永远不会看到的正面三角形。我确实认为这可能是一个绕线问题。但我不确定为什么我的正面三角形在所有三角形都存在时工作正常,但当一些不存在时似乎是错误的。 @Brian,好的,我想我现在理解得更好了。在那种情况下,我不知道有什么方法可以按照您的要求进行操作。【参考方案2】:

我没有正确计算索引。新方法应该是:

        t0 = [0,  1,  2]
        t1 = [2,  3,  0]
        t2 = [4,  5,  6]
        t3 = [6,  7,  4]
        t4 = [8,  9, 10]
        t5 = [10, 11,  8]
        t6 = [12, 13, 14]
        t7 = [14, 15, 12]
        t8 = [16, 17, 18]
        t9 = [18, 19, 16]
        t10 = [20, 21, 22]
        t11 = [22, 23, 20]
        triangles = ((k, v) for k, v in (
            ('front', [t for triangle in [t0, t1] for t in triangle]),
            ('right', [t for triangle in [t2, t3] for t in triangle]),
            ('top', [t for triangle in [t4, t5] for t in triangle]),
            ('left', [t for triangle in [t6, t7] for t in triangle]),
            ('bottom', [t for triangle in [t8, t9] for t in triangle]),
            ('back', [t for triangle in [t10, t11] for t in triangle]))
        )
        inds = []
        faces = [f for f, v in self.faces if v]
        for triangle, face in zip(triangles, faces):
            f, tdata = triangle
            inds.extend(tdata)

【讨论】:

以上是关于openGL中不正确的遮挡,正面剔除的主要内容,如果未能解决你的问题,请参考以下文章

C/C++ OpenGL 遮挡剔除

OpenGL 深度/剔除问题

OPENGL——背面剔除

OpenGL:四边形似乎没有正确剔除

原创 我的OpenGL学习进阶之旅介绍一下OpenGL ES的 遮挡查询

原创 我的OpenGL学习进阶之旅介绍一下OpenGL ES的 遮挡查询