一、OpenGL简介

OpenGL(Open Graphics Library)是一个跨编程语言、跨平台的编程图形程序接口,它将计算机的资源抽象称为一个个OpenGL的对象,对这些资源的操作抽象为一个个的OpenGL指令。 OpenGLES(OpenGL for Embedded Systems)是 OpenGL 三维图形 API 的子集,针对手机、PDA和游戏主机等嵌入式设备而设计,去除了许多不必要和性能较低的API接口。 本文介绍的OpenGL版本是基于OpenGL ES 3.0的。这也是目前覆盖率最高的OpenGL版本,被广泛运用在各种终端设备上。

二、OpenGL上下文(Context)

在应用程序调用任何OpenGL的指令之前,需要安排首先创建一个OpenGL的上下文。这个上下文是一个非常庞大的状态机,保存了OpenGL中的各种状态,这也是OpenGL指令执行的基础。

OpenGL的函数不管在哪个语言中,都是类似C语言一样的面向过程的函数,本质上都是对OpenGL上下文这个庞大的状态机中的某个状态或者对象进行操作,当然你得首先把这个对象设置为当前对象。因此,通过对OpenGL指令的封装,是可以将OpenGL的相关调用封装成为一个面向对象的图形API的。

由于OpenGL上下文是一个巨大的状态机,切换上下文往往会产生较大的开销,但是不同的绘制模块,可能需要使用完全独立的状态管理。因此,可以在应用程序中分别创建多个不同的上下文,在不同线程中使用不同的上下文,上下文之间共享纹理、缓冲区等资源。这样的方案,会比反复切换上下文,或者大量修改渲染状态,更加合理高效的。

三、帧缓冲区(FrameBuffer)

OpenGL是图形API,因此可以说所有的运算和结果最终都是需要通过图像进行输出的。那么绘图必然就需要有一块画板,而帧缓冲区就是OpenGL中的画板。但是特别需要注意的是,帧缓冲区不是常规意义缓冲区(就像鲸鱼不是鱼一样),它并不是实际存储数据的对象,类似画画的时候,需要在画板上放一块画布,才能实际在画布上进行绘画,这些画布可以是纹理(Texture)或者是渲染缓冲区(RenderBuffer),而放置这些画布的位置被称为帧缓冲区的附着(Attachment)。

3.1、附着(Attachment)

附着可以理解为画板上的夹子,夹住了哪个画布,就往对应画布上输出数据。

在帧缓冲区中可以附着3种类型的附着,颜色附着(ColorAttachment),深度附着(DepthAttachment),模板附着(StencilAttachment)。这三种附着对应的存储区域也被称为颜色缓冲区(ColorBuffer),深度缓冲区(DepthBuffer),模板缓冲区(StencilBuffer)。

颜色附着输出绘制图像的颜色数据,也就是平时常见的图像的RGBA数据。如果使用了多渲染目标(Multiple Render Targets)技术,那么颜色附着的数量可能会大于一。 深度附着输出绘制图像的深度数据,深度数据主要在3D渲染中使用,一般用于判断物体的远近来实现遮挡的效果。 模板附着输出模板数据,模板数据是渲染中较为高级的用法,一般用于渲染时进行像素级别的剔除和遮挡效果,常见的应用场景比如三维物体的描边。

四、纹理(Texture)和渲染缓冲区(RenderBuffer)

前面已经说过,帧缓冲区并不是实际存储数据的地方,实际存储图像数据数据的对象就是纹理和渲染缓冲区。 他们三者的关系是这样的,纹理或渲染缓冲区作为帧缓冲区的附着。 那么,纹理和渲染缓冲区又有什么关系和区别呢? 纹理和渲染缓冲区同样是存储图像的对象。一般来说,渲染缓冲区对应操作系统提供的窗口,而纹理代表列离屏的图像存储区域。因此,渲染缓冲区都是2D的图像类型,而纹理一般有立方体纹理,1D、2D、3D纹理等类型,同时纹理还额外支持了mipmap等其他特性。 值得注意的是,一般来说渲染缓冲区和纹理不能同时挂载在同一个帧缓冲区上。

五、顶点数组(VertexArray)和顶点缓冲区(VertexBuffer)

准备好了画布之后,就要开始画图了。画图一般是先画好图像的骨架,然后再往骨架里面填充颜色,这对于OpenGL也是一样的。顶点数据就是要画的图像的骨架,和现实中不同的是,OpenGL中的图像都是由图元组成。在OpenGL ES中,有3种类型的图元:点、线、三角形。那这些顶点数据最终是存储在哪里的呢?开发者可以选择设定函数指针,在调用绘制方法的时候,直接由内存传入顶点数据,也就是说这部分数据之前是存储在内存当中的,被称为顶点数组。而性能更高的做法是,提前分配一块显存,将顶点数据预先传入到显存当中。这部分的显存,就被称为顶点缓冲区。

六、索引数组(ElementArray)和索引缓冲区(ElementBuffer)

索引在OpenGL叫Element确实有点不够贴切,而在DirectX中叫做IndexBuffer更加合适一些。

索引数据的目的主要是为了实现顶点的复用,在绘制图像时,总是会有一些顶点被多个图元共享,而反复对这个顶点进行运算常常是没有必要的(也有某些特殊场景需要)。因此对通过索引数据,指示OpenGL绘制顶点的顺序,不但能防止顶点的重复运算,也能在不修改顶点数据的情况下,一定程度的重新组合图像。 和顶点数据一样,索引数据也可以以索引数组的形式存储在内存当中,调用绘制函数时传入;或者提前分配一块显存,将索引数据存储在这块显存当中,这块显存就被称为索引缓冲区。同样的,使用缓冲区的方式,性能一般会比直接使用索引数组的方式更加高效。 OpenGL ES提供了2种主要的绘制方法:glDrawArrays和glDrawElements。前者对应的就是没有索引数据的情况,后者对应的是有索引数据的情况。

七、着色器程序(Shader)

在固定渲染管线时代,这一步并不是必须的。而是由内置的一段包含了光照、坐标变换、裁剪等等诸多功能的固定shader程序来完成。而可自定义shader,可以说是现代图形API最重要的能力了,没有之一。可以说,shader提供对图形运算的精细操作,带来了各式各样的处理能力,极度的丰富了图形API所能实现的效果。

OpenGL和其他主流的图形API早在好几年前,就全面的将固定渲染管线架构变为了可编程渲染管线。因此,OpenGL在实际调用绘制函数之前,还需要指定一个由shader编译成的着色器程序。

常见的着色器主要有顶点着色器(VertexShader),片段着色器(FragmentShader)/像素着色器(PixelShader),几何着色器(GeometryShader),曲面细分着色器(TessellationShader)。片段着色器和像素着色器只是在OpenGL和DX中的不同叫法而已。可惜的是,直到OpenGL ES 3.0,依然只支持了顶点着色器和片段着色器这两个最基础的着色器。

OpenGL在处理shader时,和其他编译器一样。通过编译、链接等步骤,生成了着色器程序(glProgram),着色器程序同时包含了顶点着色器和片段着色器的运算逻辑。在OpenGL进行绘制的时候,首先由顶点着色器对传入的顶点数据进行运算。再通过图元装配,将顶点转换为图元。然后进行光栅化,将图元这种矢量图形,转换为栅格化数据。最后,将栅格化数据传入片段着色器中进行运算。片段着色器会对栅格化数据中的每一个像素进行运算,并决定像素的颜色,也可以在这个阶段将某些像素丢弃。

其中像素的颜色可以是具体的数值或者是由某种算法计算而来的。如果图元有纹理,就必须用纹理来产生图元的二维渲染图象上每个像素的颜色。对于图元在二维屏幕上图象的每个像素来说,都必须从纹理中获得一个颜色值。我们把这一过程称为纹理过滤(texture filtering),纹理过滤根据不同的过滤方式会由一个或多个像素确定最终获得的颜色。表示这个像素位置的数据被称为纹理坐标(TextureCoordinate)而寻找这个纹理中对应像素位置的方法被称为纹理寻址方式或者纹理环绕方式(TextureWrap)。 最终,没有被丢弃的像素,下一步会进入测试阶段。通过了深度测试和模板测试,会和帧缓冲区上的颜色附着(FrameBuffer上的ColorAttachment)上的颜色进行混合,决定最终留在画布上的颜色是什么。

7.1、顶点着色器(VertexShader)

顶点着色器是OpenGL中用于计算顶点属性的程序。顶点着色器是逐顶点运算的程序,也就是说每个顶点数据都会执行一次顶点着色器,当然这是并行的,并且顶点着色器运算过程中无法访问其他顶点的数据。

顶点着色器的数据输入主要有两种,统一变量(Uniform)、顶点属性(VertexAttribute)。统一变量在所有顶点运算中是一样的,而顶点属性则是从外部输入的顶点数据中获取,一般在每个顶点运算中都是不同的。

一般来说典型的需要计算的顶点属性主要包括顶点坐标变换、逐顶点光照运算等等。顶点坐标由自身坐标系转换到归一化坐标系的运算,就是在这里发生的。 同时顶点着色器的输出结果,也会作为片段着色器的输入。

八、逐片段操作(Per-Fragment Operation)

8.1、测试(Test)

在着色器程序完成之后,我们得到了像素数据。这些数据必须要通过测试才能最终绘制到画布,也就是帧缓冲上的颜色附着上。

测试主要可以分为像素所有者测试(PixelOwnershipTest)、裁剪测试(ScissorTest)、模板测试(StencilTest)和深度测试(DepthTest),执行的顺序也是按照这个顺序进行执行。 最开始进行的测试是像素所有者测试,主要是剔除不属于当前程序的像素运算。 之后裁剪测试,主要是剔除窗口区域之外的像素。 这两个测试都是由OpenGL内部实现的,无需开发者干预,因此不再进行赘述。

深度测试,主要是通过对像素的运算出来的深度,也就是像素离屏幕的距离进行对比,根据OpenGL设定好的深度测试程序,决定是否最终渲染到画布上。一般默认的程序是将离屏幕较近的像素保留,而将离屏幕较远的像素丢弃。如果像素最终被渲染到画布上,根据设定好的OpenGL深度覆写状态,可能会更新帧缓冲区上深度附着的值,方便进行下一次的比较。

模板测试和深度测试的执行原理一致,但是执行的顺序是在深度测试之前的,放在后面 主要是比深度测试更加难以理解一些,初学者可以暂时跳过这个部分。模板测试同样也是通过模板测试程序去决定最终的像素是否丢弃,同样也是根据OpenGL的模板覆写状态决定是否更新像素的模板值。模板测试给开发者提供了高性能的裁剪方案,三维物体的描边技术,就是模板测试典型的用处之一。

8.2、混合(Blending)

在测试阶段之后,如果像素依然没有被剔除,那么像素的颜色将会和帧缓冲区中颜色附着上的颜色进行混合,混合的算法可以通过OpenGL的函数进行指定。但是OpenGL提供的混合算法是有限的,如果需要更加复杂的混合算法,一般可以通过像素着色器进行实现,当然性能会比原生的混合算法差一些。

8.3、抖动(Dithering)

在混合阶段过后,根据OpenGL的状态设置,会决定是否有抖动这个阶段。 抖动是一种针对对于可用颜色较少的系统,可以以牺牲分辨率为代价,通过颜色值的抖动来增加可用颜色数量的技术。抖动操作是和硬件相关的,允许程序员所做的操作就只有打开或关闭抖动操作。实际上,若机器的分辨率已经相当高,激活抖动操作根本就没有任何意义。默认情况下,抖动是激活的。

九、渲染到纹理

有些OpenGL程序并不希望渲染出来的图像立即显示在屏幕上,而是需要多次渲染。可能其中一次渲染的结果是下次渲染的输入。因此,如果帧缓冲区的颜色附着设置为一张纹理,那么渲染完成之后,可以重新构造新的帧缓冲区,并将上次渲染出来的纹理作为输入,重新进行前面所述的流程。

十、渲染上屏/交换缓冲区(SwapBuffer)

前面已经提过,渲染缓冲区一般映射的是系统的资源比如窗口。如果将图像直接渲染到窗口对应的渲染缓冲区,则可以将图像显示到屏幕上。 但是,值得注意的是,如果每个窗口只有一个缓冲区,那么在绘制过程中屏幕进行了刷新,窗口可能显示出不完整的图像。 为了解决这个问题,常规的OpenGL程序至少都会有两个缓冲区。显示在屏幕上的称为屏幕缓冲区,没有显示的称为离屏缓冲区。在一个缓冲区渲染完成之后,通过将屏幕缓冲区和离屏缓冲区交换,实现图像在屏幕上的显示。

由于显示器的刷新一般是逐行进行的,因此为了防止交换缓冲区的时候屏幕上下区域的图像分属于两个不同的帧,因此交换一般会等待显示器刷新完成的信号,在显示器两次刷新的间隔中进行交换,这个信号就被称为垂直同步信号,这个技术被称为垂直同步。

使用了双缓冲区和垂直同步技术之后,由于总是要等待缓冲区交换之后再进行下一帧的渲染,使得帧率无法完全达到硬件允许的最高水平。为了解决这个问题,引入了三缓冲区技术,在等待垂直同步时,来回交替渲染两个离屏的缓冲区,而垂直同步发生时,屏幕缓冲区和最近渲染完成的离屏缓冲区交换,实现充分利用硬件性能的目的。

OpenGL学习笔记(二)相关推荐

  1. 现代OpenGL学习笔记二:第一个三角形

    本笔记主要是跟学LearnOpenGL的内容,并将其中一些不懂的地方通过查阅资料进行整理补充,推荐参考原文: 整个教程:https://learnopengl-cn.github.io/ 本笔记参考教 ...

  2. android opengl旋转,Android openGl学习笔记二,gl的旋转、位移、放大缩小

    这章文章是对上一篇的进一步学习,最终以达到类似动画效果的目的. 不管什么时候参考资料放在第一位:学习参考资料 这个是本人经过层层筛选后感觉比较全且比较易懂的文章了,学习记录将按照此链接的博客逐步深入学 ...

  3. OpenGl学习笔记二:创建自己的着色器

    1. GLSL 着色器是使用一种叫GLSL的类C语言写成的.GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性. 从基本意义上来说,着色器只是一种把输入转化为输出的程序.着色器也是 ...

  4. OpenGL学习笔记(八):进一步理解VAO、VBO和SHADER,并使用VAO、VBO和SHADER绘制一个三角形

    原博主博客地址:http://blog.csdn.net/qq21497936 本文章博客地址:http://blog.csdn.net/qq21497936/article/details/7888 ...

  5. OpenGL学习笔记:矩阵变换

    文章目录 缩放 glm矩阵表示 glm缩放矩阵实现 位移 齐次坐标 glm位移矩阵实现 旋转 沿x轴旋转 沿y轴旋转 沿z轴旋转 沿任意轴旋转 glm旋转矩阵实现 矩阵的组合 glm矩阵组合使用 接上 ...

  6. 【OpenGL学习笔记⑧】——键盘控制正方体+光源【冯氏光照模型 光照原理 环境光照+漫反射光照+镜面光照】

    ✅ 重点参考了 LearnOpenGL CN 的内容,但大部分知识内容,小编已作改写,以方便读者理解. 文章目录 零. 成果预览图 一. 光照原理与投光物的配置 1.1 光照原理 1.2 投光物 二. ...

  7. OPENGL学习笔记之八

    OPENGL学习笔记之八 2017/11/15 阅读材料来自learnopengl.com以及learnopengl-cn.github.io 我们通常会自己设定一个坐标的范围,之后再在顶点着色器中将 ...

  8. 【OpenGL学习笔记】地月系

    OpenGL学习笔记2-地月系 文章目录 OpenGL学习笔记2-地月系 前言 运行结果 纹理图片 一.TexturePool 1.**TexturePool.h** 2.**TexturePool. ...

  9. 【OpenGL学习笔记⑥】——3D变换【旋转的正方体 实现地月系统 旋转+平移+缩放】

    ✈️ 文章目录 零. 成果预览图 一.3D立方体的顶点数组 二.纹理旋转 三.纹理缩放 四.画n个3D图形 五.轨道的数学公式 六.深度缓冲(Z 缓冲) 七.完整代码 八.参考附录: 神器的正方体 ☁ ...

最新文章

  1. 沃通免费SSL证书申请指南
  2. 织梦html仅动态,dede织梦系统后台发布文章时设置为默认动态浏览的方法
  3. dwr消息推送和tomcat集群
  4. 如何选择开源许可证如何修改项目使其符合某种开源许可证
  5. Mysql手动增加一列_Blog of Grow_百度空间
  6. 逆向工程核心原理学习笔记(七):总结
  7. Js获取字符串的显示宽度/高度
  8. 从零开始学PowerShell(2)管道的作用
  9. java中如何播放wav_如何播放.wav文件与java
  10. shell按照时间排序_初识Shell(3)
  11. 使用Junit对Android应用进行单元测试
  12. 开源开放 | OMAHA 联合 OpenKG 发布新冠诊疗图谱数据
  13. html caption 靠左,HTML caption align 属性
  14. 华为的芯片战略:别忘记,代号—SD502!
  15. 激光电视好还是OLED电视好?两者各有什么优缺点
  16. linux yum安装redis
  17. OSPF规划两大模型:双塔奇兵、犬牙交错
  18. matlab 生成静音文件,matlab去除静音段
  19. 推荐开发工具系列之--PyF5(自动刷新)
  20. cad打印黑白图纸,该如何打印呢?

热门文章

  1. 对国内GIS行业的思考
  2. Excel VBA MD5 加密 16位 32位
  3. 用GPS模块校准系统时间
  4. mysql_check_MySQL中check的用法
  5. 为什么注销计算机用户登录,为什么我的电脑开不了机?一点用户登陆就显示正在注销了....
  6. 用Python实现查心率
  7. 轻松捕捉85点策略:欧元/日元经典交易策略全公开
  8. 净利环比缩水过半,Salesforce龙头地位不保?
  9. 微信开发学习笔记01
  10. 基于单片机仓库智能巡检车的设计