【GISER && Painter】Chapter00:OpenGL原理学习笔记
说明:简单了解一下OpenGL的工作原理,初步认识计算机对于图形渲染的底层设计与实现,第一次接触,也没学过C艹,欢迎各位批评指正。
一 什么是OpenGL?
OpenGL是一个开放标准(specification),是一种接口规范,并没有固定实现。每个硬件厂商负责对自己的硬件提供OpenGL接口标准的具体实现。三者关系如下链表:OpenGL API---硬件厂商【各自完成具体实现接口】--使用者【调用OpenGL提供的接口】【厂商的第三方库并不开源,但目前已有开源GL实现的DEMO,如Mesa,有兴趣可自行了解。】
在日常使用OpenGL时,需要的一些辅助第三方库:GLUT,辅助实现窗口显示、控制流程等功能,而OpenGL仅仅承担图形渲染的工作,即具体利用视图变换、光照以及纹理着色等技术,还原实现了虚拟场景:
实例1
实例2
二 OpenGL中不得不提的图形渲染管线(Pipeline)
图形渲染管线(Pipeline)可以看做是一条像素生产流水线,【流水线的输入端是顶点数据和像素数据,经过光栅化后,将片元(Fragment)组合着色成为屏幕可视内容,实现渲染效果】,
- 固定的图形管线渲染流程如下:
1 )指定需要完成渲染的几何图元:这些几何图元均是通过顶点指定的,其中包括了点、直线以及三角形等几何要素。
2) 顶点变换操作:在获得了指定图元后,就可以对定义图元的顶点进行变换处理。
3 )裁剪、透视触发以及视点适应变换等:将各个顶点进行变换处理后,需要将世界坐标中的图元规范化处理,统一缩放到正规化可视空间CCV中,图元坐标将会从世界坐标转换为窗口坐标;
2/3这一套处理方式可以从图形渲染管线的主干中分出一个顶点渲染管线分支,该分支将在下一节详细介绍。
4 )光栅化【1】:坐标变换,几何离散化,即将图元从三维坐标转换为一个二维平面图像。
5 )片元处理操作:对光栅化后的片元图像进行综合处理:提取纹理、效果、颜色等,对片元进行逐个测试,如像素所有权、剪切、模版测试以及深度测试等,只有当所有测试都通过之后,才会写入帧缓冲区(Frame Buffer)【2】
6) 片元数据转换为像素数据形式,写入帧缓冲区
图1 OpenGL的图形渲染管线【3】
三 固定管线中的核心——顶点处理管线
在图像渲染管线中细分出的顶点处理管线主要用于对顶点数据进行降维转换,即将三维空间中的实点转换到二维空间屏幕的坐标系上。
1)数学基础
在顶点处理管线中,最重要的一个概念在于变换,即上面概念提到的实点的空间转换。但在详细解释顶点处理管线中的空间转换前,我们需要简单了解一下所需的数学基础:齐次坐标 和 矩阵与图形变换。
- 坐标:本质上是一个增加维度的操作,即n维向量(p1,p2,p3,…,pn)增加至第n+1维(p1,p2,p3…,pn,pn+1),方便空间变换中的旋转、平移、透视等操作。
- 矩阵与坐标变换:图形是由一个个坐标点组成的,通过矩阵与转换矩阵相乘,得到变换后的坐标,即得到了空间变换后的图形。
举一个简单的例子:
平移操作:设某几何图形的三维点坐标集合为{ (x,y,z) | x , y , z∈R},先要将图形整体平移(a, b , c),首先,需要将三维点坐标拓展为齐次坐标(x,y,z,1),其次,构造一个4*4的矩阵T:
图2 平移单位矩阵T
所以,平移操作可以视为:(x,y,z,1)·T = (x+a, y+b, z+c, 1),所以该几何图形经过平移后的三维点坐标集合为{ (x+a, y+b, z+c) | x , y , z∈R}
2)顶点处理管线主要步骤
2-1)模型视图变换:
① 模型变换主要包括了位移、旋转和缩放等仿射变换【4】,这个部分的模型变换比较简单,上文也提到了关于平移操作的具体矩阵,所以在此就不赘述。模型变换中涉及到的变换大都是仿射变换,仅通过目标几何体的齐次坐标矩阵与操作单位矩阵点乘即可得到变换位置后的几何体。
上述三个仿射变换操作,在OpenGL中分别对应着:glTranslate*(), glRotate*(), glScale*()。
图3 仿射变换
② 视图变换
主要目的在于将目标几何体从世界坐标中移至摄像机坐标,该变换主要围绕“摄像机”的概念进行定义和实现:
首先,我们以摄像机作为视点,将视点移动到当前坐标系中的任一固定位置,并以此位置为摄像机坐标系的原点。若要将已处在世界坐标系上的几何体转换到摄像机的坐标体系中,则需要先得到摄像机坐标系与世界坐标系的相对关系,然后在从世界坐标系中取得几何体,并对该目标几何体做出相应的相对关系处理。
在OpenGL中提供了接口:gluLookAt(GLdouble eyex, GLdouble eyey, GLdouble eyez, GLdouble centerx, GLdouble centery, GLdouble centerz, GLdouble upx, GLdouble upy, GLdouble upz)
2-2)光和颜色:
① 光照:
着色需要考虑光照反射光的情况,可以从单点光照模型的情况入手:
光照模型中,我们所能看到的光主要以目标物的反射光为主,先不考虑入射光(有色OR无色)情况,反射光由镜面反射和漫反射组成,除了镜反射光IJ、漫反射光IM外,考虑到外部环境影响,设置环境光为IE,环境反射光=环境光IE*环境反射系数pe。按照聚光灯模型,反射光IReturn会随着角度和距离的变化而衰减,所以:IReturn=(IJ+IM+IE*pe)*atten(光源到几何体面上某点的距离D)*衰减系数cos(β),再加上全局环境光IE_ALL与自发光e,总体反射光为:
IReturnAll = IReturn + IE_ALL*pe + e
在OpenGL中,最多可以定义8个光源,每个光源可以设置不同的属性:
- 定义光源:Void glLight*(Glenum light, Glenum pname, GLfloat param)
【light】对应光源
【pname】参数字段
【param】参数值
其中pname可用参数参照下表:
GL_AMBIENT |
环境光分量 |
GL_DIFFUSE |
漫反射分量 |
GL_SPECULAR |
镜面反射分量 |
GL_POSITION |
光照位置 |
GL_SPOT_CUTOFF |
聚光灯角度 |
GL_SPOT_DIRECTION |
聚光灯方向向量 |
GL_SPOT_EXPONENT |
比例系数e |
GL_CONSTANT_ATTENUATION |
聚光灯衰减系数 |
GL_LINEAR_ATTENUATION |
聚光灯衰减系数 |
GL_QUADRATIC_ATTENUATION |
聚光灯衰减系数 |
- 激活光源(默认不可用):glEnable(glLight*)
- 定义目标物的表皮材质:void glMaterialf( GLenum face, Glenum pname, Gfloat param)
② 上色:
OpenGL一般有两种着色模型:平面着色( Flat shading )和平滑着色( Smooth Shading),从效果来看,平滑着色在细节表现上要优于平面着色。而平滑着色又分为Gouraud着色和Phong着色,其中Gouraud着色采用了线性插值的淡化边方法,每个像素的颜色值都是通过线性插值的方式得到的,可以平滑的表达色彩和光照的过渡,但缺点在于不善于表达强光区。而Phong则是对法向量进行着色,当前OpenGL不支持该着色模型,即无法进行透视投影变换等后续操作。
在OpenGL中:
- 设置OpenGL着色模式的函数:
void glShadeModel(GL_SMOOTH/GL_FLAT)
- 提供了如下API接口以供颜色绘制:
void glColor3f( GLfloat red, GLfloat green, GLfloat blue)
void glColor4f( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
(该着色过程发生在模型视图变换之后)
- 颜色清除API:
void glClearColor(GLfloat , GLfloat , GLfloat, GLfloat ) //用于指定空色
void glClear(GL_COLOR_BUFFER_BIT) //空色填充,清除画板
PS:值得一提的是,OpenGL3.0后均采用GLSL语言进行可编程渲染,通过着色器替代顶点处理管线、纹理化和色彩化环节【5】。着色器技术的可编程性可以实现各种图像效果而不受显卡固定管线的限制,提高了图像的画质。
2-3)投影变换(Porjection):
在完成了上述的视图模型变换之后,我们已经将几何体从世界坐标中转换到了摄像机坐标系中,接下来,为了在二维平面上表示该几何体,我们需要继续进行下一个步骤——投影变换。投影变换可以简单地理解为,将三维空间的几何体投影到摄像机视锥体的近平面和远平面之间。一般投影变换可以分为透视投影、正交投影和斜投影(本文以透视投影为例)。
图4 透视投影和平面投影(近裁面和远裁面之间的空间称之为frustum)【6】
透视投影遵循近大远小的原理,符合人们日常的视觉心理,具有较好的仿真性。在OpenGL中也提供了两个API接口:
- void glFrustum( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far) 创建一个透视投影矩阵
【left/right, bottom/top, -near】左下角点( left, bottom, -near)和右上角点( right, top, near)
【far】远裁面的Z值(负数)
- gluPerspective( GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar )
【fovy】x-z平面的视角张角的范围
【aspect】实际窗口的长宽比x/y
【zNear】视锥体的近裁面
【zFar】视锥体的远裁面
2-4)裁剪、透视除法(clip)
经过上述处理,如果点位于Frustum的空间中,则投影变换后的几何体点坐标均介于[-1,1]之间,并将x∈[-1,1], y∈[-1,1], z∈[-1,1]的正方体定义为正规化可视空间CCV,裁剪的目的在于裁剪掉CCV外的图元。
透视除法:由于之前为了方便模型视图变换操作,我们用四维齐次坐标作为三维点坐标的代表进行矩阵操作,所以需要通过一定的操作将齐次坐标(x, y, z, q)转换回三维点坐标(x, y, z)。具体操作为:齐次坐标除以第四个分量q,然后丢弃第四个分量,转换回归一化的三维坐标(x/q, y/q, z/q)。
2-5)视口变换(view port)
此时我们已经得到了归一化坐标,即所有的点坐标都处于CCV中,所以需要通过缩放和位移使其在窗口(屏幕)输出渲染。
在OpenGL中提供了:
- void glViewport( Glint x, GLin y, GLsizei width, GLsizei height )
【x, y】视窗屏幕的左下角坐标
【width/height】视窗屏幕的实际宽高
PS:此处需要提醒一下视口变换与投影变换的区别:
图5 视口变换与投影变换的区别【7】
2-6)最后得到窗口坐标,进行光栅化处理。
【名词解释 & 参考文献】
【1】光栅化(Rasterize)【像素化或栅格化】的渲染方式:
图元(primitive)指点、线、三角形等基本几何图形,片元(fragment):在光栅化之后,裁剪后称之为片元,片元采用的是屏幕窗口坐标
【2】帧缓冲区:帧缓存OR显存,是屏幕显示画面的直接映像,逻辑上是一组由屏幕上所有像素组成的二维数组,每一个存储单元负责对应屏幕上的一个像素,整个帧缓冲区对应的一帧图像即为当前屏幕显示的画面。一个完整的帧缓冲应当包括颜色缓存、深度缓存、模版缓存、积累缓存以及多重采样缓存
【3】Shreiner Dave. Opengl Programming Guide: The Official Guide To Learning Opengl, Version 2.1, 6/E. Pearson Education India, 2008.
【4】仿射变换:在几何中,一个向量空间进行一次线性变换外加一次平移操作,变换成为另一个向量空间,可以认为线性变换 (向量y=A·向量x )是一次特殊的仿射变换 (向量y=A·向量x+ 向量B )
【5】【OPENGL】第三篇 着色器基础(一):https://www.cnblogs.com/MyGameAndYOU/p/4691081.html
【6】songho:OpenGL Projection Matrix( http://www.songho.ca/opengl/gl_projectionmatrix.html
【7】图片来自于 :http://www.cnblogs.com/liangliangh/p/4089582.html
【8】本文的一部分内容是来源这位大神的博客:http://www.twinklingstar.cn/
【GISER && Painter】Chapter00:OpenGL原理学习笔记的更多相关文章
- OpenGL ES学习笔记(三)——纹理
首先申明下,本文为笔者学习<OpenGL ES应用开发实践指南(Android卷)>的笔记,涉及的代码均出自原书,如有需要,请到原书指定源码地址下载. <OpenGL ES学习笔记( ...
- OpenGL ES 学习笔记 - Overview - 小旋的博客
移动端图形标准中,目前 OpenGL ES 仍然是比较通用的标准(Vulkan 则是新一代),这里新开一个系列用于记录学习 OpenGL ES 的历程,以便查阅理解. OverView OpenGL ...
- Unity3D 骨骼动画原理学习笔记
最近研究了一下游戏中模型的骨骼动画的原理,做一个学习笔记,便于大家共同学习探讨. ps:最近改bug改的要死要活,博客写的吭哧吭哧的~ 首先列出学习参考的前人的文章,本文较多的参考了其中的表述: 1. ...
- OpenGL ES学习笔记(二)——平滑着色、自适应宽高及三维图像生成
首先申明下,本文为笔者学习<OpenGL ES应用开发实践指南(Android卷)>的笔记,涉及的代码均出自原书,如有需要,请到原书指定源码地址下载. <Android学习笔记--O ...
- OpenGL ES学习笔记(一)——基本用法、绘制流程与着色器编译
首先声明下,本文为笔者学习<OpenGL ES应用开发实践指南(Android卷)>的笔记,涉及的代码均出自原书,如有需要,请到原书指定源码地址下载. 在Android.iOS等移动平台上 ...
- Java并发之底层实现原理学习笔记
本篇博文将介绍java并发底层的实现原理,我们知道java实现的并发操作最后肯定是由我们的CPU完成的,中间经历了将java源码编译成.class文件,然后进行加载,然后虚拟机执行引擎进行执行,解释为 ...
- TCP/IP协议原理学习笔记
昨天学习了杨宁老师的TCP/IP协议原理第一讲和第二讲,主要介绍了OSI模型,整理如下: OSI是open system innerconnection的简称,即开放式系统互联参考模型,它把网络协议从 ...
- elasticsearch原理学习笔记
https://mp.weixin.qq.com/s/dn1n2FGwG9BNQuJUMVmo7w 感谢,透彻的讲解 整理笔记 请说出 唐诗中 包含 前 的诗句 ...... 其实你都会,只是想不起 ...
- 个人MySQL的事务特性原理学习笔记总结
目录 个人MySQL的事务特性原理笔记总结 一.基础概念 2. 事务控制语句 3. 事务特性 二.原子性 1. 原子性定义 2. 实现 三.持久性 1. 定义 2. 实现 3. redo log存在的 ...
随机推荐
- 用WP SMTP插件实现邮件发送功能
WordPress本身是采用mail()函数发邮件的,但是这样发出的邮件很容易被放入垃圾箱,很多主机商(特别是Windows主机)为了避免用户滥发邮件直接禁用了mail()函数,还有些云计算平台(比如 ...
- mvc部署
权限中加入windows 2008中加入SERVICE,windows2003中加入NETWORK SERVICE
- js 正则验证多个邮箱,用;隔开的那种
var r = /^((([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6}\;))*(([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\. ...
- Codeforces Round #300 E - Demiurges Play Again
E - Demiurges Play Again 感觉这种类型的dp以前没遇到过... 不是很好想.. dp[u] 表示的是以u为子树进行游戏得到的值是第几大的. #include<bits/s ...
- java总结(二)(运算符)
算数和赋值运算符 1.变量类型溢出时候,会直接取反:出现x>x+1 2.知道a++和++a 3.知道a/0错误 a/0.0无穷大 字符串 1.知道栈区.堆区和方法区 2.知道new String ...
- RecyclerView悬浮标题
效果图: 1.顶部会悬浮章的部分 2.第二章上滑会推挤第一章 3.第二章下拉会带出第一章 4.并不是所有时候都有悬浮部分(为什么这条标红,因为市面上几乎所有的悬浮都是必须存在且在顶部,害 ...
- TCP/IP重新学习
TCP/IP 是用于因特网 (Internet) 的通信协议. 1.什么是TCP/IP? TCP/IP 是供已连接因特网的计算机进行通信的通信协议. TCP/IP 指传输控制协议/网际协议(Trans ...
- go chapter 9 - 反射
https://www.cnblogs.com/diegodu/p/5590133.html // 反射,根据字段名设置值 package entities import( "reflect ...
- springMVC整合freemarker遇到的问题 maven
java.lang.IllegalAccessError: tried to access method freemarker.ext.servlet.AllHttpScopesHashModel.& ...
- 【BFS】【map】hdu5925 Coconuts
题意:一张n*m的网格图(n和m可以达到10^9),其中K个点是障碍物(不超过200个),问你没有被障碍物占据的点形成了几个连通块?并且输出各个连通块的大小. 容易证明,大小超过40000的连通块最多 ...