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

news2024/11/16 2:16:40

计算机图形学编程(使用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 1

GLuint 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 430

layout (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.coloradmin.cn/o/1946772.html

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

相关文章

Python中高效处理大数据的几种方法

随着数据量的爆炸性增长,如何在Python中高效地处理大数据成为了许多开发者和数据科学家的关注焦点。Python以其简洁的语法和丰富的库支持,在数据处理领域占据了重要地位。本文将介绍几种在Python中高效处理大数据的常用方法。 目录 1. 使用Pandas进行数…

基于STM32的逻辑分析仪

文章目录 一、逻辑分析仪体验1、使用示例1.1 逻辑分析仪1.2 开源软件PulseView 2、核心技术2.1 技术方案2.2 信号采集与存储2.3 数据上传 3、使用逻辑分析仪4、 SourceInsight 使用技巧4.1新建工程4.2 设置工程名及工程数据目录4.3 指定源码目录4.4 添加源码4.5 同步文件4.6 操…

为RTEMS Raspberrypi4 BSP添加SPI支持

为RTEMS Raspberrypi4 BSP添加SPI支持 主要参考了dev/bsps/shared/dev/spi/cadence-spi.c RTEMS 使用了基于linux的SPI框架,SPI总线驱动已经在内核中实现。在这个项目中我需要实习的是 RPI4的SPI主机控制器驱动 SPI在RTEMS中的实现如图: 首先需要将S…

25.x86游戏实战-理解发包流程

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 本次游戏没法给 内容参考于:微尘网络安全 工具下载: 链接:https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

江科大/江协科技 STM32学习笔记P9-11

文章目录 OLED1、OLED硬件main.c EXTI外部中断1、中断系统2、中断执行流程图3、STM32中断4、中断地址的作用5、EXTI6、EXTI基本结构7、AFIO复用IO口8、EXTI框图或门和与门 9、旋转编码器介绍10、硬件电路 OLED 1、OLED硬件 SCL和SDA是I2C的通信引脚,需要接在单片机…

java包装类型缓存简单探究-Integer为例

文章目录 包装类型缓存自动装箱与valueOf感悟结语 包装类型缓存 包装类型缓存是什么 本文以常用的Integer包装类为例做一个探索,感兴趣可以用类似方法查看其他包装类。 我们都知道它会缓存 -128到127之间的整数Integer对象。 结论大伙都知道。那么我们今天就来探究…

【Android】安卓四大组件之广播知识总结

文章目录 动态注册使用BroadcastReceiver监听Intent广播注册Broadcast Receiver 静态注册自定义广播标准广播发送广播定义广播接收器注册广播接收器 有序广播修改发送方法定义第二个广播接收器注册广播接收器广播截断 使用本地广播实践-强制下线使用ActivityCollector管理所有活…

ubuntu那些ppa源在哪

Ubuntu中的 PPA 终极指南 - UBUNTU粉丝之家 什么是PPA PPA 代表个人包存档。 PPA 允许应用程序开发人员和 Linux 用户创建自己的存储库来分发软件。 使用 PPA,您可以轻松获取较新的软件版本或官方 Ubuntu 存储库无法提供的软件。 为什么使用PPA? 正如…

【JavaEE】Spring Boot 自动装配原理(源码分析)

一. 前言 我们在写Spring Boot的程序代码的时候, 可以注入很多我们没有定义过的Bean.例如: Autowired private ApplicationContext applicationContext; Autowired public DataSourceTransactionManager transactionManager; Autowired public AutowireCapableBeanFactory …

软件开发者消除edge浏览器下载时“此应用不安全”的拦截方法

当Microsoft Edge浏览器显示“此应用不安全”或者“已阻止此不安全的下载”这类警告时,通常是因为Windows Defender SmartScreen或者其他安全功能认为下载的文件可能存在安全风险。对于软件开发者来说,大概率是由于软件没有进行数字签名,导致…

Visual Studio 2022新建 cmake 工程测试 tensorRT 自带样例 sampleOnnxMNIST

1. 新建 cmake 工程 vs2022_cmake_sampleOnnxMNIST_test( 如何新建 cmake 工程,请参考博客:Visual Studio 2022新建 cmake 工程测试 opencv helloworld ) 2. 删除默认生成的 vs2022_cmake_sampleOnnxMNIST_test.h 头文件 3. 修改默认生成的 vs2022_cma…

【屏显MCU】多媒体接口总结

本文主要介绍【屏显MCU】的基本概念,用于开发过程中的理解 以下是图层叠加示例 【屏显MCU】多媒体接口总结 0. 个人简介 && 授权须知1. 三大引擎1.1 【显示引擎】Display Engine1.1.1 【UI】 图层的概念1.1.2 【Video】 图层的概念1.1.3 图层的 Blending 的…

一键解锁:科研服务器性能匹配秘籍,选择性能精准匹配科研任务和计算需求的服务器

一键解锁:科研服务器性能匹配秘籍 HPC科研工作站服务器集群细分领域迷途小书童 专注于HPC科研服务器细分领域kyfwq001 🎯在当今科技飞速发展的时代,科研工作对计算资源的需求日益增长😜。选择性能精准匹配科研任务和计算需求的服…

古籍双层PDF制作教程:保姆级古籍数字化教程

在智慧古籍数字化项目中,很多图书馆要求将古籍导出为双层PDF,并且确保输出双层PDF底层文本与上层图片偏移量控制在1毫米以内。那么本教程带你使用古籍数字化平台,3分钟把一个古籍书籍转化为双侧PDF。 第1步:上传古籍 点批量上传…

前序+中序、中序+后序构造二叉树

https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/ 前序中序 前序遍历,节点按照 [根左右] 排序。 中序遍历,节点…

JavaEE - Spring Boot 简介

1.Maven 1.1 什么是Maven 翻译过来就是: Maven是⼀个项⽬管理⼯具。基于POM(Project Object Model,项⽬对象模型)的概念,Maven可以通 过⼀⼩段描述信息来管理项⽬的构建,报告和⽂档的项⽬管理⼯具软件。 可以理解为:Maven是一个项目管理工具…

nginx隐藏server及版本号

1、背景 为了提高nginx服务器的安全性,降低被攻击的风险,需要隐藏nginx的server和版本号。 2、隐藏nginx版本号 在 http {—}里加上 server_tokens off; 如: http {……省略sendfile on;tcp_nopush on;keepalive_timeout 60;tcp_nodelay o…

ROS参数服务器增删改查实操Python

ROS参数服务器增删改查实操Python 环境准备参数服务器新增(修改)参数参数服务器获取参数参数服务器删除参数 ROS通信机制包括话题通信、服务通信和参数服务器三种通信方式,各原理及代码实现如下表 功能博客链接说明VScode配置 ROS 环境VScode…

《后端程序猿 · @Value 注释说明》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数…

基于 HTML+ECharts 实现监控平台数据可视化大屏(含源码)

构建监控平台数据可视化大屏:基于 HTML 和 ECharts 的实现 监控平台的数据可视化对于实时掌握系统状态、快速响应问题至关重要。通过直观的数据展示,运维团队可以迅速发现异常,优化资源配置。本文将详细介绍如何利用 HTML 和 ECharts 实现一个…