opengl 写一个3D立方体——计算机图形学编程 第4章 管理3D图形数据 笔记

计算机图形学编程(使用OpenGL和C++) 第4章 管理3D图形数据 笔记

数据处理

opengl数据处理过程

想要绘制一个对象,它的顶点数据需要发送给顶点着色器。通常会把顶点数据在C++端放入
一个缓冲区,并把这个缓冲区和着色器中声明的顶点属性相关联。

  1. 初始化立方体顶点数据:声明vertexPositions数组
    我们需要用三角形来构建这个立方体,因此立方体的每一个面都需要由两个三角形构成,总共6×2=12个三角形(见图4.4)。由于每个三角形都由 3 个顶点指定,因此总共有36 个顶点。由于每个顶点具有 3 个值,因此数组中总共有
    36×3=108 个值。
float vertexPositions[108] = {-1.0f,  1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f,1.0f, -1.0f, -1.0f, 1.0f,  1.0f, -1.0f, -1.0f,  1.0f, -1.0f,1.0f, -1.0f, -1.0f, 1.0f, -1.0f,  1.0f, 1.0f,  1.0f, -1.0f,1.0f, -1.0f,  1.0f, 1.0f,  1.0f,  1.0f, 1.0f,  1.0f, -1.0f,1.0f, -1.0f,  1.0f, -1.0f, -1.0f,  1.0f, 1.0f,  1.0f,  1.0f,-1.0f, -1.0f,  1.0f, -1.0f,  1.0f,  1.0f, 1.0f,  1.0f,  1.0f,-1.0f, -1.0f,  1.0f, -1.0f, -1.0f, -1.0f, -1.0f,  1.0f,  1.0f,-1.0f, -1.0f, -1.0f, -1.0f,  1.0f, -1.0f, -1.0f,  1.0f,  1.0f,-1.0f, -1.0f,  1.0f,  1.0f, -1.0f,  1.0f,  1.0f, -1.0f, -1.0f,1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,  1.0f,-1.0f,  1.0f, -1.0f, 1.0f,  1.0f, -1.0f, 1.0f,  1.0f,  1.0f,1.0f,  1.0f,  1.0f, -1.0f,  1.0f,  1.0f, -1.0f,  1.0f, -1.0f};
  1. 加载方体顶点到VBO中:
    在OpenGL中,缓冲区被包含在顶点缓冲对象(Vertex Buffer Object,VBO)中,VBO在C++/OpenGL 应用程序中被声明和实例化。一个场景可能需要很多VBO,所以我们常常会在init()中生成并填充若干个VBO,以备程序需要时直接使用。

    OpenGL 中另一个作顶点数组对象(Vertex Array Object,VAO)。OpenGL要求至少创建一个VAO

#define numVAOs 1
#define numVBOs 1GLuint vao[numVAOs];
GLuint vbo[numVBOs];glGenVertexArrays(1, vao);//创建 VAO 并返回它们的整数型ID,存进数组vao 
glBindVertexArray(vao[0]);//将指定的VAO 标记为“活跃”,这样生成的缓冲区①就会和这个VAO相关联。glGenBuffers(numVBOs, vbo);//创建 VBO并返回它们的整数型ID,存进数组vbo
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);//第0个将缓冲区标记为“活跃”;
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);//将包含顶点数据的数组复制进活跃缓冲区
  1. 写顶点着色器
#version 430layout (location=0) in vec3 position;

每个缓冲区需要有在顶点着色器中声明的相应顶点属性变量。顶点属性通常是着色器中首先声明的变量。

  • 关键字in意思是“输入”(input),表示这个顶点属性将会从缓冲区中接收数值。
  • vec3的意思是着色器的每次调用会抓到3个浮点类型数值(分别表示x轴、y轴、z轴坐标,它们组成一个顶点数据)。
  • 变量的名字是position。
  • layout (location=0)称为“layout 修饰符”,也就是我们把顶点属性和特定缓冲区关联起来的方法。这个顶点属性的识别号是0。
  1. 将缓冲区中的值发送到着色器中的顶点属性
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);// 标记第 0 个缓冲区为“活跃” glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); 将第 0 个属性关联到缓冲区glEnableVertexAttribArray(0); // 启用第 0 个顶点属性 

在这里插入图片描述
当glDrawArrays()执行时,缓冲区中的数据开始流动,从缓冲区的开头开始,按顺序流过顶点着色器。像第 2 章中介绍的一样,顶点着色器对每个顶点执行一次。

MVP Matrix

MVP矩阵顾名思义,它是由M,V,P三个矩阵组够成的矩阵,其中M为模型矩阵,V为视图矩阵,P为投影矩阵。M矩阵用来将物体顶点在模型空间下的坐标转换为在世界空间下的坐标,V矩阵用来将物体顶点在世界空间下的坐标转换为视图空间下的坐标。P矩阵用来将物体顶点在视图空间的坐标转换为裁剪空间的坐标。M矩阵包括平移矩阵、旋转矩阵、缩放矩阵。V矩阵包括旋转矩阵,平移矩阵。P矩阵则包括正交投影矩阵和透视投影矩阵。

世界空间

以世界原点为原点建立的坐标系。

模型空间

在这里插入图片描述
模型空间是以模型某一点为原点建立坐标系而形成的空间
如图左边球,以球心为原点建立坐标系

由于世界空间和模型空间有两套坐标系,我们需要进行坐标系变换将物体放在世界坐标系下面:
模型矩阵描述的是 3D Point 的仿射变换,包含 Scale(缩放)、Rotate(旋转)、Translate(平移)。
可以按照下面的方式表示:
M = S c a l e × R o t a t e × T r a n s l a t e M = Scale \times Rotate \times Translate M=Scale×Rotate×Translate
在这里插入图片描述

最后进行 Translation 是为了保证前面的操作参考坐标轴不会变化。
OpenGL 是左乘的,因此编程时计算模型矩阵需要按照 Translate、Rotate、Scale 的顺序进行。

写的具体一点如下: [ x ^ y ^ z ^ 0 ] = [ a b c t x d e f t y g h i t z 0 0 0 1 ] × [ x y z 1 ] \begin{bmatrix} \hat{x}\\ \hat{y}\\ \hat{z}\\ 0\\ \end{bmatrix}= \begin{bmatrix} a & b & c & t_x\\ d & e & f & t_y\\ g & h & i & t_z\\ 0 & 0 & 0 & 1 \end{bmatrix}\times \begin{bmatrix} x \\ y \\ z \\ 1 \\ \end{bmatrix} x^y^z^0 = adg0beh0cfi0txtytz1 × xyz1

视觉空间和合成相机

将相机转换到坐标系原点,且up为y轴正方向,look-at为z轴负方向。该转换会应用于整个场景,不只是相机,即转换前后,相机在场景中的相对位置不变

观察矩阵用于将模型投影到摄像机(Camera)上。一般而言,定义观察矩阵(或者说摄像机状态)需要下面的一些参数:
在这里插入图片描述

Position:摄像机位置 P = [ x p , y p , z p ] T P = [x_p,y_p,z_p]^T P=[xp,yp,zp]T

Up:摄像机上方 U = [ x u , y u , z u ] T U = [x_u,y_u,z_u]^T U=[xu,yu,zu]T
LookAt:摄像机观察方向 L = [ x l , y l , z l ] T L = [x_l,y_l,z_l]^T L=[xl,yl,zl]T
Right:摄像机右方 R = L × U = [ x r , y r , z r ] T R = L \times U = [x_r, y_r, z_r]^T R=L×U=[xr,yr,zr]T

  1. Position:点坐标 e → = [ x e , y e , z e ] T \overrightarrow{e}= [x_e,y_e,z_e]^T e =[xe,ye,ze]T 表示相机的位置(eye position)
  2. gaze at : 相机看向的方向 g → = [ x g , y g , z g ] T \overrightarrow{g}= [x_g,y_g,z_g]^T g =[xg,yg,zg]T
  3. 表示相机向上的朝向(top): t → = [ x t , y t , z t ] T \overrightarrow{t}= [x_t,y_t,z_t]^T t =[xt,yt,zt]T

为了推导出实际的 View 矩阵(记为 V V V),假设初始状态如下的 View 矩阵(记为 V 0 V_0 V0)的参数如下:

P = [ 0 , 0 , 0 ] T P = [0,0,0]^T P=[0,0,0]T(原点)
U = [ 0 , 1 , 0 ] T U = [0,1,0]^T U=[0,1,0]T(正 Y 轴)
L = [ 0 , 0 , − 1 ] T L = [0,0,-1]^T L=[0,0,1]T(负 Z 轴)
R = [ 1 , 0 , 0 ] R = [1,0,0] R=[1,0,0](正 Z 轴)

注意,对于观察者而言,我们要感受到物体进行了平移旋转之类的操作,需要对 View 矩阵(摄像机)进行相反的操作。要得到实际 View 矩阵,需要进行逆变换 V → V 0 V \rightarrow V_0 VV0,其操作具体如下:

Translate

原点转换: e → = [ x e , y e , z e ] T → [ 0 , 0 , 0 ] T \overrightarrow{e}= [x_e,y_e,z_e]^T \rightarrow [0,0,0]^T e =[xe,ye,ze]T[0,0,0]T
易得
V T = [ 1 0 0 − x e 0 1 0 − y e 0 0 1 − z e 0 0 0 1 ] V_T= \begin{bmatrix} 1 & 0 & 0 & -x_e\\ 0 & 1 & 0 & -y_e\\ 0 & 0 & 1 & -z_e\\ 0 & 0 & 0 & 1 \end{bmatrix} VT= 100001000010xeyeze1

Rotate:

Y 轴方向: [ x t , y t , z t ] T → [ 0 , 1 , 0 ] T [x_t,y_t,z_t]^T \rightarrow [0,1,0]^T [xt,yt,zt]T[0,1,0]T,有 R × [ x t , y t , z t ] T = [ 0 , 1 , 0 ] T R\times [x_t,y_t,z_t]^T = [0,1,0]^T R×[xt,yt,zt]T=[0,1,0]T
Z 轴方向: [ x g , y g , z g ] T → [ 0 , 0 , − 1 ] T [x_g,y_g,z_g]^T \rightarrow [0,0,-1]^T [xg,yg,zg]T[0,0,1]T,有 R × [ x g , y g , z g ] T = [ 0 , 0 , − 1 ] T R\times [x_g,y_g,z_g]^T = [0,0,-1]^T R×[xg,yg,zg]T=[0,0,1]T
X 轴方向: [ x g , y g , z g ] T × [ x t , y t , z t ] ] T = [ x r , y r , z r ] T → [ 1 , 0 , 0 ] T [x_g,y_g,z_g]^T \times [x_t,y_t,z_t]]^T = [x_r, y_r, z_r]^T \rightarrow [1,0,0]^T [xg,yg,zg]T×[xt,yt,zt]]T=[xr,yr,zr]T[1,0,0]T

对于 Rotate,不方便直接计算,考虑逆向情况, 即世界坐标旋转到相机坐标。由于是正交变换所以有:
R − 1 × [ 0 , 1 , 0 ] T = [ x t , y t , z t ] T R^{-1}\times [0,1,0]^T = [x_t,y_t,z_t]^T R1×[0,1,0]T=[xt,yt,zt]T

R − 1 × [ 0 , 0 , − 1 ] T = [ x g , y g , z g ] T R^{-1}\times [0,0,-1]^T = [x_g,y_g,z_g]^T R1×[0,0,1]T=[xg,yg,zg]T
因此有
在这里插入图片描述

参考文献

https://blog.csdn.net/ZDEWBYE/article/details/129481440

https://fuji-w.github.io/theory/CG/basic/matrix/

https://games-cn.org/intro-graphics/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/50670.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

力扣 二分查找

二分查找基础篇。 题目 class Solution {public int searchInsert(int[] nums, int target) {int l 0, r nums.length - 1;while(l < r) {int mid l((r-l)>>1);//(lr)/2if(nums[mid]<target)lmid1;else rmid-1;}return l;//处理边界&#xff0c;设定数组的左半…

21 Python常用内置函数——zip()

zip() 函数用来把多个可迭代对象中的元素压缩到一起&#xff0c;返回一个可迭代的 zip 对象&#xff0c;其中每个元素都是包含原来的多个可迭代对象对应位置上元素的元组&#xff0c;最终结果中包含的元素个数取决于所有参数序列或可迭代对象中最短的那个。 可以这样理解这个函…

Redis 持久化详解

AOF 持久化 AOF持久化数据恢复相对RDB慢&#xff0c;文件也更大&#xff0c;但数据丢失的风险更小。 AOF 写入 将数据写入Redis内存后&#xff0c;将写数据的命令记录到AOP磁盘文件。 【结构】server.aof_buf 主线程写操作执行完之后&#xff0c;命令会先追加到 Redis 的 se…

启智集装箱箱号自动识别API,简单易挂接

启智集装箱箱号自动识别技术特点&#xff1a; 1、集装箱箱号自动识别速度快&#xff0c;毫秒级识别速度&#xff1b; 2、识别率高&#xff1a;综合识别率达到99%以上&#xff1b; 3、集装箱箱号自动识别种类多&#xff1a;能够识别GB/T1836-1997标准的集装箱号码&#xff0c;可…

论文阅读——Design of Environmental backscatter tag antenna for 5G Internet of things

文章目录 摘要一、背景二、系统模型三、天线设计A. 指标B. 天线结构描述C. 天线结构优化D. 天线结构确定 四、仿真结果总结 论文来源&#xff1a;https://ieeexplore.ieee.org/document/9379395 摘要 文章针对传统设备识别在电力物联网场景中存在的可靠性低和读取距离不足的问…

Java智慧养老养老护理帮忙代办陪诊陪护小程序系统源码

&#x1f31f;智慧养老新风尚&#xff0c;护理代办陪诊小程序来帮忙✨ &#x1f3e1;【开篇&#xff1a;关爱老人&#xff0c;从智慧养老开始】&#x1f3e1; 随着社会的进步&#xff0c;智慧养老已成为新时代孝心的体现。面对忙碌的生活节奏&#xff0c;如何更好地照顾家中长…

postMessage 收到消息类型 “webpackWarnings“

场景描述&#xff1a; 当A系统中的parent页面使用iframe内嵌C系统的child页面&#xff0c;并且在parent页面中通过postMessageg给child页面发送消息时&#xff0c;如果C系统中使用了webpack,则webpack也会给child页面发送消息 &#xff0c;消息类型为webpackWarnings。那么如何…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十九章 等待队列

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

右手系转Unity左手系坐标系

右手系转Unity左手系坐标系 inline Sophus::SE3d TransformRightPoseToLeftUnity(const Sophus::SE3d &pose) {Eigen::Matrix4d T_wl_wr, T_br_bl;T_wl_wr.setZero();T_wl_wr(0, 0) 1.0;T_wl_wr(1, 2) 1.0;T_wl_wr(2, 1) 1.0;T_wl_wr(3, 3) 1.0;T_br_bl.setZero();T_b…

电力系统 | 发电、输电、变电、配电、用电介绍 | 一度电从电厂发出来到用户终端需要经历哪些环节 | 变电站建在哪里

文章目录 一、一度电从电厂发出来到用户终端需要经历哪些环节&#xff1f;二、发电、变电、输电、配售电和用电过程介绍三、变电站建在哪里&#xff1f; 一、一度电从电厂发出来到用户终端需要经历哪些环节&#xff1f; 电力系统是由发电、变电、输电、配售电和用电等环节组成的…

《路过人间》好好听,歌词也戳人

嘿 意不意外 她背影 那么轻快 嘿 要明白 人会来 就会离开 世上唯一不变 是人都善变 路过人间爱都有期限 天可怜见 心碎在所难免 以为痛过几回 多了些修炼 路过人间就懂得防卫 说来惭愧人只要有机会 就又沦陷 嘿 别再猜 她可曾 想过回来 嘿 醒過來 你很好 她也不壞 快快抹干…

阿里云服务器 篇六:GitHub镜像网站

文章目录 系列文章搭建镜像网站的2种方式使用 Web 抓取工具 (Spider 技术)使用 Web 代理服务器使用 nginx 搭建GitHub镜像网站基础环境搭建添加对 github.com 的转发配置添加对 raw.githubusercontent.com 的转发配置配置更改注意事项(可选)缓存优化为新增设的二级域名配置DN…

leetcode106. 从中序与后序遍历序列构造二叉树,力扣105姊妹题

leetcode106. 从中序与后序遍历序列构造二叉树 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入&#xff1a;inorder [9,3,15,20,7…

活动报名小程序

#活动报名工具# # 活动报名小程序 ## 项目简介 一款通用的活动报名工具&#xff0c;包含活动展示&#xff0c;微信支付&#xff0c;订单管理&#xff0c;分享评价等功能。 品客聚精彩&#xff0c;有你才精彩&#xff01;不只有线下活动还可以进行线上裂变活动。 …

UE4-构建光照后导入的静态网格体变黑

当我们将我们的静态网格体导入到项目当中的时候&#xff0c;此时我们进行重新构建光照&#xff0c;我们在从新构建完光照后&#xff0c;会发现我们的静态网格体全部变黑了&#xff0c;此时是因为没有设置光照贴图分辨率和坐标索引引起的。 将General Settings中的L…

Cmake生成的Xcode工程相对路径与绝对路径的问题

Cmake生成的Xcode工程相对路径与绝对路径的问题 文章目录 Cmake生成的Xcode工程相对路径与绝对路径的问题前言修改.pbxproj文件验证工程小结 前言 由于Cmake的跨平台的自动化构建的方便性以及他广泛应用于编译过程的管理&#xff0c;在开发过程中难免用到Cmake。我也使用Cmake…

framework直播学习笔记--安卓如何实现Launcher启动应用全部变自由窗口Freeform模式

背景&#xff1a; 前些天在学员在学员群里有聊到一个需求&#xff0c;那就是把手机桌面点击应用图标后&#xff0c;不是进行全屏显示&#xff0c;而是都进行自由窗口显示。这个其实有点类似我们windows电脑打开app&#xff0c;每个app都是一个非全屏的窗口&#xff0c;而且可以…

从 Batch Norm 到 SGD 隐藏的内容

我们仍然不了解机器学习的哪些方面 欢迎来到雲闪世界。令人惊讶的是&#xff0c;机器学习中的一些基本主题仍然不为研究人员所知&#xff0c;尽管它们很基础且常用&#xff0c;但却似乎很神秘。机器学习的有趣之处在于我们构建了可以工作的东西&#xff0c;然后弄清楚它们为什么…

Vue中 watch 与 watchEffect 的区别

Watch 默认是懒侦听&#xff0c;即仅在侦听源发生变化时才执行回调函数。只追踪明确定义的数据源&#xff0c;不会追踪在回调中访问到的东西可以访问侦听数据的新值和旧值。总共接收三个参数&#xff0c;侦听数据源、回调函数和配置选项。 const x ref(1) const y ref(1) c…

硅纪元视角 | 摩根大通拥抱AI:LLM Suite开启金融行业新篇章!

在数字化浪潮的推动下&#xff0c;人工智能&#xff08;AI&#xff09;正成为塑造未来的关键力量。硅纪元视角栏目紧跟AI科技的最新发展&#xff0c;捕捉行业动态&#xff1b;提供深入的新闻解读&#xff0c;助您洞悉技术背后的逻辑&#xff1b;汇聚行业专家的见解&#xff0c;…