1. 引言

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

前置知识可参考:

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

2. 概述

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

有图可知:

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

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

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

  • 正交投影:平行视角

  • 透视投影:近大远小

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)

基于Python的OpenGL 05 之坐标系统的更多相关文章

  1. 【Machine Learning】决策树案例:基于python的商品购买能力预测系统

    决策树在商品购买能力预测案例中的算法实现 作者:白宁超 2016年12月24日22:05:42 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本 ...

  2. 如何简单实现接口自动化测试(基于 python) 原博主地址https://blog.csdn.net/gitchat/article/details/77849725

    如何简单实现接口自动化测试(基于 python) 2017年09月05日 11:52:25 阅读数:9904 GitChat 作者:饿了么技术社区 原文:如何简单实现接口自动化测试(基于 python ...

  3. Python 基于python+mysql浅谈redis缓存设计与数据库关联数据处理

    基于python+mysql浅谈redis缓存设计与数据库关联数据处理 by:授客  QQ:1033553122 测试环境 redis-3.0.7 CentOS 6.5-x86_64 python 3 ...

  4. (数据科学学习手札47)基于Python的网络数据采集实战(2)

    一.简介 马上大四了,最近在暑期实习,在数据挖掘的主业之外,也帮助同事做了很多网络数据采集的内容,接下来的数篇文章就将一一罗列出来,来续写几个月前开的这个网络数据采集实战的坑. 二.马蜂窝评论数据采集 ...

  5. 看我如何基于Python&Facepp打造智能监控系统

    由于种种原因,最近想亲自做一个基于python&facepp打造的智能监控系统. 0×00:萌芽 1:暑假在家很无聊 想出去玩,找不到人.玩个lol(已卸载),老是坑人.实在是无聊至极,不过, ...

  6. 从零学习基于Python的RobotFramework自动化

    从零学习基于Python的RobotFramework自动化 一.        Python基础 1)      版本差异 版本 编码 语法 其他 2.X ASCII try: raise Type ...

  7. 从0开始学正则表达式-基于python

    关于正则表达式,当我们了解它就不难,不了解就很难,其实任何事情都是这样,没有人一生下来就啥都会,说白了,每个人都是一个学习了解进步的过程.学习和掌握正则表达式可能并不是太简单,因为它确实是有点像“外星 ...

  8. python网络编程05 /TCP阻塞机制

    python网络编程05 /TCP阻塞机制 目录 python网络编程05 /TCP阻塞机制 1.什么是拥塞控制 2.拥塞控制要考虑的因素 3.拥塞控制的方法: 1.慢开始和拥塞避免 2.快重传和快恢 ...

  9. 基于python的pixiv爬虫

    基于python的pixiv爬虫 1.目标 在和朋友吹逼过程中,聊到qq群机器人,突发奇想动手做一个p站每日推荐色图的色图机,遂学习爬虫. 目标: 批量下载首页推荐色图. 由于对qq机器人不熟,先利用 ...

  10. 基于Python+Django的Kubernetes集群管理平台

    ➠更多技术干货请戳:听云博客 时至今日,接触kubernetes也有一段时间了,而我们的大部分业务也已经稳定地运行在不同规模的kubernetes集群上,不得不说,无论是从应用部署.迭代,还是从资源调 ...

随机推荐

  1. 【Shell脚本案例】案例6:查看网卡实时流量

    一.背景 监控,对服务器查看实时流量 了解服务器的数据传输量 二.说明 1.获取网络流量 ifconfig查看网卡就能看到数据包传输情况 2.可以使用工具查看 iftop cat /proc/net/ ...

  2. 【每日一题】【栈和队列、双端队列】20. 有效的括号/NC52 有效括号序列-211127/220126

    给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效. 有效字符串需满足: 左括号必须用相同类型的右括号闭合.左括号必须以正确的顺序闭合. 来源:力扣(L ...

  3. hdoj笔记1000

    1000,A+B problem #include <stdio.h>//要用EOF!! int main() { int a,b; while(scanf("%d%d" ...

  4. 1+x初级Web的关键词填写

    H5+CSS: 声明HTML网页标准:<!DOCTYPE> 图片标签 img css颜色样式color 定位 position 绝对absolute 相对 relative 外边距:mar ...

  5. 【架构设计】保持简单轻量设计的三个原则——DRY,KISS, YAGNI

    前言 一个软件轻量简单的软件架构是非常重要的,它可以让我们花最小的代价就能满足业务上的需求.那如何保证轻量简单呢?那今天就和大家分享下这其中的秘密,也就是3个重要的指导原则,KISS原则,YAGNI原 ...

  6. 什么是RPC? (全面了解)

    一:RPC 1.什么是RPC? RPC 是指远程过程调用,也就是说两台服务器,A 和 B,一个应用部署在A 服务器上,想要调用B 服务器上应用提供的函数或方法,由于不在一个内存空间,不能直接调用,需要 ...

  7. C#开发的磁吸屏幕类库 - 开源研究系列文章

    上次写了一个关于线程池的博文,里面讲到了关于磁吸屏幕的类库,今天就把这个类库进行下讲解. 一.      类库目录: 类库的目录见下图,主要定义了Win32的一些API,以及一些API使用到的常量和结 ...

  8. 《Effective C++》模版与泛型编程

    Item41:了解隐式接口和编译期多态. 纵使你从未使用过templates,应该不陌生"运行期多态"和"编译期多态"之间的差异.因为它类似于"哪一个 ...

  9. Linux c 程序自动启动自己

    在程序自动升级的时候需要自己重新启动自己 #include <stdio.h> #include <stdlib.h> #include <unistd.h> in ...

  10. 洛谷P8567 真·基础数论问题

    基础数论重定向 今天蒟蒻切水题切到一道建议评黄的红题,一下子给我整不会了-- 题目传送门 理解题意 首先,我们要理解题意. [JRKSJ R6] Nothing 我们定义 \(f(x)\) 表示 \( ...