Неправильная окклюзия, отсечение передней поверхности в openGL

Я использую свой собственный алгоритм для удаления закрытых лицевых поверхностей, которые никогда не должны отображаться. Когда я не отбираю лица, у меня получается идеальный набор блоков. Когда я включаю отбраковку лица, я получаю неожиданные результаты. Я проверил свой код отбраковки и считаю, что он правильно отбраковывает соответствующие лица.

Для простоты я сократил свой вывод до плоскости из 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]>

Отбор, без каркасов! Без отбраковки, без каркасов  Отбор, каркасов  Без отбраковки, каркасы


person Brian Bruggeman    schedule 27.04.2014    source источник
comment
Мне немного лень читать весь твой код. Но я думаю, у вас есть фундаментальное недопонимание того, что такое отбраковка лиц. Код, который вы публикуете при отбраковке, не имеет никакого смысла. Отбор на стороне gl GL_CULL_FACE полностью автоматический. Самый простой способ понять это - это 2d. Вычислите площадь каждого результирующего 2-го треугольника. Если отрицательный, он обращен назад. Вы легко можете это нарисовать. 3 вершины, нарисуйте треугольники 0,1,2 и 2,1,0. Та же форма, но другой знак для местности. Это не имеет ничего общего с соседями или нормалями! Просто 2-я обмотка.   -  person starmole    schedule 27.04.2014
comment
Вся трехмерность работает, потому что обмотка не меняется при проецировании. Но на самом деле это 2-е место. В качестве упражнения выясните, как вычислить площадь 2-го треугольника с 3 вершинами, которые определяются тремя индексами 0,1,2. Затем проделаем то же самое с индексами 2,1,0.   -  person starmole    schedule 27.04.2014
comment
Я пытаюсь удалить обращенные вперед треугольники, которых никогда не было. В качестве простого примера, среднему блоку нужны только верхняя и нижняя грани - все остальные грани используются в другом блоке и никогда не видны.   -  person Brian Bruggeman    schedule 27.04.2014
comment
Вы можете выполнить предварительный проход только с глубиной, чтобы удалить внутренние грани каркаса. Технически это не собирается ничего отбирать, просто предотвратите затенение этих лиц, заставив их не пройти тест глубины во второй раз. Поскольку это больше похоже на то, что вы пытаетесь сделать это, чтобы уменьшить нагрузку на обработку вершин, я не думаю, что это то, что вам действительно нужно. Но, честно говоря, в этом простом примере не было бы никакой пользы от идентификации закрытых лиц, это заняло бы больше времени на обработку, чем экономило бы. Это верно, даже если вы увеличиваете его до большего количества треугольников (в пределах разумного).   -  person Andon M. Coleman    schedule 27.04.2014
comment
Вы ориентируетесь на определенные платформы? Поскольку вы используете Python, я полагаю, что это не графические процессоры мобильного класса, у которых частично очень разные архитектуры. Большинство традиционных графических процессоров выполняют тестирование глубины перед фрагментным шейдером. Если вы можете визуализировать свою геометрию спереди назад, что может быть довольно просто с блоками в сетке, скрытые грани будут удалены до фрагментного шейдера. Возможно, это лучшее, что ты можешь сделать. Я сомневаюсь, что применение логики к процессору, особенно в Python, будет более эффективным, чем позволить графическому процессору устранять невидимые лица.   -  person Reto Koradi    schedule 27.04.2014
comment
@RetoKoradi: В настоящее время я не ориентируюсь на платформы. Однако, если я могу написать его на python, я могу легко перевести его на c / c ++ / objC, если мне понадобится позже. Python позволяет мне создавать прототипы дизайна без затрат на отладку, связывание / компиляцию / проблемы с библиотекой. Кроме того, я новичок в openGL, так что это тоже полезный опыт.   -  person Brian Bruggeman    schedule 27.04.2014
comment
@ AndonM.Coleman: Прочитав ваш комментарий, я провел небольшое тестирование. При правильной работе отбраковки я могу увидеть на 50% сокращение времени, необходимого для рендеринга некоторых моих сцен в десятках тысяч или сотнях тысяч ящиков. Поэтому я думаю, что удаление лицевых лиц имеет большой смысл.   -  person Brian Bruggeman    schedule 28.04.2014


Ответы (2)


См. здесь, чтобы узнать, как реализовать алгоритм отбраковки. В вашем коде отбраковки вы делаете что-то совершенно неуместное. Чтобы определить, следует ли отбраковывать грань, вам нужно только проверить порядок ее наматывания (при условии, что вы указали все треугольники в порядке CW или CCW по отношению к их нормалям). Если ваши треугольники не упорядочены, то для определения отсечения вам необходимо проверить z-порядок вершин треугольника, что графическая карта делает автоматически. Если вы решите реализовать это самостоятельно, вы эффективно реализуете программный рендеринг (хотя рендеринг выполняется фрагментами треугольника, а не отдельными пикселями).

person d3dave    schedule 27.04.2014
comment
Собственно, это становится важным, когда у меня потом появляется около 350 000 лишних лиц. Я не пытаюсь убрать треугольники, повернутые назад. Как вы заметили, это уже делается автоматически. Тем не менее, я пытаюсь отбраковать передние треугольники, которые никогда не будут видны. Я действительно думаю, что это может быть проблема с заводом. Но я не уверен, почему мои передние треугольники работают нормально, когда все присутствуют, но кажутся неправильными, когда некоторых нет. - person Brian Bruggeman; 27.04.2014
comment
@ Брайан, хорошо, теперь я думаю, что понимаю лучше. В таком случае я не знаю, как сделать то, о чем вы просите банкомат. - person d3dave; 27.04.2014

Я неправильно рассчитывал индексы. Новый метод должен быть:

        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)
person Brian Bruggeman    schedule 27.04.2014