• OpenGL ES 3.0管线渲染流程


    OpenGL ES 3.0 实现了具有可编程着色功能的图形管线,由两个规范组成:

    • OpenGL ES 3.0 API规范
    • OpenGL ES 着色语言3.0规范

    下图概述了OpenGL ES 3.0 图像管线的各个阶段:

    在这里插入图片描述

    1、顶点缓冲区/数组对象

    顶点缓冲区在应用程序中是可选的,对于某些在整个场景中顶点数据基本不变的情况,可以在初始化阶段将顶点数据经基本处理后送入顶点缓冲区,在绘制每一帧想要的图像时就省去了顶点数据IO的步骤,直接从顶点缓冲区中获取顶点数据即可。

    例子:画三角形,在Java层构建缓冲区。

        // 开辟本地内存,目的将数据从Dalvik传进OpenGL
        var triangleCoords = floatArrayOf(     // in counterclockwise order:
            0.0f, 0.622008459f, 0.0f,      // top
            -0.5f, -0.311004243f, 0.0f,    // bottom left
            0.5f, -0.311004243f, 0.0f      // bottom right
        )
    
        private var vertexBuffer: FloatBuffer =
            // (number of coordinate values * 4 bytes per float)
            ByteBuffer.allocateDirect(triangleCoords.size * 4).run {
                // use the device hardware's native byte order
                order(ByteOrder.nativeOrder())
    
                // create a floating point buffer from the ByteBuffer
                asFloatBuffer().apply {
                    // add the coordinates to the FloatBuffer
                    put(triangleCoords)
                    // set the buffer to read the first coordinate
                    position(0)
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    2、顶点着色器

    着色器(Shader)是在GPU上运行的小程序。这些小程序为图形渲染管线的某个特定部分而运行。从基本意义上来说,着色器只是一种把输入转化为输出的程序。着色器也是一种非常独立的程序,因为它们之间不能相互通信;它们之间唯一的沟通只有通过输入和输出。

    顶点着色器描述顶点需要执行的模型变换、视变换、投影变换、光照处理的顶点着色器程序源代码/可执行文件。

    顶点着色器的输入包括:

    • 着色器程序
    • 顶点着色器输入(或者属性):输入:in(或者属性:attribute
    • 统一变量(uniform):顶点(片段)着色器使用的不变数据。因为uniform是全局的(Global),所以也可以称为全局变量
    • 采样器(simper):代表顶点着色器使用纹理变量类型

    OpenGL ES 2.0 与 3.0 输入输出变化:

    • 2.0中 attribute 输入修饰符,3.0使用 in修饰
    • 2.0中可变变量修饰符(varying), 3.0中在vertex使用 out 输出,在fragment使用 in 输入

    例子: 顶点着色器示例

    // 为着色语言版本v3.00,必须出现在第一行
    #version 300 es
    // 总变换矩阵
    uniform mat4 u_mvpMatrix; 
    // 顶点位置,2.0使用attributes修饰符表示输入,3.0使用in
    in vec4 a_Position; 
    // 顶点颜色
    in vec4 a_Color; 
    // 为片段着色器指定一个颜色输出
    out vec4 v_Color; 
    
    void main()     
    {
      // 将接收的颜色传递给片元着色器
       v_Color = a_Color; 
       // 根据总变换矩阵计算此次绘制此顶点位置
       gl_Position = u_mvpMatrix * a_Position; 
    } 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3、纹理

    纹理是一个2D图片(甚至也有1D和3D的纹理),它可以用来添加物体的细节。

    每个二维的纹理都有自己的坐空间,其范围是从一个拐点的(0,0)到另一个拐点的(1,1)。按照管理一个维度叫做S,另一个维度称为T。

    纹理坐标

    4、图元装配

    图元:是点、线、三角形等几何图形。

    图元装配的过程:

    • 图元的每个顶点被发送到顶点着色器的不同拷贝,在图元装配期间,这些顶点被组合成图元。
    • 确定图元是否位于视椎体(屏幕上可见的3D区域空间)内。如果图元没有完全在视椎体内,则需要进行裁剪;如果图元完全处于视椎体之外,它就会被抛弃;裁剪之后,顶点位置被转换为屏幕坐标。也可以执行一次淘汰操作,根据图元面向前方或者后方抛弃他们。裁剪和淘汰之后,图元便准备传递给管线下一阶段–光栅化阶段。

    所以图元装配阶段主要做了两件事:图元组装图元处理

    • 图元组装: 顶点数据被组装成完整图元。
    • 图元处理:主要就是裁剪,将处于视椎体之外的部分裁剪。

    5、光栅化

    光栅化就是将三维空间中连续的数学图形(点、直线、三角形),栅格化为二维显示平面上一个个像素点的过程。

    光栅化分为两个过程:

    • 将三维空间中的几何图元投影到二维平面上
      在这里插入图片描述

    • 将投影到二维平面上的几何图元栅格化为帧缓冲区的一个个像素
      在这里插入图片描述
      图片来源:一文详解 OpenGL ES 3.x 渲染管线

    6、片段着色器

    片段着色器主要计算像素的最终颜色,包含3D:光照,阴影,光的颜色等,用来计算最终的呈现在屏幕上的像素的颜色。
    会对光栅化阶段生成的每个片段(像素)执行着色器程序

    片段着色器输入内容有:

    • 着色器程序
    • 输入变量 – 光栅化单元用插值为每个片段生成的顶点着色器输出。
    • 统一变量 – 片段(或顶点)着色器使用的不变的数据
    • 采样器 – 代表片段着色器所用纹理的变量类型

    例子:

    // 着色器语言版本 
    #version 300 es
    // 定义所有浮点数据类型的默认精度:lowp、mediump、highp 低精度、中等精度、高精度
    // 顶点着色器默认是高精度,片段着色器基于性能和兼容性考虑,设置为中精度
    precision mediump float;
    
    in vec4 v_color; // 从顶点着色器传来的输入变量(名称相同、类型相同)  
     
    out vec4 fragColor; // 输出到的片元颜色
    
     void main(){
       // 输出的颜色为:v_color
       fragColor = v_color
     } 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    7.逐片段操作

    经过光栅化,我们已经得到了若干片段(像素)。但是这些片段(像素)还不能被直接送至帧缓冲器。比如对于物体重叠的情况,此时我们将得到若干同个位置的片段(像素),因此我们需要对这些片段(像素)进行选择。逐片段操作包含若干这样的操作:
    在这里插入图片描述

    如上图所示,在逐片段操作阶段,每个片段(像素)上执行如下操作(和测试):

    • 像素归属测试:这个测试确定帧缓冲区中位置(xw , yw)的像素目前是不是归属OpenGL ES所有,不归属就不显示。

      例如:如果一个显示OpenGL ES帧缓冲区窗口被另一个窗口所遮蔽,则被遮蔽的像素不属于OpenGL ES上下文,从而完全不显示这些像素。

    • 裁剪测试:裁剪测试确定(xw , yw)是否位于OpenGL ES 状态的裁剪矩形范围内。如果该片段(像素)位于裁剪区域之外,则被抛弃。

    • 模板和深度测试:这些测试在输入片段的模板和深度值上进行,以确定片段是否应该被拒绝。

    • 混合:混合将新生成的片段颜色值与保存在帧缓冲区(xw , yw)位置的颜色值组合起来。

    • 抖动:抖动可用于最小化因为使用有限精度在帧缓冲区中保存颜色值而产生的伪像。

    在逐片段操作后,片段或者被拒绝,或者在帧缓冲区的(Xw, Yw)位置写入普安段的颜色、深度或者模板值。写入片段的颜色、深度和模板值取决于启动的相应写入掩码。

    8、帧缓冲区

    帧缓冲是渲染结果显示到屏幕的内容缓存。

    通常情况下,程序采用双缓冲(double buffer)的形式。因为如果仅采用一个缓冲,那渲染新一帧的过程中写入和新数据与旧数据混杂,会导致画面撕裂。因此通常程序会设置两个缓冲区。前缓冲区用来保存供屏幕显示的内容,后缓冲区用于渲染程序的绘制操作。在新一帧的渲染结束之后,交换两个缓冲区的内容。这样画面撕裂问题就能得到很好的缓解。

    参考:
    LearnOpenGL-CN
    一文详解 OpenGL ES 3.x 渲染管线
    《OpenGL ES 3.0 编程指南》

  • 相关阅读:
    HDLbits exercises 2 (MODULES节选题)
    关于分布式分片,你该知道的事儿
    如何在React项目中使用TypeScript
    【无标题】
    机器学习(第二章)—— 模型评估
    cubeIDE开发,基于已有的STM32CubeMX (.ioc)创建工程文件
    计算机算法分析与设计(8)---图像压缩动态规划算法(含C++)代码
    k8s部署mysql一主两从
    uni app 打肉肉(打飞机)小游戏
    mac pro M1(ARM)安装:centos8.0虚拟机
  • 原文地址:https://blog.csdn.net/sjdjdjdjahd/article/details/126424782