深入浅出OpenGL三维渲染管线

news2025/1/22 19:11:44

1 前言

在计算机图形学中,渲染是根据模型描述在显示器上生成图像的过程。3D图形渲染管线输入根据图元顶点(如三角形、点、线和四边形)对3D模型的描述,并为显示器上的像素生成颜色值。

如下图所示的是3D图形渲染管线的流程。
在这里插入图片描述
3D图形渲染管线主要包含以下几个主要阶段:

  1. Vertex Processing:处理顶点,即对每一个顶点做变换;
  2. Rasterization:光栅化,即将每一个图元转换为片元。片元可以被认为是3D空间中的像素,它与像素网格对齐,具有位置、颜色、法线和纹理等属性;
  3. Fragment Processing:对每一个片元进行处理;
  4. Output Merging:将所有在3D空间的片元合并成2D彩色像素以便显示。

在现代GPU中,Vertex Processing和Fragment Processing是可编程的,我们可以编写顶点着色器和片元着色器的程序来执行对顶点和片元的自定义变换。而Rasterization和Output Merging是不可编程的,是通过向GPU发出配置命令进行配置。

2 顶点、图元、片元和像素

先讲一下顶点、图元、片元和像素这四个概念以及之间的关系。
这四者的生成是有先后顺序的:顶点>图元>片元>像素,生成顺序示意图如下图所示。

  • 顶点:最小的单位
  • 图元:由顶点组成的。一个顶点,一条线段,一个三角形或者多边形都可以成为图元
  • 片元:在图元经过光栅化阶段后,被分割成一个个像素大小的基本单位。片元其实已经很接近像素了,但是它还不是像素。片元包含了比RGBA更多的信息,比如可能有深度值,法线,纹理坐标等等信息。片元需要在通过一些测试(如深度测试)后才会最终成为像素。可能会有多个片元竞争同一个像素,而这些测试会最终筛选出一个合适的片元,丢弃法线和纹理坐标等不需要的信息后,成为像素。
  • 像素:最终呈现在屏幕上的包含RGBA值的图像最小单元
    在这里插入图片描述
    以上是生成顺序的一个大致流程,大家也对这四个概念之间的关系有了一个初步的了解。现在具体说明一下。

2.1 顶点

在计算机图形学中,顶点具有以下属性:

  • 3D 空间中的位置 V=(x, y, z):通常用浮点数表示。
  • 颜色:以 RGB(红-绿-蓝)或 RGBA(红-绿-蓝-Alpha)分量表示。 分量值通常归一化到 0.0 到 1.0 的范围(或 0 到 255 之间的 8 位无符号整数),alpha 用于指定透明度,alpha 为 0 表示完全透明,alpha 为 1 表示不透明。
  • Vertex-Normal N=(nx, ny, nz):我们熟悉表面法线的概念,法线向量垂直于表面。 然而,在计算机图形学中,我们需要为每个顶点附加一个法线向量,称为顶点法线。 法线用于区分正面和背面,以及用于其他处理,例如照明。 在 OpenGL 中使用右手法则(或逆时针方向)。法线指向外,表示外表面(或正面)。
  • 纹理 T=(s, t):在计算机图形学中,我们经常将 2D 图像包裹到物体上,以使其看起来逼真。 一个顶点可以有一个 2D 纹理坐标 (s, t),它提供了一个到 2D 纹理图像的参考点。

2.2 图元

在OpenGL中,它是支持三类几何图元:点、线段和闭合多边形。 它们是通过顶点指定的,每个顶点都与其属性相关联,例如位置、颜色、法线和纹理。 如图所示,OpenGL 提供了 10 个图元。
在这里插入图片描述

2.3 片元

在由顶点组成的图元中,其通常以浮点值表示的顶点不一定与显示器的像素网格对齐。因此在光栅化阶段,对由一个或多个顶点定义的每个图元进行光栅扫描,得到一组包含在图元中的片元,片元可以被视为与像素网格对齐的3D像素,从顶点插值的3D片段具有与顶点相同的属性,例如位置、颜色、法线和纹理。
光栅化阶段包括视口变换、裁剪、透视划分、背面剔除和扫描转换。 光栅器不是可编程的,但可以通过指令进行配置。

光栅化阶段完成后,还需要对片元进行处理

下面分别讲述光栅化阶段中的几个重要步骤和片元处理。

2.3.1 视口变换

视口是应用程序窗口上的一个矩形显示区域,以屏幕坐标(以像素为单位,原点在左上角)测量。 视口定义显示区域的大小和形状,以将相机捕获的投影场景映射到应用程序窗口。 它可能会或可能不会占据整个屏幕。

在OpenGL中,视口默认设置为覆盖整个应用程序窗口,我们可以使用 glViewport() 函数来选择较小的区域(例如,用于分屏或多屏应用程序)。

void glViewport(GLint xTopLeft, GLint yTopLeft, GLsizei width, GLsizei height)

我们还可以通过 glDepthRange() 设置视口的 z 范围

glDepthRange(GLint minZ, GLint maxZ)

视口变换,即将裁剪体积(2x2x1 长方体)映射到 3D 视口,如下图所示。它是由一系列仿射(y 轴)、缩放(x、y 和 z 轴)和平移(从裁剪体积的近平面中心到左上角的原点)组成 的 3D 视口。
在这里插入图片描述
如果视口覆盖整个屏幕,则 minX=minY=minZ=0,w=screenWidth 和 h=screenHeight。

Note:如果视口的纵横比和投影平面的纵横比不一样,形状就会发生变形。因此需要重新配置投影平面的纵横比以匹配视口的纵横比。

// Callback when the OpenGL's window is re-sized.
void reshape(GLsizei width, GLsizei height) {  // GLsizei for non-negative integer
   if (height == 0) height = 1;                        // To prevent divide by 0
   GLfloat aspect = (GLfloat)width / (GLfloat)height; // Compute aspect ratio
   
   // Set the viewport (display area on the window) to cover the whole application window
   glViewport(0, 0, width, height);
   
   // Adjust the aspect ratio of projection's clipping volume to match the viewport 
   glMatrixMode(GL_PROJECTION);   // Select Projection matrix
   glLoadIdentity();              // Reset the Projection matrix
   
   // Either "perspective projection" or "orthographic projection", NOT both
   
   // 3D Perspective Projection (fovy, aspect, zNear, zFar), relative to camera's eye position 
   gluPerspective(45.0, aspect, 0.1, 100.0);
   
   // OR

   // 3D Orthographic Projection (xLeft, xRight, yBottom, yTop, zNear, zFar),
   // relative to camera's eye position.
   if (width <= height) {
      glOrtho(-1.0, 1.0, -1.0 / aspect, 1.0 / aspect, -1.0, 1.0);  // aspect <= 1
   } else {
      glOrtho(-1.0 * aspect, 1.0 * aspect, -1.0, 1.0, -1.0, 1.0);  // aspect > 1
   }
   
   // Reset the Model-View matrix
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
}

2.3.2 背面剔除

视锥剔除丢弃视锥外的对象,而背面剔除则丢弃不面向相机的图元。

可以根据法向量和连接表面和相机的向量来声明背面。

如果对象是透明的并且启用了 alpha 混合,则不应启用背面剔除。

2.3.3 片元处理

光栅化后,每个图元都有一组片元,一个片有一个位置,它与像素网格对齐,具有深度、颜色、法线和纹理坐标,这些坐标是从顶点插值得到的。

片元处理侧重于纹理和光照,这两者对图形的质量影响最大。片元处理器涉及的操作有纹理化、结合原色和二次色应用雾计算。

3 点操作

点操作对应前言部分图片中的Vertex Processing,点操作可细分为3个变换,如下图所示,输入原始的点,经过3次坐标变换后,每个顶点连同它们的顶点法线一起变换并定位在 裁剪体积长方体空间中,其中x和y坐标(在-1到+1的范围内)代表它在屏幕上的位置,z值(在0到1的范围内)代表它的深度,即离近平面有多远;再通过光栅化阶段的视口变换最终得到屏幕空间下的点,显示在显示器上。
在这里插入图片描述
以上坐标变换涉及到4个转换:

  1. 将物体/模型放在世界空间下,即模型变换或世界变换;
  2. 放置相机,即视图变换;
  3. 选择相机的镜头,调整焦距和变焦系数以设置相机的视野,即投影变换;
  4. 光栅化阶段,在指定区域将图显示出来,即视口变换。

3.1 模型变换/世界变换

3D 场景中的每个对象/模型通常在自己的坐标系中绘制,称为模型空间(或局部空间,或对象空间)。当我们组装对象时,我们需要将顶点从它们的局部空间转换到所有对象共有的世界空间,称为世界变换。 世界变换由一系列缩放(缩放对象以匹配世界的维度)、旋转(对齐轴)和平移(移动原点)组成。

在这里插入图片描述

旋转和缩放属于一类称为线性变换的变换(根据定义,线性变换保留向量加法和标量乘法)。 线性变换和平移形成所谓的仿射变换, 在仿射变换下,直线仍然是直线并且点之间的距离比率保持不变。

3.2 视图变换

世界变换后,所有的物体都被转换到世界空间中, 我们现在将放置相机以捕捉视图。
在这里插入图片描述
定位相机:
在 3D 图形中,我们通过指定三个视图参数将相机定位到世界空间:世界空间中的 EYE、AT 和 UP。

  • 点 EYE (ex, ey, ez) 定义了相机的位置。
  • 向量 AT (ax, ay, az) 表示相机瞄准的方向,通常是世界或物体的中心。
  • 矢量 UP (ux, uy, uz) 粗略地表示相机的向上方向。 UP 通常与世界空间的 y 轴重合。 UP 与 AT 大致正交,但不是必需的。 由于 UP 和 AT 定义了一个平面,我们可以在相机空间中构造一个与 AT 正交的向量。

在OpenGL中,在默认参数下,相机位于原点 (0, 0, 0),对准屏幕(负 z 轴),并面朝上(正 y 轴)。 如果使用默认参数,则必须将对象/物体放置在负 z 值处。

Note:在计算机图形学中,相对于固定相机移动对象(模型变换)和相对于固定对象移动相机(视图变换)产生相同的图像,因此是等价的。 因此,OpenGL 在所谓的模型-视图矩阵上以相同的方式管理模型变换和视图变换,投影变换(后面会讲)通过投影矩阵进行管理。

3.3 投影变换

相机定位和定向后,我们需要决定它可以看到什么(类似于通过调整焦距和缩放系数来选择相机的视野),以及如何将对象/物体投影到屏幕上。 这是通过选择投影模式(透视或正交)并指定查看体积或裁剪体积来完成的。 裁剪体积之外的对象被裁剪出场景并且看不到。

3.3.1 透视投影

在这里插入图片描述
在透视图中查看视锥体,相机的视野有限,呈现视锥体(截头金字塔),并由四个参数指定:fovy、aspect、zNear 和 zFar。

  • fovy:以度为单位指定总垂直视角。
  • aspect:宽度与高度的比率。 对于特定的 z,我们可以从 fovy 获取高度,然后从 aspect 获取宽度。
  • zNear; 近平面。
  • zFar:远平面。
    为方便起见,相机空间 (xc, yc, zc) 被重命名为熟悉的 (x, y, z)。

3.3.2 正交投影

除了常用的透视投影外,还有一种所谓的正交投影(或平行投影),这是一种特殊情况,相机放置在离世界很远的地方(类似于使用望远镜镜头)。 正交投影的视体积是平行六面体(而不是透视投影中的平截头体)。
在这里插入图片描述

4 总结

OpenGL渲染实际上就是将3D坐标进行系列转换变为2D坐标,整个流程如下:
在这里插入图片描述
除此之外,为了让最终结果和实际物体更接近, 会进行打光和贴纹理等操作。

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

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

相关文章

JVM知识

类加载机制 虚拟机把class文件加载到内存&#xff0c;并对数据进行校验&#xff0c;转换解析和初始化&#xff0c;形成虚拟机可以直接使用的Java类型&#xff0c;即java.lang.class 装载(Load) ClassFile -> 字节流 ->类加载器 查找和导入class文件 1&#xff1a;通…

解决方案|以大数据为抓手,打造粮食安全智慧监管平台

食为政首&#xff0c;粮安天下&#xff0c;粮食问题一直深受总书记记挂&#xff0c;总书记多次提到&#xff1a;“中国十三亿多人口&#xff0c;吃饭主要靠自己&#xff0c;不能靠外面来解决。” 近年来粮食安全事件频发&#xff0c;中央纪委国家监委在全国开展粮食购销领域腐败…

程序员的那些事儿

作者主页&#xff1a;爱笑的男孩。 持续分享&#xff1a;机器学习、深度学习、python相关内容、日常BUG解决方法及Windows&Linux实践小技巧。 如发现文章有误&#xff0c;麻烦请指出&#xff0c;我会及时去纠正。有其他需要可以私信我或者发我邮箱:zhilong666foxmail.com 目…

编译后的hue 替换cdh默认版本hue步骤

基于hue源码编译的hue 进行替换cdh6.x.x默认带的hue版本,主要解决hue滚动条 拉的时候,一下就到末尾的bug,通过源码编译githu上的hue解决问题 一. 拷贝编译好的hue到cdh目录替换原来hue目录 三.启动hue报错 问题一:没有pip命令 pip list -bash: pip: command not found …

4.17、TCP三次握手

4.17、TCP三次握手 1.TCP三次握手2.TCP通信具体流程①三次握手②服务器客户端进行通信 1.TCP三次握手 TCP 是一种面向连接的单播协议&#xff0c;在发送数据前&#xff0c;通信双方必须在彼此间建立一条连接。所谓的“连接”&#xff0c;其实是客户端和服务器的内存里保存的一…

热点数据监测方法

在日常开发中&#xff0c;我们需要着重注意一种场景-热点数据。他可能是一种请求&#xff0c;每次请求的数据类型都是一样的&#xff1b;可能是同一个数据&#xff0c;比如页面上公用的类型数据&#xff1b;可能是同一个用户大量的请求。他们都有着同一个特点&#xff0c;瞬时爆…

Redis---哨兵服务

一、配置哨兵服务 1、哨兵服务介绍 监视 master 服务器&#xff0c;发现 master 宕机后&#xff0c;将 slave 服务器提升为 master 服务器 主配置文件&#xff1a;sentinel.conf 模板文件&#xff1a;redis-4.0.8/sentinel.conf 哨兵服务&#xff1a;类似于mha的管理节点&#…

ELK日志

思维导图 一、ELK介绍 ELK是Elasticsearch、Logstash、Kibana首字母大写缩写&#xff0c;后续加入了Beats&#xff08;Beats是负责单一用途数据采集并推送给Logstash或Elasticsearch的轻量级产品&#xff09;&#xff0c;就更名为 Elastic Stack。 Elastic Stack技术栈的功能…

JVM-GC回收机制

目录 1.判定垃圾 1.引用计数 2.可达性分析 2.清理垃圾 1.标记清除 2.复制算法 3.标记整理 4.分代回收 上文讲述的Java运行时内存划分,对于程序计数器,虚拟机栈,本地方法栈来说,生命周期是和线程有关的,随着线程而生,随线程而灭,当方法结束或者线程结束时,它们的内存就自…

细讲const与引用的关系

目录 先了解语言层面的权限 进入正题引用与const权限关系 引用权限的概念 const引用返回值时错误情况 一&#xff1a;返回到临时空间时权限问题 二&#xff1a;临时空间到调用处保存问题 结论&#xff1a;如果不对子函数内部数据修改、那么在父函数变量ret的类型可以设置…

vite+vue3+ts开发流程

目录 vue页面开发 数据类型开发 组件开发 API开发 页面中使用组件、API、数据类型 vue页面开发 <template><!-- 卡片区域--><el-card style"padding:0px;"><!-- 搜索与添加区域--><div style"margin-bottom: 15px;&quo…

SAP ABAP selection screen 动态获取字段名不需要回车触发

SAP ABAP selection screen &#xff0c;get field vaule without click Enter key. 有个需求&#xff0c;界面上几个字段的搜索帮助需要根据已输入值联动&#xff0c;比如AOP version 根据已输入的BU 过滤。 但尴尬的是在事件AT SELECTION-SCREEN ON VALUE-REQUEST FOR 中&a…

.Net中的标识框架Identity

中间件&#xff1a; 1、Authentication对访问者的用户身份进行验证&#xff0c;“用户是否登录成功”。 2、Authorization验证访问者的用户身份是否有对资源访问的访问权限&#xff0c;“用户是否有权限访问这个地址”。 简单描述 1、标识&#xff08;Identity&#xff09;框架…

TCP协议二:TCP状态转换(重要)

TCP状态转换分析https://www.bilibili.com/video/BV1iJ411S7UA?p44&spm_id_frompageDriver&vd_sourced239c7cf48aa4f74eccfa736c3122e65 TCP状态转换图 粗实线&#xff1a;主动端 虚线&#xff1a; 被动端 细实线&#xff1a;内核操作 状态分析 CLOSED&#xff1…

RabbitMQ:消息中间件

文章目录 概念管理界面简介4中常见交换器类型1.Direct交换器:2.Fanout交换器3.Topic交换器4.headers交换器 对象类型消息传递同步等待使用代码创建队列待续...... 概念 在微服务架构中项目之间项目A调用项目B 项目B调用项目C项目C调用项目D。。 用户必须等待项目之间内容依次的…

后端太难了,不 All in 了

作者&#xff1a;阿秀 校招八股文学习网站&#xff1a;https://interviewguide.cn 这是阿秀的第「256」篇原创 小伙伴们大家好&#xff0c;我是阿秀。 欢迎今年参加秋招的小伙伴加入阿秀的学习圈&#xff0c;目前已经超过 2200 小伙伴加入&#xff01;去年认真准备和走下来的基…

Segment Anything Model代码讲解(二)之image_encoder

image_encoder代码解析 在transformer的结构中&#xff0c;编码是非常重要的部分。接下来看image_encoder的代码部分目录 class ImageEncoderViT def initdef forward class Block def initdef forward class Attention def initdef forward def window_partitiondef window_…

【C++】引用(下)【深度全面解析】

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…

自定义类型——枚举与联合体

枚举 枚举顾名思义就是一一列举 把可能的取值一一列举 枚举类型的定义 enum Day//星期 {Mon,Tues,Wed,Thur,Fri,Sat,Sun };enum Sex//性别 {MALE,FEMALE,SECRET }&#xff1b;以上定义的 enum Day &#xff0c; enum Sex 都是枚举类型 { }中的内容是枚举类型的可能取值&…

English Learning - L2 第 14 次小组纠音 复习元音 [ɔɪ ] [aʊ] [əʊ] [ɪə] 弱读 2023.4.12 周三

English Learning - L2 第 14 次小组纠音 复习元音 [ɔɪ ] [aʊ] [əʊ] [ɪə] 弱读 2023.4.12 周三 共性问题coin voice /kɔɪn/ /vɔɪs/ 中 ɔɪvowel pounds /ˈvaʊəl/ /paʊndz/ 中的 aʊshow /ʃəʊ/beer nearly /bɪə/ /ˈnɪəlɪ/ 中的 ɪəbest bed ten /best…