一、概述

        OpenGL是跨平台计算机图形应用程序的应用规范,广泛应用于仿真、游戏、GIS系统等领域,实现二三维图形的渲染。OpenGL渲染过程需要经历CPU、GPU两个阶段,CPU中进行图形计算,完成之后调用OpenGL开发接口在GPU中创建缓存区缓存绘制数据,将生成的几何数据(顶点坐标、几何单元等)输入到一系列着色器中进行处理。

        着色器(Shaders)是一段用着色器语言GLSL编写的脚本,在OpenGL渲染过程中通常依次经过顶点着色、细分着色以及几何着色阶段,然后对数据进行光栅化处理,生成片元数据,最后经过片元着色,将图形渲染到屏幕上。OpenGL的可编程管线着色器能直接对GPU的数据缓存进行操作,渲染效率高, 在X86商用计算机上帧率通常能达到60FPS以上。

        OpenGL的完整渲染过程,客户端运行于CPU中,通过驱动程序将数据与渲染指令进行连接,并发送到服务端执行。服务端和客户端为异步调用,因此两端都能够不间断的工作。客户端计算完毕后,将计算结果和命令块组合在一起送入缓冲区,然后缓冲区会发送到服务端执行。服务端执行缓冲区内容的同时,客户端已经在进行下一个周期的计算,如此循环实现计算和图形资源的最大化利用。

        图形渲染管线主要包括两个功能:一是将物体3D坐标转变为屏幕空间2D坐标,二是为屏幕每个像素点进行着色。渲染管线的一般流程如下图所示。分别是:顶点数据的输入、顶点着色器、曲面细分过程、几何着色器、图元装配、裁剪剔除、光栅化、片段着色器以及混合测试。

android opengl渲染 opengl渲染cpu_图形

 二、OpenGL渲染管线

android opengl渲染 opengl渲染cpu_图形渲染_02

1、顶点着色器

        顶点着色器主要功能是进行坐标变换。将输入的局部坐标变换到世界坐标、观察坐标和裁剪坐标。虽然我们也会在顶点着色器进行光照计算(称作高洛德着色),然后经过光栅化插值得到各个片段的颜色,但由于这种方法得到的光照比较不自然,所以一般在片段着色器进行光照计算。

2、曲面细分

        曲面细分是利用镶嵌化处理技术对三角面进行细分,以此来增加物体表面的三角面的数量,是渲染管线一个可选的阶段。它由外壳着色器(Hull Shader)、镶嵌器(Tessellator)和域着色器(Domain Shader)构成,其中外壳着色器和域着色器是可编程的,而镶嵌器是有硬件管理的。我们可以借助曲面细分的技术实现细节层次(Level-of-Detail)的机制,使得离摄像机越近的物体具有更加丰富的细节,而远离摄像机的物体具有较少的细节。

3、几何着色器

        几何着色器也是渲染管线一个可选的阶段。我们知道,顶点着色器的输入是单个顶点(以及属性), 输出的是经过变换后的顶点。与顶点着色器不同,几何着色器的输入是完整的图元(比如,点),输出可以是一个或多个其他的图元(比如,三角面),或者不输出任何的图元。几何着色器的拿手好戏就是将输入的点或线扩展成多边形。 

4、图元装配

        图元装配将输入的顶点组装成指定的图元。图元装配阶段会进行裁剪和背面剔除相关的优化,以减少进入光栅化的图元的数量,加速渲染过程。在光栅化之前,还会进行屏幕映射的操作:透视除法和视口变换。

        关于透视除法和视口变换到底属于流水线的那个阶段并没有一个权威的说法,某些资料将这两个操作归入到图元装配阶段,某些资料将它归入到光栅化过程,但对我们理解整个渲染管线并没有太大的影响,我们只需要知道在光栅化前需要进行屏幕映射就可以了,所以我们这里将屏幕映射放到了图元组装过程。这两个操作主要是硬件实现,不同厂商会有不同的设计。

5、光栅化

        经过图元组装以及屏幕映射阶段后,我们将物体坐标变换到了窗口坐标。光栅化是个离散化的过程,将3D连续的物体转化为离散屏幕像素点的过程。包括三角形组装和三角形遍历两个阶段。光栅化会确定图元所覆盖的片段,利用顶点属性插值得到片段的属性信息,然后送到片段着色器进行颜色计算,我们这里需要注意到片段是像素的候选者,只有通过后续的测试,片段才会成为最终显示的像素点。

6、片段着色器

        片段着色器在DirectX中也成为像素着色器(Pixel Shader)。片段着色器用来决定屏幕上像素的最终颜色。在这个阶段会进行光照计算以及阴影处理,是渲染管线高级效果产生的地方。

7、测试混合阶段

        管线的最后一个阶段是测试混合阶段。测试包括裁切测试、Alpha测试、模板测试和深度测试。没有经过测试的片段会被丢弃,不需要进行混合阶段;经过测试的片段会进入混合阶段。Alpha混合可以根据片段的alpha值进行混合,用来产生半透明的效果。Alpha表示的是物体的不透明度,因此alpha=1表示完全不透明,alpha=0表示完全透明。测试混合阶段虽然不是可编程阶段,但是我们可以通过OpenGL或DirectX提供的接口进行配置,定制混合和测试的方式。

android opengl渲染 opengl渲染cpu_图形渲染_03

        值得注意的是,半透明物体的绘制需要遵循画家算法(painter Algorithm)由远及近进行绘制,因为半透明的混合跟物体的顺序有严格的对应关系。从下面两张图我们可以看到,先绘制红色还是先绘制绿色对最终颜色的有这很大的影响。所以,绘制半透明物体之前,我们需要按照距离远近对场景中的物体进行严格排序,然而这是一个非常棘手的问题。比如,我们如何排序下面几个三角形呢?所以当进行半透明物体渲染时,一般会使用顺序无关的半透明渲染技术(Order-independent transparency,OIT)。

三、渲染管线其它划分方法

        渲染管线并非严格这样划分,不同的教材会有不同的划分方法。《Real Time Rendering》一书将渲染管线划分为以下四个阶段:应用程序阶段(Application)、几何处理阶段(Geometry Processing)、光栅化(Rasterization)和像素处理阶段(Pixel Processing)。应用阶段通常是在CPU端进行处理,包括碰撞检测、动画物理模拟以及视椎体剔除等任务,这个阶段会将数据送到渲染管线中;几何处理阶段主要执行顶点着色器、投影变换、裁剪和屏幕映射的功能;光栅化阶段和我们上面讨论的差不多,都是将图元离散化片段的过程;像素处理阶段包括像素着色和混合的功能。我们可以发现,虽然管线的划分粒度不一样,但是每个阶段的具体功能其实是差不多的,原理也是一样的,并没有太大的差异。