Используя PyOpenGL, как применить инстансинг к файлу .obj? - PullRequest
0 голосов
/ 03 марта 2020

Я пытаюсь применить инстансинг к файлу объекта (.obj), следуя примеру на Learn Opengl - Instancing .

Мой вопрос фактически состоит из двух частей:

1) Как применить инстансинг к файлам .obj? (Я думаю, что я почти там)

2) Как я понимаю, для использования экземпляров необходимо сохранить файл .obj в буфере массивов вершин (VAO), и, следовательно, нужна помощь в настройке материалов и текстуры для файла .obj. Это если мое предположение верно? (На данный момент я просто присвоил объекту solid белого цвета.)

Что я сделал до сих пор:

Я использую Pywavefront для загрузки .obj файл, но я не застрял на нем, если нужен другой загрузчик obj.

Настройка местоположений, в которых нужно разместить объект:

        #Locations
        vertices = [[-33.64413, 0.0, 19.03057], [-33.64818, 0.0, 19.0219],
                        [-33.64921, 0.0, 19.011],[-33.64913, 0.0, 19.03027], [-33.69828, 0.0, 19.0219],
                        [-33.64991, 0.0, 19.021]]

        self.vertices = numpy.array(vertices, dtype='f')

Загрузка OBJ и сбор вершин и граней (индексов) :

        obj_file = pywavefront.Wavefront('OBJ_PINS/Mcdonalds.obj', collect_faces=True)
        obj_vertices = numpy.array(obj_file.vertices, dtype='f')
        faces_list = []
        for mesh in obj_file.mesh_list:
            for face in mesh.faces:
                faces_list.append(face)

        obj_ind = numpy.array(faces_list,  dtype=numpy.int32)

Затем я создаю объект массива вершин (VAO) с необходимым объектом буфера вершин (VBO):

  VAOs = glGenVertexArrays(1)
        glBindVertexArray(VAOs)

        # Vertex Buffer Object
        VBO = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, VBO)
        glBufferData(GL_ARRAY_BUFFER, obj_vertices.nbytes, obj_vertices, GL_STATIC_DRAW)

        # Element Buffer Object
        EBO = glGenBuffers(1)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, obj_ind.nbytes, obj_ind, GL_STATIC_DRAW)

        # vertices
        glEnableVertexAttribArray(0)
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, obj_vertices.itemsize * 3, ctypes.c_void_p(0))

Преобразование и сохранение объекта для создания экземпляров:

      # instance locations
      amount = len(self.vertices)
      #List storing the same object in different places
      modelMatrices = []

      #Storring the objects in a list
      for i in range(0, amount):
          model_o = glm.mat4(1.0)
          model_o = glm.translate(model_o, glm.vec3(self.vertices[i][0], self.vertices[i][1], self.vertices[i][2]))
          model_o = glm.scale(model_o, glm.vec3(0.0001, 0.0001, 0.0001))
          modelMatrices.append(model_o)

Интересная часть, экземпляр объекта в разных местах:

    #Create VBO for instancing
    instanceVBO = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, instanceVBO)
    glBufferData(GL_ARRAY_BUFFER, glm.sizeof(glm.mat4), glm.value_ptr(modelMatrices[0]), GL_STATIC_DRAW)

    #Bind each vertex attrib array of matrices (4 vectors in Matrix)
    glEnableVertexAttribArray(3)
    glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p(0))
    glEnableVertexAttribArray(4)
    glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p(glm.sizeof(glm.vec4)))
    glEnableVertexAttribArray(5)
    glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p((2 * glm.sizeof(glm.vec4))))
    glEnableVertexAttribArray(6)
    glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p((3 * glm.sizeof(glm.vec4))))

    #Set instancing
    glVertexAttribDivisor(3, 1)
    glVertexAttribDivisor(4, 1)
    glVertexAttribDivisor(5, 1)
    glVertexAttribDivisor(6, 1)

    #End VAO    
    glBindVertexArray(0)

Шейдеры:

self.vertex_obj = """
                       # version 330
                       in layout(location = 0) vec3 aPos;
                       in layout (location = 3) mat4 instanceMatrix;

                       uniform mat4 model;
                       uniform mat4 view;
                       uniform mat4 projection;

                       void main()
                       {
                           gl_Position = projection * view * instanceMatrix * vec4(aPos, 1.0);
                       }
                       """
 self.fragment_obj = """
                       #version 330
                       out vec4 FragColor;

                       void main()
                       {                           
                               FragColor = vec4(1.0,1.0,1.0, 1.0);
                       }
                        """

Когда я рисую объект на чертеже l oop Я вижу только один объект, а не 6:

    glBindVertexArray(VAOs)
    glDrawElementsInstanced(GL_TRIANGLES, len(obj_ind)*3, GL_UNSIGNED_INT, None, amount)
    glBindVertexArray(0)

Что я считаю неправильным:

Мне нужно вызвать весь список матриц для рисования в instanced glBufferData, но не знаю, как это сделать.

Если он изменится на что-то вроде:

glBufferD ata (GL_ARRAY_BUFFER, сумма * glm.sizeof (glm.mat4), glm.value_ptr (modelMatrices [?]), GL_STATIC_DRAW)

ПОЛНЫЙ КОД

    import numpy
    import glfw
    from OpenGL.GL import *
    from OpenGL.GL.shaders import compileProgram, compileShader
    import pyrr
    import glm
    from pyrr import  matrix44
    from math import sin, cos, radians
    from OBJ_Loader import*
    import pywavefront



    class threeD_viewer():
        def __init__(self):

            vertices = [[-33.64413, 0.0, 19.03057], [-33.64818, 0.0, 19.0219],
                            [-33.64921, 0.0, 19.011],[-33.64913, 0.0, 19.03027], [-33.69828, 0.0, 19.0219],
                            [-33.64991, 0.0, 19.021]]

            self.vertices = numpy.array(vertices, dtype='f')

            self.cam = Camera(self.vertices[0][0], self.vertices[0][2])
            self.WIDTH, self.HEIGHT = 1280, 720
            self.lastX, self.LastY = self.WIDTH / 2, self.HEIGHT / 2
            self.first_mouse = True
            self.forward, self.backward, self.right, self.left, self.up, self.down = False, False, False, False, False, False

            self.x_start, self.y_start = self.vertices[0][0], self.vertices[0][2]

            self.vertex_obj = """
                       # version 330
                       in layout(location = 0) vec3 aPos;
                       in layout (location = 3) mat4 instanceMatrix;

                       uniform mat4 model;
                       uniform mat4 view;
                       uniform mat4 projection;

                       void main()
                       {
                           gl_Position = projection * view * instanceMatrix * vec4(aPos, 1.0);
                       }
                       """
            self.fragment_obj = """
                       #version 330
                       out vec4 FragColor;

                       void main()
                       {                           
                               FragColor = vec4(1.0,1.0,1.0, 1.0);
                       }
                        """
            self.main()

        # the keyboard input callback
        def key_input_clb(self, window, key, scancode, action, mode):
            if key == glfw.KEY_ESCAPE and action == glfw.PRESS:
                glfw.set_window_should_close(window, True)
            if key == glfw.KEY_W and action == glfw.PRESS:
                self.forward = True
            elif key == glfw.KEY_W and action == glfw.RELEASE:
                self.forward = False
            if key == glfw.KEY_S and action == glfw.PRESS:
                self.backward = True
            elif key == glfw.KEY_S and action == glfw.RELEASE:
                self.backward = False
            if key == glfw.KEY_A and action == glfw.PRESS:
                self.left = True
            elif key == glfw.KEY_A and action == glfw.RELEASE:
                self.left = False
            if key == glfw.KEY_D and action == glfw.PRESS:
                self.right = True
            elif key == glfw.KEY_D and action == glfw.RELEASE:
                self.right = False
            if key == glfw.KEY_Q and action == glfw.PRESS:
                self.up = True
            elif key == glfw.KEY_Q and action == glfw.RELEASE:
                self.up = False
            if key == glfw.KEY_E and action == glfw.PRESS:
                self.down = True
            elif key == glfw.KEY_E and action == glfw.RELEASE:
                self.down = False

        def do_movement(self):
            if self.forward:
                self.cam.process_keyboard("FORWARD", 0.000008)
            if self.backward:
                self.cam.process_keyboard("BACKWARD", 0.000008)
            if self.right:
                self.cam.process_keyboard("RIGHT", 0.000008)
            if self.left:
                self.cam.process_keyboard("LEFT", 0.000008)
            if self.up:
                self.cam.process_keyboard("UP", 0.000008)
            if self.down:
                self.cam.process_keyboard("DOWN", 0.000008)

        def mmouse_look_clb(self, window, xpos, ypos):
            if self.first_mouse:
                self.lastX = xpos
                self.lastY = ypos
                self.first_mouse = False

            xoffset = xpos - self.lastX
            yoffset = self.lastY - ypos

            self.lastX = xpos
            self.lastY = ypos
            self.cam.process_mouse_movement(xoffset, yoffset)


        def window_resize(self, window, width, height):
            glViewport(0, 0, width, height)
            projection = pyrr.matrix44.create_perspective_projection_matrix(45, width / height,  0.0001, 100)
            proj_loc = glGetUniformLocation(self.shader_obj, "projection")
            glUniformMatrix4fv(proj_loc, 1, GL_FALSE, projection)

        def main(self):

            # initializing glfw library
            if not glfw.init():
                raise Exception("glfw can not be initialized!")
            # creating the window
            window = glfw.create_window(self.WIDTH, self.HEIGHT, "My OpenGL window", None, None)
            # check if window was created
            if not window:
                glfw.terminate()
                raise Exception("glfw window can not be created!")


            # set window's position
            glfw.set_window_pos(window, 400, 200)
            # set the callback function for window resize
            glfw.set_window_size_callback(window, self.window_resize)
            glfw.set_cursor_pos_callback(window, self.mmouse_look_clb)
            glfw.set_input_mode(window, glfw.CURSOR, glfw.CURSOR_DISABLED)
            glfw.set_key_callback(window, self.key_input_clb)
            # make the context current
            glfw.make_context_current(window)

            self.shader_obj = compileProgram(compileShader(self.vertex_obj, GL_VERTEX_SHADER),
                                         compileShader(self.fragment_obj, GL_FRAGMENT_SHADER))


            obj_file = pywavefront.Wavefront('OBJ_PINS/Mcdonalds.obj', collect_faces=True)
            obj_vertices = numpy.array(obj_file.vertices, dtype='f')
            faces_list = []
            for mesh in obj_file.mesh_list:
                for face in mesh.faces:
                    faces_list.append(face)

            obj_ind = numpy.array(faces_list,  dtype=numpy.int32)


            VAOs = glGenVertexArrays(1)
            glBindVertexArray(VAOs)
            # Vertex Buffer Object
            VBO = glGenBuffers(1)
            glBindBuffer(GL_ARRAY_BUFFER, VBO)
            glBufferData(GL_ARRAY_BUFFER, obj_vertices.nbytes, obj_vertices, GL_STATIC_DRAW)
            # Element Buffer Object
            EBO = glGenBuffers(1)
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, obj_ind.nbytes, obj_ind, GL_STATIC_DRAW)
            # vertices
            glEnableVertexAttribArray(0)
            glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, obj_vertices.itemsize * 3, ctypes.c_void_p(0))

            # instance locations
            amount = len(self.vertices)
            #List storing the same object in different places
            modelMatrices = []
            #Storring the objects in a list
            for i in range(0, amount):
                model_o = glm.mat4(1.0)
                model_o = glm.translate(model_o, glm.vec3(self.vertices[i][0], self.vertices[i][1], self.vertices[i][2]))
                model_o = glm.scale(model_o, glm.vec3(0.0001, 0.0001, 0.0001))
                modelMatrices.append(model_o)

            #Create VBO for instancing
            instanceVBO = glGenBuffers(1)
            glBindBuffer(GL_ARRAY_BUFFER, instanceVBO)
            glBufferData(GL_ARRAY_BUFFER, glm.sizeof(glm.mat4), glm.value_ptr(modelMatrices[0]), GL_STATIC_DRAW)
            #Bind each vertex attrib array of matrices (4 vectors in Matrix)
            glEnableVertexAttribArray(3)
            glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p(0))
            glEnableVertexAttribArray(4)
            glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p(glm.sizeof(glm.vec4)))
            glEnableVertexAttribArray(5)
            glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p((2 * glm.sizeof(glm.vec4))))
            glEnableVertexAttribArray(6)
            glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p((3 * glm.sizeof(glm.vec4))))
            #Set instancing
            glVertexAttribDivisor(3, 1)
            glVertexAttribDivisor(4, 1)
            glVertexAttribDivisor(5, 1)
            glVertexAttribDivisor(6, 1)

            #End VAO
            glBindVertexArray(0)


            glClearColor(0, 0.1, 0.1, 1)
            glEnable(GL_DEPTH_TEST)
            glEnable(GL_BLEND)
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

            #projection = pyrr.matrix44.create_perspective_projection_matrix(45, self.WIDTH / self.HEIGHT, 0.0001, 1000)

            projection = glm.perspective(glm.radians(45.0), self.WIDTH / self.HEIGHT, 0.0001, 1000)

            glUseProgram(self.shader_obj)
            view_loc_obj = glGetUniformLocation(self.shader_obj, "view")
            proj_loc_obj = glGetUniformLocation(self.shader_obj, "projection")
            model_loc_obj = glGetUniformLocation(self.shader_obj, "model")
            glUniformMatrix4fv(proj_loc_obj, 1, GL_FALSE, glm.value_ptr(projection))
            glUseProgram(0)

            # the main application loop
            while not glfw.window_should_close(window):
                glfw.poll_events()
                self.do_movement()

                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

                glUseProgram(self.shader_obj)
                view = self.cam.get_view_matrix()
                glUniformMatrix4fv(proj_loc_obj, 1, GL_FALSE, glm.value_ptr(projection))
                glUniformMatrix4fv(view_loc_obj, 1, GL_FALSE, view)

                glBindVertexArray(VAOs)
                glDrawElementsInstanced(GL_TRIANGLES, len(obj_ind)*3, GL_UNSIGNED_INT, None, amount)
                glBindVertexArray(0)

                glUseProgram(0)


                glfw.swap_buffers(window)

            # terminate glfw, free up allocated resources
            glfw.terminate()


    class Camera:
        def __init__(self, xstart, ystart):
            self.camera_pos = glm.vec3(xstart+0.005, 0, ystart+0.01)
            #self.camera_pos = glm.vec3(0, 0, 0)
            print(xstart+0.005)
            print(ystart+0.01)

            self.camera_front = glm.vec3(0.0, 0.0, -200.0)
            self.camera_up = glm.vec3(0.0, 1.0, 0.0)
            self.camera_right = glm.vec3(1.0, 0.0, 0.0)

            self.mouse_sensitivity = 0.25
            self.jaw = -90.0
            self.pitch = 0.0

        def get_view_matrix(self):
            return matrix44.create_look_at(self.camera_pos, self.camera_pos + self.camera_front, self.camera_up)

        def process_mouse_movement(self, xoffset, yoffset, constrain_pitch=True):
            xoffset *= self.mouse_sensitivity
            yoffset *= self.mouse_sensitivity

            self.jaw += xoffset
            self.pitch += yoffset

            if constrain_pitch:
                if self.pitch > 45:
                    self.pitch = 45
                if self.pitch < -45:
                    self.pitch = -45

            self.update_camera_vectors()

        def update_camera_vectors(self):
            front = glm.vec3(0.0, 0.0, 0.0)
            front.x = cos(radians(self.jaw)) * cos(radians(self.pitch))
            front.y = sin(radians(self.pitch))
            front.z = sin(radians(self.jaw)) * cos(radians(self.pitch))

            self.camera_front = glm.normalize(front)
            self.camera_right = glm.normalize(glm.cross(self.camera_front, glm.vec3(0.0, 1.0, 0.0)))
            #self.camera_up = glm.normalize(glm.cross(self.camera_right, self.camera_front))

        # Camera method for the WASD movement
        def process_keyboard(self, direction, velocity):
            if direction == "FORWARD":
                self.camera_pos += self.camera_front * velocity
            if direction == "BACKWARD":
                self.camera_pos -= self.camera_front * velocity
            if direction == "LEFT":
                self.camera_pos -= self.camera_right * velocity
            if direction == "RIGHT":
                self.camera_pos += self.camera_right * velocity
            if direction == "UP":
                self.camera_pos += self.camera_up * velocity
            if direction == "DOWN":
                self.camera_pos -= self.camera_up * velocity

    threeD_viewer()

Обновление

Экземпляр работает сейчас. Решением было сохранить список векторов, а не объекты glm.mat4:

        # instance locations
        amount = len(self.vertices)
        #List storing the same object in different places
        modelMatrices = []
        #Storring the objects in a list
        for i in range(0, amount):
            mod_list = []
            model_o = glm.mat4(1.0)
            model_o = glm.translate(model_o, glm.vec3(self.vertices[i][0], self.vertices[i][1], self.vertices[i][2]))
            for i in range(4):
                for j in range(4):
                    mod_list.append(model_o[i][j])
            modelMatrices.append(mod_list)
        modelMatrices = numpy.array(modelMatrices, dtype ="f")
        print(modelMatrices.shape)

        #Create VBO for instancing
        instanceVBO = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, instanceVBO)


        glBufferData(GL_ARRAY_BUFFER, amount* glm.sizeof(glm.mat4), modelMatrices, GL_STATIC_DRAW)
...