基于Python的OpenGL 06 之摄像机
1. 引言
本文基于Python语言,描述OpenGL的摄像机
前置知识可参考:
笔者这里不过多描述每个名词、函数和细节,更详细的文档可以参考:
2. 概述
OpenGL的坐标变换流程图如下:

有图可知:
- 摄像机的参数(如,位置、视点、方向)决定视图
根据变化的相对性,控制摄像机的参数可以看成物体的变化(如,摄像机后移相当于物体后移)
观察矩阵可由摄像机的位置、视点和方向计算,如下图:

计算公式:
\]
其中R是右向量,U是上向量,D是方向向量,P是摄像机位置向量;
位置向量是相反的,因为我们最终希望把世界平移到与我们自身移动的相反方向
3. 编码
控制摄像机的参数实质就是控制观察矩阵(view)
生成一个观察矩阵需要位置、视点和方向向量,GLM的lookAt()函数可用于生成观察矩阵:
view = glm.lookAt(glm.vec3(0.0, 0.0, -3.0),
glm.vec3(0.0, 0.0, 0.0),
glm.vec3(0.0, 1.0, 0.0))
可选项,让摄像机的位置绕圆转动,会形成物体转动的感觉
radius = 10.0
camX = np.sin(glfw.get_time())*radius
camZ = np.cos(glfw.get_time())*radius
view = glm.lookAt(glm.vec3(camX, 0.0, camZ),
glm.vec3(0.0, 0.0, 0.0),
glm.vec3(0.0, 1.0, 0.0))
运行一下,结果图如下:

4. 完整代码
主要文件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, "camera", 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.rotate(glm.radians(-55.0)*glfw.get_time(), glm.vec3(1.0, 1.0, 0.0))
radius = 10.0
camX = np.sin(glfw.get_time())*radius
camZ = np.cos(glfw.get_time())*radius
view = glm.lookAt(glm.vec3(camX, 0.0, camZ),
glm.vec3(0.0, 0.0, 0.0),
glm.vec3(0.0, 1.0, 0.0))
projection = glm.perspective(glm.radians(45.0), 800 / 600, 0.1, 100.0)
shader.use()
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()
#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);
}
5. 参考资料
[1]摄像机 - LearnOpenGL CN (learnopengl-cn.github.io)
[2]OpenGL学习笔记(八)摄像机 - 知乎 (zhihu.com)
[3]LearnOpenGL-Python/camera_circle.py at master · Zuzu-Typ/LearnOpenGL-Python (github.com)
基于Python的OpenGL 06 之摄像机的更多相关文章
- 基于python的opcode优化和模块按需加载机制研究(学习与个人思路)(原创)
基于python的opcode优化和模块按需加载机制研究(学习与思考) 姓名:XXX 学校信息:XXX 主用编程语言:python3.5 个人技术博客:http://www.cnblogs.com/M ...
- 基于Python的机器学习实战:KNN
1.KNN原理: 存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一个数据与所属分类的对应关系.输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应 ...
- 看我如何基于Python&Facepp打造智能监控系统
由于种种原因,最近想亲自做一个基于python&facepp打造的智能监控系统. 0×00:萌芽 1:暑假在家很无聊 想出去玩,找不到人.玩个lol(已卸载),老是坑人.实在是无聊至极,不过, ...
- Python并发编程06 /阻塞、异步调用/同步调用、异步回调函数、线程queue、事件event、协程
Python并发编程06 /阻塞.异步调用/同步调用.异步回调函数.线程queue.事件event.协程 目录 Python并发编程06 /阻塞.异步调用/同步调用.异步回调函数.线程queue.事件 ...
- 基于Python实现的系统SLA可用性统计
基于Python实现的系统SLA可用性统计 1. 介绍 SLA是Service Level Agreement的英文缩写,也叫服务质量协议.根据SRE Google运维解密一书中的定义: SLA是服务 ...
- 【Machine Learning】决策树案例:基于python的商品购买能力预测系统
决策树在商品购买能力预测案例中的算法实现 作者:白宁超 2016年12月24日22:05:42 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本 ...
- 基于Python+Django的Kubernetes集群管理平台
➠更多技术干货请戳:听云博客 时至今日,接触kubernetes也有一段时间了,而我们的大部分业务也已经稳定地运行在不同规模的kubernetes集群上,不得不说,无论是从应用部署.迭代,还是从资源调 ...
- 关于《selenium2自动测试实战--基于Python语言》
关于本书的类型: 首先在我看来技术书分为两类,一类是“思想”,一类是“操作手册”. 对于思想类的书,一般作者有很多年经验积累,这类书需要细读与品位.高手读了会深有体会,豁然开朗.新手读了不止所云,甚至 ...
- psutil一个基于python的跨平台系统信息跟踪模块
受益于这个模块的帮助,在这里我推荐一手. https://pythonhosted.org/psutil/#processes psutil是一个基于python的跨平台系统信息监视模块.在pytho ...
- 一次完整的自动化登录测试-基于python+selenium进行cnblog的自动化登录测试
Web登录测试是很常见的测试!手动测试大家再熟悉不过了,那如何进行自动化登录测试呢!本文作者就用python+selenium结合unittest单元测试框架来进行一次简单但比较完整的cnblog自动 ...
随机推荐
- 【每日一题】【上右下左模拟&while循环体条件不满足时】54.螺旋矩阵-211110/220204
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素. 解答:while循环内部不满足也会继续走到结尾 import java.util.ArrayList; ...
- vulnhub靶场之DIGITALWORLD.LOCAL: FALL
准备: 攻击机:虚拟机kali.本机win10. 靶机:digitalworld.local: FALL,下载地址:https://download.vulnhub.com/digitalworld/ ...
- Vue中关于数组与对象修改触发页面更新的机制与原理简析
Vue中关于数组与对象修改触发页面更新的机制与原理简析 相关问题 数组 使用索引直接赋值与直接修改数组length时,不会触发页面更新. 例如: <script> export defau ...
- 几种数据库jar包获取方式
摘要:以下提供的都是各个数据库较为官方的jar包获取方式. 本文分享自华为云社区<JDBC连接相关jar包获取及上传管理中心白名单处理>,作者:HuaWei XYe. jar包获取 以下提 ...
- vue使用echarts引入离线地图(geo.json)并切换省市(以四川为例)
https://blog.csdn.net/weixin_43374193/article/details/95594419 https://blog.csdn.net/harrisonz8/arti ...
- 宝塔渗透之msf代理入侵
前言 在渗透中遇到内网主机是一层接一层的拓扑形式,可以采用多层代理加路由转发访问,便于在渗透中出现网段隔绝可以使用此方法跳出局限 实验环境 kali: 192.168.75.131 target-ce ...
- [cocos2d-x]我发现的内存管理机制的一些问题
之前看过的一些文章中关于内存的管理机制讲的非常好,但是我发现它们在谈到每一帧都会创建一个新的内存池的时候,我发现源码并不是这样: PoolManager* PoolManager::getInstan ...
- Matplotlib学习笔记1 - 上手制作一些图表吧!
Matplotlib学习笔记1 - 上手制作一些图表吧! Matplotlib是一个面向Python的,专注于数据可视化的模块. 快速上手 这是使用频率最高的几个模块,在接下来的程序中,都需要把它们作 ...
- angular打包部署设置publicPath文件目录及访问地址,解决打包完成后,运行打包文件,报错404,js,css未找到
方案1.改变文件目录 不改变访问地址:XXXPRO 是部署的服务器上面的 文件夹名称 ng build --prod --base-href /XXXPRO 方案2.改变访问地址录同时改变文件目录-- ...
- C# 托管堆 遭破坏 问题溯源分析
一:背景 1. 讲故事 年前遇到了好几例托管堆被损坏的案例,有些运气好一些,从被破坏的托管堆内存现场能观测出大概是什么问题,但更多的情况下是无法做出准确判断的,原因就在于生成的dump是第二现场,借用 ...