基于Python的OpenGL 05 之坐标系统
1. 引言
本文基于Python语言,描述OpenGL的坐标系统
前置知识可参考:
笔者这里不过多描述每个名词、函数和细节,更详细的文档可以参考:
2. 概述
OpenGL中坐标变换的流程如下图:
有图可知:
-
创建一个物体到屏幕绘制需要三个矩阵变换:模型(Model)、观察(View)、投影(Projection)(即,MVP)
-
裁剪坐标:
投影时主要有两者投影方式:
- 正交投影:平行视角
- 透视投影:近大远小
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变换,结果图如下:
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))
如果顺利的话,结果如下:
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)
实现效果:
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)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下