OpenGL ES 顶点着色器内建变量和矩阵变换

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/afei__/article/details/89520350

一、简介

顶点着色器用于基于顶点的操作,例如通过矩阵变换位置、计算照明方程式以生成逐顶点的颜色以及生成或者变换纹理坐标。

常见概念:

  • 属性:用顶点数组提供的顶点数据(关键字:attribute/in)
  • 输出:在图元光栅化阶段为每个生成的片段计算顶点着色器的输出,并作为片段着色器的输入(关键字:varying/out)
  • 统一变量:顶点着色器使用的不变数据(关键字:uniform)
  • 采样器:顶点着色器使用纹理的特殊统一变量类型(关键字:sampler2d/sampler3d)
  • 着色器程序:着色器源代码或可执行文件

二、内建变量

1. 内建特殊变量

变量 描述
gl_VertexID 输入变量,用于保存顶点的整数索引。highp 精度的整数变量
gl_InstanceID 输入变量,用于保存实例化绘图调用中图元的实例编号。highp 精度的整数变量,通常情况下为 0
gl_Position 输出变量,用于输出顶点作为的裁剪坐标。highp 精度的浮点变量
gl_PointSize 用于指定点精灵的尺寸,单位为像素。highp 精度的浮点变量
gl_FrontFacing 不由顶点着色器直接写入,而是根据顶点着色器生成的位置值和渲染的图元类型生成,它是一个布尔变量

2. 内建统一状态

顶点着色器中唯一的内建统一状态是窗口坐标中的深度范围,由内建统一变量名 gl_DepthRange 给出,该变量声明为 gl_DepthRangeParameters 类型的统一变量

struct gl_DepthRangeParameters {
    highp float near; // near z
    highp flaot far; // far z
    highp float diff; // far - near
}

uniform gl_DepthRangeParameters gl_DepthRange;

3. 内建常量

在所有 ES 3.0 实现中,如下常量都有支持的最小值,但实际值需要使用指定接口查询。

类型 名称 最小值 描述
const mediump int gl_MaxVertexAttribs 16 顶点着色器中属性的最大数量
const mediump int gl_MaxVertexUniformVectors 256 顶点着色器中统一变量的最大数量
const mediump int gl_MaxVertexoutputVectors 16 顶点着色器中输出向量的最大数量
const mediump int gl_MaxTextureImageUnits 16 顶点着色器中纹理单元的最大数量
const mediump int gl_MaxCombinedTextureImageUnits 32 顶点着色器和片段着色器中纹理单元最大数量的总和

三、统一变量数量限制

上面我们知道 gl_MaxVertexUniformVectors 描述了顶点着色器统一变量的最大值,统一变量存储如下变量:

  • uniform 限定符声明的变量
  • const 限定符声明的常数变量
  • 字面值(如 0.0 或者 1.0 等)
  • 特定于实现的常量

对于字面值,ES 3.0 规范规定不做任何常量传播。结果是一个字面值的多个实例将被计算多次,即我们应该声明相应的常数变量代替字面值,可避免同一个字面值计算多次。

示例

如下一个顶点着色器:

#version 300 es
#define NUM_TEXTURES 2

uniform mat4 tex_matrix[NUM_TEXTURES];
uniform bool enable_tex[NUM_TEXTURES];
uniform bool enable_tex_matrix[NUM_TEXTURES];

in vec4 a_texcoord0; // available if enable_tex[0] is true
in vec4 a_texcoord1; // available if enable_tex[1] is true

out vec4 v_textcoord[NUM_TEXTURES];

void main() {
    v_textcoord[0] = vec4(0.0, 0.0, 0.0, 1.0);
    if (enable_tex[0]) {
        if (enable_tex_matrix[0]) {
            v_textcoord[0] = tex_matrix[0] * a_texcoord0;
        } else {
            v_textcoord[0] = a_texcoord0;
        }
    }
    
    v_textcoord[1] = vec4(0.0, 0.0, 0.0, 1.0);
    if (enable_tex[1]) {
        if (enable_tex_matrix[1]) {
            v_textcoord[1] = tex_matrix[1] * a_texcoord1;
        } else {
            v_textcoord[1] = a_texcoord1;
        }
    }
}

上例中字面值 0、1、0.0、1.0 的每次引用都会被计入统一变量存储,为了保证这些字面值只计数一次,可以使用常量变量代替,例如:

#version 300 es
#define NUM_TEXTURES 2

uniform mat4 tex_matrix[NUM_TEXTURES];
uniform bool enable_tex[NUM_TEXTURES];
uniform bool enable_tex_matrix[NUM_TEXTURES];

in vec4 a_texcoord0; // available if enable_tex[0] is true
in vec4 a_texcoord1; // available if enable_tex[1] is true

out vec4 v_textcoord[NUM_TEXTURES];

const int zero = 0;
const int one = 1;

void main() {
    v_textcoord[zero] = vec4(float(zero), float(zero), float(zero), float(one));
    if (enable_tex[zero]) {
        if (enable_tex_matrix[zero]) {
            v_textcoord[zero] = tex_matrix[zero] * a_texcoord0;
        } else {
            v_textcoord[zero] = a_texcoord0;
        }
    }
    
    v_textcoord[one] = vec4(float(zero), float(zero), float(zero), float(one));
    if (enable_tex[one]) {
        if (enable_tex_matrix[one]) {
            v_textcoord[one] = tex_matrix[one] * a_texcoord1;
        } else {
            v_textcoord[one] = a_texcoord1;
        }
    }
}

四、矩阵变换

1. 使用矩阵变换位置的顶点着色器

#version es 300

unidorm mat4 u_mvpMatrix // 把坐标从模型空间转换到裁剪空间的矩阵

layout(location = 0) in vec4 a_position; // 输入的位置值
layout(location = 1) in vec4 a_color; // 输入的颜色值

out vec4 v_color; // 输出的颜色值,将是片段着色器的输入值

void main() {
    v_color = a_color;
    gl_Position = u_mvpMatrix * a_position;
}

上例中的统一变量 u_mvpMatrix 引入了“模型-视图-投影(MVP)”矩阵的概念。顶点着色器的位置输入保存为物体坐标,而输出位置保存为裁剪坐标。MVP 矩阵是 3D 图形中进行这种变换的 3 个非常重要的变换矩阵的乘积:模型矩阵、试图矩阵和投影矩阵。

相关概念:

  • 模型矩阵:将物体坐标变换为世界坐标
  • 视图矩阵:将世界坐标变换为眼睛坐标
  • 投影矩阵:将眼睛坐标变化为裁剪坐标
  • 模型-视图矩阵:模型和视图矩阵合并为一个矩阵,将顶点位置从物体坐标变换为眼睛坐标,组合了从物体到世界坐标和世界坐标到眼睛坐标的变化。

2. 示例

常见一个MVP矩阵产生的过程为:

private final float[] mProjectionMatrix = new float[16]; // 投影矩阵
private final float[] mModelMatrix = new float[16]; // 模型矩阵
private final float[] mViewMatrix = new float[16]; // 视图矩阵

private final float[] mViewProjectionMatrix = new float[16];
private final float[] mModelViewProjectionMatrix = new float[16]; // MVP矩阵

// 1.投影矩阵
// 以45度的视野创建一个投影矩阵,视锥体从z值为-1的位置开始,在z值为-10的位置结束
Matrix.perspectiveM(mProjectionMatrix, 0, 45, (float) width / (float) height, 1f, 10f);

// 2.视图矩阵
// 把眼睛设为(0,1.2,2.2)的位置,即眼睛在x-z平面三方1.2个单位,向后2.2个单位
Matrix.setLookAtM(mViewMatrix, 0, 0f, 1.2f, 2.2f, 0f, 0f, 0f, 0f, 1f, 0f);

// 3. 模型矩阵
// 初始化模型矩阵为一个单位矩阵
Matrix.setIdentityM(mModelMatrix, 0);
// 将模型沿着z轴平移-2个单位,使得我们可以看见它
Matrix.translateM(mModelMatrix, 0, 0f, 0f, -2f);
// 将模型绕着x轴旋转-60度
Matrix.rotateM(mModelMatrix, 0, -60, 1f, 0f, 0f);

// 4.MVP矩阵
// 将投影和视图矩阵相乘,并缓存到mViewProjectionMatrix中
Matrix.multiplyMM(mViewProjectionMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
// 将模型矩阵和上述ViewProjectionMatrix矩阵相乘,即是MVP矩阵
Matrix.multiplyMM(mModelViewProjectionMatrix, 0, mViewProjectionMatrix, 0, mModelMatrix, 0);

猜你喜欢

转载自blog.csdn.net/afei__/article/details/89520350