1. 引言

本文基于Python语言,描述OpenGL的摄像机

前置知识可参考:

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

2. 概述

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

有图可知:

  • 摄像机的参数(如,位置、视点、方向)决定视图

根据变化的相对性,控制摄像机的参数可以看成物体的变化(如,摄像机后移相当于物体后移)

观察矩阵可由摄像机的位置、视点和方向计算,如下图:

计算公式:

\[LookAt = \begin{bmatrix} \color{red}{R_x} & \color{red}{R_y} & \color{red}{R_z} & 0 \\ \color{green}{U_x} & \color{green}{U_y} & \color{green}{U_z} & 0 \\ \color{blue}{D_x} & \color{blue}{D_y} & \color{blue}{D_z} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} * \begin{bmatrix} 1 & 0 & 0 & -\color{purple}{P_x} \\ 0 & 1 & 0 & -\color{purple}{P_y} \\ 0 & 0 & 1 & -\color{purple}{P_z} \\ 0 & 0 & 0 & 1 \end{bmatrix}
\]

其中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 之摄像机的更多相关文章

  1. 基于python的opcode优化和模块按需加载机制研究(学习与个人思路)(原创)

    基于python的opcode优化和模块按需加载机制研究(学习与思考) 姓名:XXX 学校信息:XXX 主用编程语言:python3.5 个人技术博客:http://www.cnblogs.com/M ...

  2. 基于Python的机器学习实战:KNN

    1.KNN原理: 存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一个数据与所属分类的对应关系.输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应 ...

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

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

  4. Python并发编程06 /阻塞、异步调用/同步调用、异步回调函数、线程queue、事件event、协程

    Python并发编程06 /阻塞.异步调用/同步调用.异步回调函数.线程queue.事件event.协程 目录 Python并发编程06 /阻塞.异步调用/同步调用.异步回调函数.线程queue.事件 ...

  5. 基于Python实现的系统SLA可用性统计

    基于Python实现的系统SLA可用性统计 1. 介绍 SLA是Service Level Agreement的英文缩写,也叫服务质量协议.根据SRE Google运维解密一书中的定义: SLA是服务 ...

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

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

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

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

  8. 关于《selenium2自动测试实战--基于Python语言》

    关于本书的类型: 首先在我看来技术书分为两类,一类是“思想”,一类是“操作手册”. 对于思想类的书,一般作者有很多年经验积累,这类书需要细读与品位.高手读了会深有体会,豁然开朗.新手读了不止所云,甚至 ...

  9. psutil一个基于python的跨平台系统信息跟踪模块

    受益于这个模块的帮助,在这里我推荐一手. https://pythonhosted.org/psutil/#processes psutil是一个基于python的跨平台系统信息监视模块.在pytho ...

  10. 一次完整的自动化登录测试-基于python+selenium进行cnblog的自动化登录测试

    Web登录测试是很常见的测试!手动测试大家再熟悉不过了,那如何进行自动化登录测试呢!本文作者就用python+selenium结合unittest单元测试框架来进行一次简单但比较完整的cnblog自动 ...

随机推荐

  1. (四) 一文搞懂 JMM - 内存模型

    4.JMM - 内存模型 1.JMM内存模型 JMM与happen-before 1.可见性问题产生原因 下图为x86架构下CPU缓存的布局,即在一个CPU 4核下,L1.L2.L3三级缓存与主内存的 ...

  2. docker部署项目

    @ 目录 前言 一.下载安装docker: 1.前提工作 1.1 查看linux版本 1.2 yum包更新到最新 1.3 安装工具包 1.4 设置yum源并更新yum包索引 2.安装docker 2. ...

  3. 全网最全的linux上docker安装oracle的详细文档,遇到了n个问题,查了几十篇文章,最终汇总版,再有解决不了的,私聊我,我帮你解决

    目录 全网最全的linux上docker安装oracle的详细文档,遇到了n个问题,查了几十篇文章,最终汇总版,再有解决不了的,私聊我,我帮你解决 1. 拉取阿里镜像oracle 2. 创建初始化数据 ...

  4. 【Java难点攻克】「NIO和内存映射性能提升系列」彻底透析NIO底层的内存映射机制原理与Direct Memory的关系

    NIO与内存映射文件 Java类库中的NIO包相对于IO包来说有一个新功能就是 [内存映射文件],在业务层面的日常开发过程中并不是经常会使用,但是一旦在处理大文件时是比较理想的提高效率的手段,之前已经 ...

  5. 03.Javascript学习笔记2

    1.逻辑运算符 在javascript中与或非对应的逻辑运算符是: && || ! const a = true; const b = false; console.log(a &am ...

  6. 使用 System.Text.Json 时,如何处理 Dictionary 中 Key 为自定义类型的问题

    在使用 System.Text.Json 进行 JSON 序列化和反序列化操作时,我们会遇到一个问题:如何处理字典中的 Key 为自定义类型的问题. 背景说明 例如,我们有如下代码:   // 定义一 ...

  7. [常用工具] 基于psutil和GPUtil获取系统状态信息

    本文主要介绍在Python3中利用psutil库获取系统状态,利用GPUtil获取gpu状态. psutil (process and system utilities)(进程和系统实用程序)是一个跨 ...

  8. [OpenCV实战]40 计算机视觉工具对比

    文章目录 1 简介 2 适用于计算机视觉的MATLAB 2.1 为什么要使用MATLAB进行计算机视觉:优点 2.2 为什么不应该将MATLAB用于计算机视觉:缺点 3 适用于计算机视觉的OpenCV ...

  9. [R语言] ggplot2入门笔记2—通用教程ggplot2简介

    文章目录 通用教程简介(Introduction To ggplot2) 2 ggplot2入门笔记2-通用教程ggplot2简介 1. 了解ggplot语法(Understanding the gg ...

  10. 聊聊MongoDB中连接池、索引、事务

    大家好,我是哪吒. 三分钟你将学会: MongoDB连接池的使用方式与常用参数 查询五步走,能活九十九? MongoDB索引与MySQL索引有何异同? MongoDB事务与ACID 什么是聚合框架? ...