PyOpenGL 透视投影

PyOpenGL Perspective Projection

我对 PyOpenGL 比较陌生,开始学习 t运行sformations 和模型、视图和投影矩阵。这很好,直到我编译 运行 我的代码,发现我的对象突然消失了,老实说,我不知道 OpenGL 是否正在剪裁我的对象,或者只是因为一些相机错误而没有显示它,但我相信它与我的代码中实现的投影矩阵有关,因为当我从我的代码中删除投影矩阵并 运行 我的程序时,一切突然又开始工作了,除了我不会' 已实施透视投影。无论如何,我们将不胜感激:D

这是我的 PyOpenGL 代码。

import OpenGL, PIL, pygame, numpy, pyrr, math, sys, os

from OpenGL.GL import *
from PIL import Image
from pyrr import Matrix44, Vector4, Vector3, Quaternion

VERT_DATA = numpy.array([0.5, 0.5, 0.0,
                         0.5, -0.5, 0.0,
                        -0.5, -0.5, 0.0,
                        -0.5, 0.5, 0.0],
                        dtype="float32")

COLOR_DATA = numpy.array([1.0, 0.0, 0.0, 1.0,
                          0.0, 1.0, 0.0, 1.0,
                          0.0, 0.0, 1.0, 1.0,
                          0.0, 1.0, 1.0, 1.0],
                          dtype="float32")

TEXTURE_COORD_DATA = numpy.array([0.5, 0.5,
                                  0.5, -0.5,
                                 -0.5, -0.5,
                                 -0.5, 0.5],
                                 dtype="float32")

INDICES = numpy.array([0, 1, 3,
                       1, 2, 3],
                       dtype="int32")

class GLProgram:
    def __init__(self):
        self.gl_program = glCreateProgram()
        self.mvp_matrix = self.projection()
        self.shaders()
        self.gl_buffers()

    def gl_texture(self, texture_path):
        image = Image.open(texture_path).transpose(Image.FLIP_TOP_BOTTOM)
        image_data = numpy.fromstring(image.tobytes(), numpy.uint8)
        width, height = image.size

        texture = glGenTextures(1)
        glBindTexture(GL_TEXTURE_2D, texture)

        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)

        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image_data)
        glGenerateMipmap(GL_TEXTURE_2D)

        return texture

    def gl_buffers(self):
        self.vao = glGenVertexArrays(1)
        glBindVertexArray(self.vao)

        self.pos_vbo = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, self.pos_vbo)
        glBufferData(GL_ARRAY_BUFFER, VERT_DATA, GL_STATIC_DRAW)
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
        glEnableVertexAttribArray(0)

        self.text_coord_vbo = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, self.text_coord_vbo)
        glBufferData(GL_ARRAY_BUFFER, TEXTURE_COORD_DATA, GL_STATIC_DRAW)
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, None)
        glEnableVertexAttribArray(1)

        self.pos_ebo = glGenBuffers(1)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.pos_ebo)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.pos_ebo)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, INDICES, GL_STATIC_DRAW)

        self.brick_texture = self.gl_texture("check.jpg")

    def shaders(self):
        vertex_shader = glCreateShader(GL_VERTEX_SHADER)
        fragment_shader = glCreateShader(GL_FRAGMENT_SHADER)

        with open("VertexShader.vert", "r") as vert_file:
            vert_source = vert_file.read()
        with open("FragmentShader.frag", "r") as frag_file:
            frag_source = frag_file.read()

        glShaderSource(vertex_shader, vert_source)
        glShaderSource(fragment_shader, frag_source)

        glCompileShader(vertex_shader)
        if not glGetShaderiv(vertex_shader, GL_COMPILE_STATUS):
            info_log = glGetShaderInfoLog(vertex_shader)
            print ("Compilation Failure for " + vertex_shader + " shader:\n" + info_log)

        glCompileShader(fragment_shader)
        if not glGetShaderiv(fragment_shader, GL_COMPILE_STATUS):
            info_log = glGetShaderInfoLog(fragment_shader)
            print ("Compilation Failure for " + fragment_shader + " shader:\n" + info_log)

        glAttachShader(self.gl_program, vertex_shader)
        glAttachShader(self.gl_program, fragment_shader)

        glLinkProgram(self.gl_program)

        glDeleteShader(vertex_shader)
        glDeleteShader(fragment_shader)

    def projection(self):
        scale_matrix = pyrr.matrix44.create_from_scale(Vector3([1, 1, 1]))
        rot_matrix = Matrix44.identity()
        trans_matrix = pyrr.matrix44.create_from_translation(Vector3([1, 1, 0]))

        model_matrix = scale_matrix * rot_matrix * trans_matrix
        view_matrix = pyrr.matrix44.create_look_at(numpy.array([4, 3, 3]), numpy.array([1, 1, 0]), numpy.array([0, 1, 0]))
        proj_matrix = pyrr.matrix44.create_perspective_projection_matrix(45.0, 1280/720, 0.1, 1000.0)
        mvp_matrix = proj_matrix * view_matrix * model_matrix

        return mvp_matrix

    def display(self):
        glEnable(GL_DEPTH_TEST)

        glClearColor(0.0, 0.0, 0.0, 1.0)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

        glUseProgram(self.gl_program)
        glActiveTexture(GL_TEXTURE0)
        glBindTexture(GL_TEXTURE_2D, self.brick_texture)
        texture_uniform = glGetUniformLocation(self.gl_program, "the_texture")
        glUniform1i(texture_uniform, 0)

        trans_uniform = glGetUniformLocation(self.gl_program, "mvp")
        glUniformMatrix4fv(trans_uniform, 1, GL_FALSE, self.mvp_matrix)

        glBindVertexArray(self.vao)

        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
        glUseProgram(0)

def main():
    pygame.init()
    os.environ['SDL_VIDEO_CENTERED'] = '1'
    pygame.display.set_caption("3D Graphics")
    pygame.display.set_mode((1280, 720), pygame.DOUBLEBUF | pygame.OPENGL)
    clock = pygame.time.Clock()
    gl = GLProgram()

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

        clock.tick(60)
        gl.display()
        pygame.display.flip()

if __name__ == "__main__":
    main()

顶点着色器:

#version 330 core

layout (location = 0) in vec3 position;
layout (location = 1) in vec2 text_coord;

out vec2 final_text_coord;

uniform mat4 mvp;

void main() {
    gl_Position = mvp * vec4(position, 1.0);
    final_text_coord = text_coord;
}

片段着色器:

#version 330 core

in vec2 final_text_coord;

out vec4 frag_color;

uniform sampler2D the_texture;

void main() {
    frag_color = texture(the_texture, final_text_coord);
}

您的代码有几个问题:

  1. 矩阵乘法。将两个二维 numpy 数组相乘会产生分量乘积,而不是矩阵乘法。使用 numpy.matmul 将解决该问题。

  2. 矩阵约定不明确。

    正如 pyrr 的文档所述,矩阵在内存中按行优先排列,这与 GL 的默认约定相反。此外,pyrr 创建转置为标准 GL 约定的矩阵。人们可能会认为这两件事会相互抵消,这只是在一定程度上是正确的。每当您实际对这些矩阵执行一些其他操作(如乘法)时,它就会中断,然后将使用本机约定,并搞砸了。

我确实修改了你的代码,提供了两种不同的解决方案。由于您在问题和答案之间稍微更改了代码,因此我最终得到了两个版本的混合体。我还禁用了纹理的东西,因为我缺少一些库(和图像文件)。我相应地修改了片段着色器,但这并不重要。

import OpenGL, PIL, pygame, numpy, pyrr, math, sys, os

from OpenGL.GL import *
from PIL import Image
from pyrr import Matrix44, Vector4, Vector3, Quaternion

VERT_DATA = numpy.array([0.5, 0.5, 0.0,
                         0.5, -0.5, 0.0,
                        -0.5, -0.5, 0.0,
                        -0.5, 0.5, 0.0],
                        dtype="float32")

COLOR_DATA = numpy.array([1.0, 0.0, 0.0, 1.0,
                          0.0, 1.0, 0.0, 1.0,
                          0.0, 0.0, 1.0, 1.0,
                          0.0, 1.0, 1.0, 1.0],
                          dtype="float32")

TEXTURE_COORD_DATA = numpy.array([0.5, 0.5,
                                  0.5, -0.5,
                                 -0.5, -0.5,
                                 -0.5, 0.5],
                                 dtype="float32")

INDICES = numpy.array([0, 1, 3,
                       1, 2, 3],
                       dtype="int32")

WINDOW_WIDTH=1280
WINDOW_HEIGHT=720

class GLProgram:
    def __init__(self):
        self.gl_program = glCreateProgram()
        self.mvp_matrix = self.projection()
        self.shaders()
        self.gl_buffers()
        self.cube_model_matrix, self.cube_view_matrix, self.cube_proj_matrix = self.gl_translate(Vector3([1.0, 1.0, 1.0]), 45.0, Vector3([0.5, 0.5, 0.5]))
        self.cube_mvp = self.gl_translate3(Vector3([1.0, 1.0, 1.0]), -45.0, Vector3([0.5, 0.5, 0.5]))

    def gl_texture(self, texture_path):
        return 0

    def gl_buffers(self):
        self.vao = glGenVertexArrays(1)
        glBindVertexArray(self.vao)

        self.pos_vbo = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, self.pos_vbo)
        glBufferData(GL_ARRAY_BUFFER, VERT_DATA, GL_STATIC_DRAW)
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
        glEnableVertexAttribArray(0)

        self.text_coord_vbo = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, self.text_coord_vbo)
        glBufferData(GL_ARRAY_BUFFER, TEXTURE_COORD_DATA, GL_STATIC_DRAW)
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, None)
        glEnableVertexAttribArray(1)

        self.pos_ebo = glGenBuffers(1)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.pos_ebo)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.pos_ebo)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, INDICES, GL_STATIC_DRAW)

        self.brick_texture = self.gl_texture("check.jpg")

    def shaders(self):
        vertex_shader = glCreateShader(GL_VERTEX_SHADER)
        fragment_shader = glCreateShader(GL_FRAGMENT_SHADER)

        with open("VertexShader.vert", "r") as vert_file:
            vert_source = vert_file.read()
        with open("FragmentShader.frag", "r") as frag_file:
            frag_source = frag_file.read()

        glShaderSource(vertex_shader, vert_source)
        glShaderSource(fragment_shader, frag_source)

        glCompileShader(vertex_shader)
        if not glGetShaderiv(vertex_shader, GL_COMPILE_STATUS):
            info_log = glGetShaderInfoLog(vertex_shader)
            print ("Compilation Failure for " + vertex_shader + " shader:\n" + info_log)

        glCompileShader(fragment_shader)
        if not glGetShaderiv(fragment_shader, GL_COMPILE_STATUS):
            info_log = glGetShaderInfoLog(fragment_shader)
            print ("Compilation Failure for " + fragment_shader + " shader:\n" + info_log)

        glAttachShader(self.gl_program, vertex_shader)
        glAttachShader(self.gl_program, fragment_shader)

        glLinkProgram(self.gl_program)

        glDeleteShader(vertex_shader)
        glDeleteShader(fragment_shader)

    def projection(self):
        scale_matrix = pyrr.matrix44.create_from_scale(Vector3([1, 1, 1]))
        rot_matrix = Matrix44.identity()
        trans_matrix = pyrr.matrix44.create_from_translation(Vector3([1, 1, 0]))

        model_matrix = scale_matrix * rot_matrix * trans_matrix
        view_matrix = pyrr.matrix44.create_look_at(numpy.array([4, 3, 3]), numpy.array([1, 1, 0]), numpy.array([0, 1, 0]))
        proj_matrix = pyrr.matrix44.create_perspective_projection_matrix(45.0, 1280/720, 0.1, 1000.0)
        mvp_matrix = proj_matrix * view_matrix * model_matrix

        return mvp_matrix
    def gl_translate(self, translation, rotation, scale):
        trans_matrix = pyrr.matrix44.create_from_translation(translation)
        rot_matrix = numpy.transpose(pyrr.matrix44.create_from_y_rotation(rotation))
        scale_matrix = numpy.transpose(pyrr.matrix44.create_from_scale(scale))

        model_matrix = scale_matrix * rot_matrix * trans_matrix
        view_matrix = pyrr.matrix44.create_look_at(numpy.array([2.0, 2.0, 3.0], dtype="float32"),
            numpy.array([0.0, 0.0, 0.0], dtype="float32"),
            numpy.array([0.0, 1.0, 0.0], dtype="float32"))
        proj_matrix = pyrr.matrix44.create_perspective_projection(45.0, WINDOW_WIDTH/WINDOW_HEIGHT, 0.1, 200.0)

        return model_matrix, view_matrix, proj_matrix

    def gl_translate2(self, translation, rotation, scale):
        trans_matrix = pyrr.matrix44.create_from_translation(translation)
        rot_matrix = pyrr.matrix44.create_from_y_rotation(rotation)
        scale_matrix = pyrr.matrix44.create_from_scale(scale)

        model_matrix = numpy.matmul(numpy.matmul(scale_matrix,rot_matrix),trans_matrix)
        view_matrix = pyrr.matrix44.create_look_at(numpy.array([2.0, 2.0, 3.0], dtype="float32"),
            numpy.array([0.0, 0.0, 0.0], dtype="float32"),
            numpy.array([0.0, 1.0, 0.0], dtype="float32"))
        proj_matrix = pyrr.matrix44.create_perspective_projection(45.0, WINDOW_WIDTH/WINDOW_HEIGHT, 0.1, 200.0)
        m = numpy.matmul(numpy.matmul(model_matrix,view_matrix),proj_matrix) 

        return m
    def gl_translate3(self, translation, rotation, scale):
        trans_matrix = numpy.transpose(pyrr.matrix44.create_from_translation(translation))
        rot_matrix = numpy.transpose(pyrr.matrix44.create_from_y_rotation(rotation))
        scale_matrix = numpy.transpose(pyrr.matrix44.create_from_scale(scale))

        model_matrix = numpy.matmul(numpy.matmul(trans_matrix,rot_matrix),scale_matrix)
        view_matrix = numpy.transpose(pyrr.matrix44.create_look_at(numpy.array([2.0, 2.0, 3.0], dtype="float32"),
            numpy.array([0.0, 0.0, 0.0], dtype="float32"),
            numpy.array([0.0, 1.0, 0.0], dtype="float32")))
        proj_matrix = numpy.transpose(pyrr.matrix44.create_perspective_projection(45.0, WINDOW_WIDTH/WINDOW_HEIGHT, 0.1, 200.0))
        m = numpy.matmul(numpy.matmul(proj_matrix,view_matrix),model_matrix) 

        return numpy.transpose(m)

    def display(self):
        glEnable(GL_DEPTH_TEST)

        glClearColor(0.0, 0.0, 0.0, 1.0)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

        glUseProgram(self.gl_program)
        glActiveTexture(GL_TEXTURE0)
        glBindTexture(GL_TEXTURE_2D, self.brick_texture)
        texture_uniform = glGetUniformLocation(self.gl_program, "the_texture")
        glUniform1i(texture_uniform, 0)

        trans_uniform = glGetUniformLocation(self.gl_program, "mvp")
        glUniformMatrix4fv(trans_uniform, 1, GL_FALSE, self.cube_mvp)
        #model_location = glGetUniformLocation(self.gl_program, "model")
        #glUniformMatrix4fv(model_location, 1, GL_FALSE, self.cube_model_matrix)
        #view_location = glGetUniformLocation(self.gl_program, "view")
        #glUniformMatrix4fv(view_location, 1, GL_FALSE, self.cube_view_matrix)
        #proj_location = glGetUniformLocation(self.gl_program, "proj")
        #glUniformMatrix4fv(proj_location, 1, GL_FALSE, self.cube_proj_matrix)
        glBindVertexArray(self.vao)

        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
        glUseProgram(0)

def main():
    pygame.init()
    os.environ['SDL_VIDEO_CENTERED'] = '1'
    pygame.display.set_caption("3D Graphics")
    pygame.display.set_mode((1280, 720), pygame.DOUBLEBUF | pygame.OPENGL)
    clock = pygame.time.Clock()
    gl = GLProgram()

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

        clock.tick(60)
        gl.display()
        pygame.display.flip()

if __name__ == "__main__":
    main()

使用这个顶点着色器:

#version 330 core

layout (location = 0) in vec3 position;
layout (location = 1) in vec2 text_coord;

out vec2 final_text_coord;

uniform mat4 mvp;

void main() {
    gl_Position = mvp * vec4(position, 1.0);
    final_text_coord = text_coord;
}

和这个片段着色器:

#version 330 core

in vec2 final_text_coord;

out vec4 frag_color;

uniform sampler2D the_texture;

void main() {
    frag_color = vec4(1,0,0,1);
}

我特地添加了方法gl_translate2gl_translate3。两者都产生相同的矩阵,2 变体仅使用 pyrr 库的本机乘法顺序约定,而 gl_translate3 使用 GL 约定。

我还创建了具有不同参数的矩阵

self.cube_mvp = self.gl_translate3(Vector3([1.0, 1.0, 1.0]), -45.0, Vector3([0.5, 0.5, 0.5]))

旋转的负号不同(并补偿您在 gl_transaltion 中所做的额外 transpose)。

结果与您在发布的答案中得到的结果不同,但这是因为您的模型矩阵在该答案中被破坏了,这是由于错误的乘法函数(特别是删除了翻译部分并扭曲了旋转成一些剪切操作)。

我使用 gl_translate2gl_translate3 得到的结果是: 对于指定的参数,这看起来非常合理。