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;最终结果中包含的元素个数取决于所有参数序列或可迭代对象中最短的那个。 可以这样理解这个函…

论文阅读——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;如何更好地照顾家中长…

【北京迅为】《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…

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

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

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;然后弄清楚它们为什么…

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

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

华为OD机试 - 计算三叉搜索树的高度 (python 2024年C卷D卷)

华为OD机试&#xff08;C卷D卷&#xff09;2024真题目录(Java & c & python) 题目描述 定义构造三叉搜索树规则如下&#xff1a; 每个节点都存有一个数&#xff0c;当插入一个新的数时&#xff0c;从根节点向下寻找&#xff0c;直到找到一个合适的空节点插入。查找的…

【字母异位词分组】python刷题记录

R2-字符串篇 class Solution:def groupAnagrams(self, strs: List[str]) -> List[List[str]]:#哈希表分组#把每种字母出现次数相同的字符串分到同一组#sort一下好像能分dictdefaultdict(list)for s in strs:asorted(s)#sorted(s)相同的字符串分到同一组dict[.join(a)].appen…

Java 基础 and 进阶面试知识点(超详细)

一个 Java 文件中是否可以存在多个类&#xff08;修饰类除外&#xff09;&#xff1f; 一个 Java 文件中是可以存在多个类的&#xff0c;但是一个 Java 文件中只能存在一个 public 所修饰的类&#xff0c;而且这个 Java 文件的文件名还必须和 public 所修饰类的类名保持一致&a…

如何应对零日威胁:漏洞扫描

零日威胁正变得比以往任何时候都更加危险。5月底至6月初&#xff0c;恶意行为者通过零日攻击接管了众多名人和品牌的TikTok账户。用户声称在打开一条私信后便丧失了账户的控制权。而用于攻击的恶意软件能够在用户不下载或安装任何程序的情况下感染设备。 目前尚不清楚此次事件的…

linux系统巡检及shell脚本

目录 步骤1 系统巡检基本命令 CPU 内存 磁盘 进程 网络、流量 步骤2 shell脚本编写 awk awk常用内置变量 awk正则 实例一 实例二 实例三 实例四 实例五 sed 选项与参数 例一 例二 例三 例四 例五 例六 例七 grep 主要参数 常用用法 例一 例二 例三…

vue 开发环境配置

1. nvm 安装 在 github上下载 最新的 nvm 包 https://github.com/coreybutler/nvm-windows/releases或者在 csdn 上下载&#xff08;从github上迁移&#xff0c;方便下载&#xff09;https://download.csdn.net/download/u011171506/89585197 下载后不用修改任何配置&#x…

2024年【广东省安全员B证第四批(项目负责人)】考试报名及广东省安全员B证第四批(项目负责人)模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 广东省安全员B证第四批&#xff08;项目负责人&#xff09;考试报名根据新广东省安全员B证第四批&#xff08;项目负责人&#xff09;考试大纲要求&#xff0c;安全生产模拟考试一点通将广东省安全员B证第四批&#x…