problem with backface culling on OpenGL python

Question:

My goal is to render a .pmx 3D model using PyOpenGL on pygame. I’ve found pymeshio module that extracts vertices and normal vectors and etc. found an example code on it’s github repo that renders on tkinter. I changed the code to render on pygame instead, didn’t change parts related to OpenGL rendering. The output is this:

enter image description here enter image description here

The model file is not corrupted, I checked it on Blender and MMD. I’m new with OpenGL and 3D programming but I think it might be related to sequence of vertices for back-face culling, some of triangles are clockwise and some of the others are counter clockwise.

this is rendering code. it uses draw function to render.

class IndexedVertexArray(object):
    def __init__(self):
        # vertices
        self.vertices=[]
        self.normal=[]
        self.colors=[]
        self.uvlist=[]
        self.b0=[]
        self.b1=[]
        self.w0=[]
        self.materials=[]
        self.indices=[]
        self.buffers=[]

        self.new_vertices=[]
        self.new_normal=[]

    def addVertex(self, pos, normal, uv, color, b0, b1, w0):
        self.vertices+=pos
        self.normal+=normal
        self.colors+=color
        self.uvlist+=uv
        self.b0.append(b0)
        self.b1.append(b1)
        self.w0.append(w0)

    def setIndices(self, indices):
        self.indices=indices

    def addMaterial(self, material):
        self.materials.append(material)

    def create_array_buffer(self, buffer_id, floats):
        # print('create_array_buuffer', buffer_id)
        glBindBuffer(GL_ARRAY_BUFFER, buffer_id)
        glBufferData(GL_ARRAY_BUFFER, 
                len(floats)*4,  # byte size
                (ctypes.c_float*len(floats))(*floats), # 謎のctypes
                GL_STATIC_DRAW)

    def create_vbo(self):
        self.buffers = glGenBuffers(4+1)
        # print("create_vbo", self.buffers)

        self.create_array_buffer(self.buffers[0], self.vertices)
        self.create_array_buffer(self.buffers[1], self.normal)
        self.create_array_buffer(self.buffers[2], self.colors)
        self.create_array_buffer(self.buffers[3], self.uvlist)

        # indices
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.buffers[4])
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, 
                len(self.indices)*4, # byte size
                (ctypes.c_uint*len(self.indices))(*self.indices),  # 謎のctypes
                GL_STATIC_DRAW)

    def draw(self):
        if len(self.buffers)==0:
            self.create_vbo()

        glEnableClientState(GL_VERTEX_ARRAY);
        glBindBuffer(GL_ARRAY_BUFFER, self.buffers[0]);
        glVertexPointer(4, GL_FLOAT, 0, None);

        glEnableClientState(GL_NORMAL_ARRAY);
        glBindBuffer(GL_ARRAY_BUFFER, self.buffers[1]);
        glNormalPointer(GL_FLOAT, 0, None);

        glEnableClientState(GL_COLOR_ARRAY);
        glBindBuffer(GL_ARRAY_BUFFER, self.buffers[2]);
        glColorPointer(4, GL_FLOAT, 0, None);

        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        glBindBuffer(GL_ARRAY_BUFFER, self.buffers[3]);
        glTexCoordPointer(2, GL_FLOAT, 0, None);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.buffers[4]);
        index_offset=0
        for i, m in enumerate(self.materials):
            # submesh
            m.begin()
            glDrawElements(GL_TRIANGLES, m.vertex_count, GL_UNSIGNED_INT, ctypes.c_void_p(index_offset));
            index_offset+=m.vertex_count * 4 # byte size
            m.end()

        # cleanup
        glDisableClientState(GL_TEXTURE_COORD_ARRAY)
        glDisableClientState(GL_COLOR_ARRAY)
        glDisableClientState(GL_NORMAL_ARRAY);
        glDisableClientState(GL_VERTEX_ARRAY)

this is the part related to back-face culling


class MQOMaterial(object):
    def __init__(self):
        self.rgba=(1, 1, 1, 1)
        self.vcol=False
        self.texture=None

    def __enter__(self):
        self.begin()

    def __exit__(self):
        self.end()

    def begin(self):
        glColor4f(*self.rgba)
        if self.texture:
            self.texture.begin()

        # backface culling
        glEnable(GL_CULL_FACE)
        glFrontFace(GL_CW)
        glCullFace(GL_BACK)
        # glCullFace(GL_FRONT)
        # alpha test
        glEnable(GL_ALPHA_TEST);
        glAlphaFunc(GL_GREATER, 0.5);

    def end(self):
        if self.texture:
            self.texture.end()

First I disabled alpha channel and did nothing.
I tried GL_FRONT and GL_CCW but it didn’t work.
I tried to separate vertices groups and render them using glVertex3fv. the original code already saves vertices in this format:

vertices = [v0.x, v0.y, v0.z, 1, v1.x, v1.y, v1.z, 1, v2.x, v2.y, v2.z, 1, ...]
            ___________________  ___________________  ___________________
                   v0                    v1                  v2
normal = [v0.normal.x, v0.normal.y, v0.normal.z, v1.normal.x, v1.normal.y, v1.normal.z, ...]
          _____________________________________  _____________________________________
                          v0                                   v1
indices = [0, 1, 2, 1, 4, 5, 2, 4, 6, ...]
           -------  -------  -------
           group0   group1    group2

I tried to render triangles with this code:

def _draw(self):
    glBegin(GL_TRIANGLES)
    for i in range(len(self.indices) // 3):
      # glTexCoord2fv( tex_coords[ti] )
      if i == len(self.new_normal):
          break
          # glNormal3fv( self.new_normal[i] )
          glVertex3fv( self.new_vertices[i])
    glEnd()


def new_sort(self):
    for i in range(len(self.indices) // 3):
       if i <= -1:
           continue
       k = 4 * i
       j = 3 * i
       if k + 2 >= len(self.vertices) or j + 2 >= len(self.normal):
            break
       self.new_vertices.append(tuple((self.vertices[k], self.vertices[k + 1], self.vertices[k + 2] )))
       self.new_normal.append(tuple((self.normal[j], self.normal[j + 1], self.normal[j + 2] )))

the output
enter image description here

I thought maybe wrong points were together so shifted them with 1 and 2 to set correct points but the output became uglier. I tested this with quadrilateral and no change.

I would be appreciated for any help or hint.

Asked By: Ali Ent

||

Answers:

The colorful images on the top seem to be rendered without depth test. You have to enable the Depth Test and clear the depth buffer:

glEnable(GL_DEPTH_TEST)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
Answered By: Rabbid76
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.