Ali Ent
Ali Ent

Reputation: 2617

problem with backface culling on OpenGL python

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.

Upvotes: 1

Views: 252

Answers (1)

Rabbid76
Rabbid76

Reputation: 210878

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)

Upvotes: 1

Related Questions