Android OpenGL

OpenGL ES渲染管线(渲染流程)

2018-03-20  本文已影响89人  sellse

渲染管线

苹果提供了两种OpenGL ES的可视化模型,一种是客户端—服务端的架构模型,另一种就是管线的模型。

OpenGL ES client-server architecture
客户端—服务端架构:应用程序状态一旦更改,纹理、顶点数据以及渲染命令都将传递给OpenGL ES客户端。客户端将这些数据转换成图形硬件可以理解的格式,发送给GPU。这个过程增加了app图形性能的开销。为了获得好的性能需要管理好这种开销。一个好的设计需要减少OpenGL ES调用的频率,使用适合硬件的数据格式来减少转换成本,并且管理好自身与OpenGL ES的数据流。
管线架构:另外就是本文将要说的管线架构,由于管线中每个独立阶段都依赖于上一阶段的产出,所以任何阶段的工作量太大或者运行太慢,其他阶段都会被迫闲置以等待前一阶段的完成。好的设计会根据硬件功能平衡每个阶段的执行工作。
OpenGL ES graphics pipeline

在 OpenGL ES 1.0 版本中,支持固定管线(fixed-function pipeline),有一系列固定的函数用来在屏幕上渲染对象,而不是创建一个单独的程序来指导 GPU 的行为。这样有很大的局限性,你不能做出任何特殊的效果。如果想知道着色器在工程中可以造成怎样的不同,看看这篇 Brad Larson 写的他用着色器替代固定函数重构 Molecules 应用的博客
而 OpenGL ES 2.0 版本不再支持固定管线,只支持可编程管线。

什么是管线?什么又是固定管线和可编程管线?

管线(pipeline):也称渲染管线,因为 OpenGL ES在渲染处理过程中会顺序执行一系列操作,这一系列相关的处理阶段就被称为OpenGL ES 渲染管线。OpenGL ES 渲染过程就如流水线作业一样,这样的实现极大地提高了渲染的效率。如图就是 OpenGL ES 的管线图,学习OpenGL ES 就是学习这张图中的每一个部分。

图中阴影部分的 Vertex Shader 和 Fragment Shader 是可编程管线。可编程管线就是说这个操作可以动态编程而不必写死在代码中。可动态编程实现这一功能一般都是脚本提供的,在OpenGL ES 中也一样,编写这样脚本的能力是由着色语言GLSL提供的。那可编程管线有什么好处呢?方便我们动态修改渲染过程,而无需重写编译代码,当然也和很多脚本语言一样,调试起来不太方便。

渲染管线中的各个模块

Vertex Shader

由图可见,顶点着色器分输入输出两部分。顶点着色器定义了在 2D 或者 3D 场景中几何图形是如何处理的,实现了顶点操作的通用可编程方法。一个顶点指的是 2D 或者 3D 空间中的一个点。在图像处理中,有 4 个顶点:每一个顶点代表图像的一个角。顶点着色器设置顶点的位置,并且把位置和纹理坐标这样的参数发送到片段着色器。
输入:

Primitive Assembly图元装配(还有啥好的翻译?)

图元(Primitive):OpenGL ES 支持三种基本图元:点,线和三角形,它们是可被 OpenGL ES 渲染的。经过着色器处理之后的顶点在这一阶段被装配为基本图元。对于每个图元,必须确定图元是否位于视锥体(屏幕上可见的3D空间区域)内,保留完全在视锥体中的图元,丢弃完全不在视锥体中的图元,对一半在一半不在的图元进行裁剪。裁剪之后,顶点位置就被转换成了屏幕坐标。也可以再对在视锥体中的图元进行剔除(cull):这个过程可编码来决定是剔除正面,背面还是全部剔除。裁剪和剔除之后,图元便准备传递给管线的下一个阶段——光栅化阶段。

Rasterization

在光栅化阶段,基本图元被转换为一组二维的片元(fragment),fragment 表示可以被渲染到屏幕上的像素,它包含位置,颜色,纹理坐标等信息,这些值是由图元的顶点信息进行插值计算得到的。这些片元接着被送到片元着色器中处理。这是从顶点数据到可渲染在显示设备上的像素的质变过程。

Fragment Shader

GPU 使用片元着色器在对象或者图片的每一个像素上进行计算,处理由光栅化阶段生成的每个片元,最终计算出每个像素的最终颜色。图片,归根结底,实际上仅仅是数据的集合。图片的文档包含每一个像素的各个颜色分量和像素透明度的值。因为对每一个像素,算式是相同的,GPU 可以流水线作业这个过程,从而更加有效的进行处理。使用正确优化过的着色器,在 GPU 上进行处理,将获得百倍于在 CPU 上用同样的过程进行图像处理的效率。

顶点着色器与片元着色器的编程区别

使用顶点着色器和片元着色器

可编程管线通过用 GLSL 语言编写脚本文件实现的,这些脚本文件相当于 C 源码,有源码就需要编译链接,因此需要对应的编译器与链接器,shader 对象与 program 对象就相当于编译器与链接器。shader 对象载入源码,然后编译成 object 形式(就像C源码编译成 .obj文件)。经过编译的 shader 就可以装配到 program 对象中,每个 program对象必须装配两个 shader 对象:一个顶点 shader,一个片元 shader,然后 program 对象被连接成“可执行文件”,这样就可以在 render 中运行该“可执行文件”了。

render方法:

- (void)render {
    glClearColor(0.5, 1.0, 0.5, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    // Setup viewport
    glViewport(0, 0, self.frame.size.width, self.frame.size.height);

    GLfloat vertices[] = {
        0.0f,  0.5f, 0.0f, 
        -0.5f, -0.5f, 0.0f,
        0.5f,  -0.5f, 0.0f };

    // Load the vertex data
    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices );
    glEnableVertexAttribArray(_positionSlot);

    // Draw triangle
    glDrawArrays(GL_TRIANGLES, 0, 3);

    [_context presentRenderbuffer:GL_RENDERBUFFER];
}

在新增的代码中,第一句 glViewport 表示渲染 surface 将在屏幕上的哪个区域呈现出来,然后我们创建一个三角形顶点数组,通过 glVertexAttribPointer 将三角形顶点数据装载到 OpenGL ES 中并与 vPositon 关联起来,最后通过 glDrawArrays 将三角形图元渲染出来。


绘制三角形

代码存放在绘制一个三角形中New Group文件夹

OpenGL ES渲染管线与着色器
OpenGL ES 3.0编程指南
Apple文档——OpenGL ES Design Guidelines
Notes on OpenGL ES Graphics Pipeline
GPU 加速下的图像处理

上一篇 下一篇

猜你喜欢

热点阅读