基于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自动 ...
随机推荐
- 【每日一题】【归并排序/堆排序&虚拟头结点】148. 排序链表-211220/220217【出栈时不断容易产生环状链表!】
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 . 进阶: 你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗? 方法1:归并排序+使用辅助函数 ...
- 什么是django中间件?(七个中间件-自定义中间件)
目录 一:django中间件 1.什么是django中间件 2.django请求生命周期流程图 二:django自带七个中间件 1.研究django中间件代码规律 2.django支持程序员自定义中间 ...
- Spark详解(01) - Scala编程语言
Spark详解(01) - Scala编程语言概述 Scala官网:https://www.scala-lang.org/ 什么是Scala 从英文的角度来讲,Scala并不是一个单词,而是Scala ...
- 一文了解华为FusionInsight MRS HBase的集群隔离方案RSGroup
摘要: RSGroup是集群隔离方案. 本文分享自华为云社区<华为FusionInsight MRS HBase的集群隔离--RSGroup>,作者: MissAverage. 一.HBa ...
- [cocos2d-x]从源码角度思考convertToWorldSpace()与convertToWorldSpaceAR()坐标系的转换
convertToWorldSpace() 话不多说,先上源码,之后再慢慢讲解: (5和6图截图的时候重复了,这里就不弄出来了) 只要通过图1到图8中我写的注释进行分析(不懂的地方可以自己去翻一下co ...
- Array.from() ------来自❀ 前端宇宙 ❀公众号。
JavaScript 中有一个这样的函数: Array.from:允许在 JavaScript 集合(如: 数组.类数组对象.或者是字符串.map .set 等可迭代对象) 上进行有用的转换. 1. ...
- Go语言的template
html/template包实现了数据驱动的模板,用于生成可防止代码注入的安全的HTML内容.它提供了和 text/template包相同的接口,Go语言中输出HTML的场景都应使用html/temp ...
- BUG日记---SSM进行多表查询错误-----页面使用<c:foreach>错误
javax.servlet.ServletException: javax.servlet.jsp.JspTagException: Don't know how to iterate over su ...
- Kubernetes(k8s)控制器(一):deployment
目录 一.系统环境 二.前言 三.Kubernetes 控制器 四.Deployment概览 五.创建deployment 六.修改deploy副本数 6.1 kubectl edit deploy ...
- springBoot简单记录日志
记录日志的几种方法 springboot项目内置日志框架 在配置文件中添加以下配置: logging: file: name: "./log/xxx.log" pattern: f ...