基于Python的OpenGL 05 之坐标系统

1. 引言

本文基于Python语言,描述OpenGL的坐标系统

前置知识可参考:

笔者这里不过多描述每个名词、函数和细节,更详细的文档可以参考:

2. 概述

OpenGL中坐标变换的流程如下图:

coordinate_systems

有图可知:

  • 创建一个物体到屏幕绘制需要三个矩阵变换:模型(Model)、观察(View)、投影(Projection)(即,MVP)

  • 裁剪坐标:\(V_{clip} = M_{projrction} \cdot M_{view} \cdot M_{model} \cdot V_{local}\)

投影时主要有两者投影方式:

  • 正交投影:平行视角

orthographic projection frustum

  • 透视投影:近大远小

 perspective_frustum

3. 编码

编码实现只需设置MVP矩阵即可

设置Model矩阵:

model = glm.mat4(1.0)
model = glm.rotate(glm.radians(-55.0)*glfw.get_time(), glm.vec3(1.0, 1.0, 0.0))

设置View矩阵:

view = glm.mat4(1.0)
# 注意,我们将矩阵向我们要进行移动场景的反方向移动
view = glm.translate(glm.vec3(0.0, 0.0, -3.0))

设置投影矩阵:

projection = glm.mat4(1.0)
projection = glm.perspective(glm.radians(45.0f), screenWidth / screenHeight, 0.1, 100.0);

在顶点着色器中设置MVP变换:

#version 330 core
layout (location = 0) in vec3 aPos;
...
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    // 注意乘法要从右向左读
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    ...
}

将变换矩阵传输到GPU:

glUniformMatrix4fv(glGetUniformLocation(shader.shaderProgram, 'model'), 1, GL_FALSE, glm.value_ptr(model))
glUniformMatrix4fv(glGetUniformLocation(shader.shaderProgram, 'view'), 1, GL_FALSE, glm.value_ptr(view))
glUniformMatrix4fv(glGetUniformLocation(shader.shaderProgram, 'projection'), 1, GL_FALSE, glm.value_ptr(projection))

至此就完成了一次简单的MVP变换,结果图如下:

image-20220803134748520

4. 立体化

构建一个立体的箱子:

设置立方体的六个面(12个三角形,36个点):

vertices = np.array([
    -0.5, -0.5, -0.5, 0.0, 0.0,
    0.5, -0.5, -0.5, 1.0, 0.0,
    0.5, 0.5, -0.5, 1.0, 1.0,
    0.5, 0.5, -0.5, 1.0, 1.0,
    -0.5, 0.5, -0.5, 0.0, 1.0,
    -0.5, -0.5, -0.5, 0.0, 0.0,

    -0.5, -0.5, 0.5, 0.0, 0.0,
    0.5, -0.5, 0.5, 1.0, 0.0,
    0.5, 0.5, 0.5, 1.0, 1.0,
    0.5, 0.5, 0.5, 1.0, 1.0,
    -0.5, 0.5, 0.5, 0.0, 1.0,
    -0.5, -0.5, 0.5, 0.0, 0.0,

    -0.5, 0.5, 0.5, 1.0, 0.0,
    -0.5, 0.5, -0.5, 1.0, 1.0,
    -0.5, -0.5, -0.5, 0.0, 1.0,
    -0.5, -0.5, -0.5, 0.0, 1.0,
    -0.5, -0.5, 0.5, 0.0, 0.0,
    -0.5, 0.5, 0.5, 1.0, 0.0,

    0.5, 0.5, 0.5, 1.0, 0.0,
    0.5, 0.5, -0.5, 1.0, 1.0,
    0.5, -0.5, -0.5, 0.0, 1.0,
    0.5, -0.5, -0.5, 0.0, 1.0,
    0.5, -0.5, 0.5, 0.0, 0.0,
    0.5, 0.5, 0.5, 1.0, 0.0,

    -0.5, -0.5, -0.5, 0.0, 1.0,
    0.5, -0.5, -0.5, 1.0, 1.0,
    0.5, -0.5, 0.5, 1.0, 0.0,
    0.5, -0.5, 0.5, 1.0, 0.0,
    -0.5, -0.5, 0.5, 0.0, 0.0,
    -0.5, -0.5, -0.5, 0.0, 1.0,

    -0.5, 0.5, -0.5, 0.0, 1.0,
    0.5, 0.5, -0.5, 1.0, 1.0,
    0.5, 0.5, 0.5, 1.0, 0.0,
    0.5, 0.5, 0.5, 1.0, 0.0,
    -0.5, 0.5, 0.5, 0.0, 0.0,
    -0.5, 0.5, -0.5, 0.0, 1.0
])

开启深度测试:

glEnable(GL_DEPTH_TEST)

清除深度缓冲:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

可选项,让箱子旋转:

model = glm.rotate(model, (float)glfwGetTime() * glm.radians(50.0f), glm.vec3(0.5f, 1.0f, 0.0f))

如果顺利的话,结果如下:

动画4

6. 多个立方体

这里的多个立方体实质就是指定(同一个立方体)平移到多个位置

设置多个位置:

cubePositions = [
    glm.vec3(0.0, 0.0, 0.0),
    glm.vec3(2.0, 5.0, -15.0),
    glm.vec3(-1.5, -2.2, -2.5),
    glm.vec3(-3.8, -2.0, -12.3),
    glm.vec3(2.4, -0.4, -3.5),
    glm.vec3(-1.7, 3.0, -7.5),
    glm.vec3(1.3, -2.0, -2.5),
    glm.vec3(1.5, 2.0, -2.5),
    glm.vec3(1.5, 0.2, -1.5),
    glm.vec3(-1.3, 1.0, -1.5)
]

绘制多个Model:

for cube in cubePositions:
    model = glm.translate(cube)
    model = glm.rotate(model, glfw.get_time(), glm.vec3(1.0, 0.3, 0.5))
    glUniformMatrix4fv(glGetUniformLocation(shader.shaderProgram, 'model'), 1, GL_FALSE, glm.value_ptr(model))
    glDrawArrays(GL_TRIANGLES, 0, 36)

实现效果:

image-20220803144759754

7. 完整代码

主要文件test.py

import glfw as glfw
from OpenGL.GL import *
import numpy as np
from PIL.Image import open
import glm as glm

import shader as shader

glfw.init()
window = glfw.create_window(800, 600, "CoordinateSystem", None, None)
glfw.make_context_current(window)

VAO = glGenVertexArrays(1)
glBindVertexArray(VAO)

vertices = np.array([
    -0.5, -0.5, -0.5, 0.0, 0.0,
    0.5, -0.5, -0.5, 1.0, 0.0,
    0.5, 0.5, -0.5, 1.0, 1.0,
    0.5, 0.5, -0.5, 1.0, 1.0,
    -0.5, 0.5, -0.5, 0.0, 1.0,
    -0.5, -0.5, -0.5, 0.0, 0.0,

    -0.5, -0.5, 0.5, 0.0, 0.0,
    0.5, -0.5, 0.5, 1.0, 0.0,
    0.5, 0.5, 0.5, 1.0, 1.0,
    0.5, 0.5, 0.5, 1.0, 1.0,
    -0.5, 0.5, 0.5, 0.0, 1.0,
    -0.5, -0.5, 0.5, 0.0, 0.0,

    -0.5, 0.5, 0.5, 1.0, 0.0,
    -0.5, 0.5, -0.5, 1.0, 1.0,
    -0.5, -0.5, -0.5, 0.0, 1.0,
    -0.5, -0.5, -0.5, 0.0, 1.0,
    -0.5, -0.5, 0.5, 0.0, 0.0,
    -0.5, 0.5, 0.5, 1.0, 0.0,

    0.5, 0.5, 0.5, 1.0, 0.0,
    0.5, 0.5, -0.5, 1.0, 1.0,
    0.5, -0.5, -0.5, 0.0, 1.0,
    0.5, -0.5, -0.5, 0.0, 1.0,
    0.5, -0.5, 0.5, 0.0, 0.0,
    0.5, 0.5, 0.5, 1.0, 0.0,

    -0.5, -0.5, -0.5, 0.0, 1.0,
    0.5, -0.5, -0.5, 1.0, 1.0,
    0.5, -0.5, 0.5, 1.0, 0.0,
    0.5, -0.5, 0.5, 1.0, 0.0,
    -0.5, -0.5, 0.5, 0.0, 0.0,
    -0.5, -0.5, -0.5, 0.0, 1.0,

    -0.5, 0.5, -0.5, 0.0, 1.0,
    0.5, 0.5, -0.5, 1.0, 1.0,
    0.5, 0.5, 0.5, 1.0, 0.0,
    0.5, 0.5, 0.5, 1.0, 0.0,
    -0.5, 0.5, 0.5, 0.0, 0.0,
    -0.5, 0.5, -0.5, 0.0, 1.0
])
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, 8 * vertices.size, vertices, GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_DOUBLE, GL_FALSE, int(8 * 5), None)
glEnableVertexArrayAttrib(VAO, 0)
glVertexAttribPointer(1, 2, GL_DOUBLE, GL_FALSE, int(8 * 5), ctypes.c_void_p(8 * 3))
glEnableVertexAttribArray(1)

image = open('./textures/container.jpg')
texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture)
# 为当前绑定的纹理对象设置环绕、过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.size[0], image.size[1], 0, GL_RGB, GL_UNSIGNED_BYTE, image.tobytes())
glGenerateMipmap(GL_TEXTURE_2D)

image2 = open('./textures/awesomeface.png')
texture2 = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture2)
# 为当前绑定的纹理对象设置环绕、过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image2.size[0], image2.size[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, image2.tobytes())
glGenerateMipmap(GL_TEXTURE_2D)

shader = shader.Shader("./glsl/test.vs.glsl", "./glsl/test.fs.glsl")

# 配置项
glEnable(GL_DEPTH_TEST)

shader.use()
glUniform1i(glGetUniformLocation(shader.shaderProgram, "texture1"), 0)
glUniform1i(glGetUniformLocation(shader.shaderProgram, "texture2"), 1)

while not glfw.window_should_close(window):
    glClearColor(0.2, 0.3, 0.3, 1.0)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    model = glm.mat4(1.0)
    model = glm.rotate(glm.radians(-55.0)*glfw.get_time(), glm.vec3(1.0, 1.0, 0.0))
    view = glm.mat4(1.0)
    view = glm.translate(glm.vec3(0.0, 0.0, -3.0))
    projection = glm.mat4(1.0)
    projection = glm.perspective(glm.radians(45.0), 800 / 600, 0.1, 100.0)

    shader.use()

    # glUniformMatrix4fv(glGetUniformLocation(shader.shaderProgram, 'model'), 1, GL_FALSE, glm.value_ptr(model))
    glUniformMatrix4fv(glGetUniformLocation(shader.shaderProgram, 'view'), 1, GL_FALSE, glm.value_ptr(view))
    glUniformMatrix4fv(glGetUniformLocation(shader.shaderProgram, 'projection'), 1, GL_FALSE, glm.value_ptr(projection))

    glBindVertexArray(VAO)
    glActiveTexture(GL_TEXTURE0)  # 在绑定纹理之前先激活纹理单元
    glBindTexture(GL_TEXTURE_2D, texture)
    glActiveTexture(GL_TEXTURE1)  # 在绑定纹理之前先激活纹理单元
    glBindTexture(GL_TEXTURE_2D, texture2)

    cubePositions = [
        glm.vec3(0.0, 0.0, 0.0),
        glm.vec3(2.0, 5.0, -15.0),
        glm.vec3(-1.5, -2.2, -2.5),
        glm.vec3(-3.8, -2.0, -12.3),
        glm.vec3(2.4, -0.4, -3.5),
        glm.vec3(-1.7, 3.0, -7.5),
        glm.vec3(1.3, -2.0, -2.5),
        glm.vec3(1.5, 2.0, -2.5),
        glm.vec3(1.5, 0.2, -1.5),
        glm.vec3(-1.3, 1.0, -1.5)
    ]

    for cube in cubePositions:
        model = glm.translate(cube)
        model = glm.rotate(model, glfw.get_time(), glm.vec3(1.0, 0.3, 0.5))
        glUniformMatrix4fv(glGetUniformLocation(shader.shaderProgram, 'model'), 1, GL_FALSE, glm.value_ptr(model))
        glDrawArrays(GL_TRIANGLES, 0, 36)

    glfw.swap_buffers(window)
    glfw.poll_events()

shader.delete()

顶点着色器test.vs.glsl

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;

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

void main()
{
    // 注意乘法要从右向左读
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    TexCoord = aTexCoord;
}

片段着色器test.fs.glsl

#version 330 core
out vec4 FragColor;

in vec2 TexCoord;

uniform sampler2D texture1;
uniform sampler2D texture2;

void main()
{
    FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
}

注释:

8. 参考资料

[1]坐标系统 - LearnOpenGL CN (learnopengl-cn.github.io)

[2]OpenGL学习笔记(七)坐标系统 - 知乎 (zhihu.com)

[3]g-truc/glm: OpenGL Mathematics (GLM) (github.com)

[4]基于C++的OpenGL 05 之坐标系统 - 当时明月在曾照彩云归 - 博客园 (cnblogs.com)

[5]LearnOpenGL-Python/coordinate_systems.py at master · Zuzu-Typ/LearnOpenGL-Python (github.com)

posted @ 2022-08-10 10:00  当时明月在曾照彩云归  阅读(305)  评论(0编辑  收藏  举报