(五)OpenGL ES着色语言

上一篇:(四)着色器和程序:https://www.jianshu.com/p/b1b4656cef9e

着色器是OpenGL ES 3.0 API的一个基础核心概念。每个OpenGL ES 3.0程序都需要且仅需要一个顶点着色器和一个片段着色器。

OpenGL ES着色语言基础知识

着色器的语法和C编程语言很相似,但是在版本规范原生数据类型上有重大区别。

着色器版本规范

首先第一行必须定义着色器使用的OpenGL版本,我们使用OpenGL ES 3.0:

#version 300 es

变量和变量类型

计算机图形学中,两个基本数据类型组成了变换的基础:向量和矩阵。
着色器语言中存在基于标量、向量和矩阵的数据类型,如下图:


(五)OpenGL ES着色语言_第1张图片
着色器语言数据类型.png

变量构造器

OpenGL ES着色语言在类型转换方面非常严格,不允许隐含类型转换。每种内建变量类型都有一组相关的构造器。

向量和矩阵分量

向量分量提取:

vec3 tmp = vec3(0.0, 1.0, 2.0);
// tmp.x = 0.0
// tmp.xyzx = {0.0, 1.0, 2.0 0.0}

矩阵分量提取:

mat3 tmp = mat3{ 0.0, 1.0, 2.0, // 第一列
    3.0, 4.0, 5.0, // 第二列
    6.0, 7.0, 8.0} // 第三列
// tmp[0] = {0.0, 1.0, 2.0}
// tmp[1][1] = 4.0

常量

可以将任何基本类型声明为常数变量。

const float pi =3.14159;

结构体

与C语言结构体类似:

// 定义
struct fogStruct {
    vec4 color;
    float start;
    float end;
} fogVar;

// 赋值
fogVar = fogStruct(vec4(0.0, 1.0, 0.0, 0.0), // color
    0.5, // start
    2.0); // end

数组

与C语言数组类似:

// 浮点数数组
float floatArray[4];
// 向量数组
vec4 vecArray[2];
// -------------------
float a[4] = float[](1.0, 2.0, 3.0, 4.0);
float b[4] = float[4](1.0, 2.0, 3.0, 4.0);
vec2 c[2] = vec2[2](vec2(1.0), vec2(1.0));

运算符

着色器运算符和C语言的运算符一致:


(五)OpenGL ES着色语言_第2张图片
运算符1.png

(五)OpenGL ES着色语言_第3张图片
运算符2.png

函数

着色语言限定符有三种:in,inout和out:


(五)OpenGL ES着色语言_第4张图片
着色语言限定符.png

函数定义示例,基本漫射光线的简单函数:

vec4 diffuse(vec3 normal,
vec3 light,
vec4 baseColor) {
    return baseColor * dot(normal, light); 
}

注意:OpenGL ES著色语言的函数不能递归

内建函数

下面计算基本反射照明的着色器代码:

float nDotL = dot(normal, light);
float rDotV = dot(viewDir, (2.0 * normal) * nDotL - light);
float specular = specularColor * pow(rDotV, specularPower);

上述dot和pow都是OpenGL的内建函数。

控制流语句

主要包含判断逻辑和循环逻辑:
判断:

if(color.a < 0.25) {
    color *= color.a;
} else {
    color = vec4(0.0);
}

循环:

while(){
}
// =========
do{
} while();

统一变量

统一变量(uniform)是OpenGL ES着色语言中的变量类型限定符之一。

uniform mat4 viewProjMatrix;

统一变量的命名空间在顶点着色器和片段着色器中都是共享的。即,如果顶点着色器和片段着色器都链接到一个程序对象,它们就会共享同一组变量。
统一变量通常保存在硬件中,这个区域被称为“常量存储”,其大小固定,所以统一变量个数受限。可以通过函数:

gl_MaxVertexUniformVectors() // 获取最大顶点着色器数目
gl_MaxFragmentUniformVectors() // 获取最大片元顶点着色器数目

统一变量块

统一变量块比统一变量的优势:
1、统一变量缓冲区对象可以多个程序共享,但只需要设置一次;
2、统一变量缓冲区对象一般可以存储更大量的统一变量;
3、统一变量缓冲区对象之间切换比一次单加载一个统一变量更高效。

uniform TransformBlock {
    mat4 matViewProj;
    mat3 matNormal;
    mat3 matTexGen;
};

统一变量块的内存布局限定符有shared、packed、std140、row_major和column_major:

(五)OpenGL ES着色语言_第5张图片
统一变量块的内存布局限定符.png

默认方式是(shared, column_major):

layout(shared, column_major) uniform;

顶点和片段着色器输入/输出

顶点输入变量用于指定顶点着色器种每个顶点的输入,用in关键字指定,它们通常存储位置、法线、纹理和颜色这样的数据。
顶点着色器样板:

#version 300 es
uniform mat4 u_matViewProjection;
layout(location = 0) in vec4 a_position; // 顶点位置 
layout(location = 1) in vec3 a_color; // 顶点颜色
out vec3 v_color;
void main(void) {
    gl_Position = u_matViewProjection * a_position;
    v_color = a_color;
}

具有匹配的输出/输入声明的顶点和片段着色器:

// 顶点着色器
#version 300 es
uniform mat4 u_matViewProjection;
// 顶点着色器输入
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec3 a_color;
// 顶点着色器输出
out vec3 v_color;
void main(void) {
    gl_Position = u_matViewProjection * a_position;
    v_color = a_color;
}
// ==============================
// 片元着色器
#version 300 es
precision mediump float;
// 来自顶点着色器的输入
in vec3 v_color;
// 片元着色器输出
layout(location = 0) out vec4 o_fragColor;
void main() {
    o_fragColor = vec4(v_color, 1.0);
}

插值限定符

OpenGL ES 3.0主要包含2种插值器:
平滑着色:smooth
平面着色:flat
上述两种插值器还可以用质心采样:centroid来修饰。

预处理器和指令

预处理器:
类似C++预处理器,可以使用如下指令定义宏和条件测试:

#define
#undef
#if
#ifdef
#ifndef
#else
#elif
#endif

扩展行为指令:

extension指令用于启用和设置扩展行为。下述示例即希望预处理器在

NVIDIA阴影采样器立方体扩展不受支持时产生警告:

#extension GL_NV_shadow_samplers_cube : enable

统一变量和插值器打包

底层硬件种可用于每个变量存储的资源是固定的。我们采用打包规则来节省程序所需内存空间。

打包规则规定了插值器和统一变量映射到物理存储空间的方式,它基于物理存储空间被组织为一个存储位置4列和1行的网格的概念。举下面的统一变量块为例说明:

uniform mat3 m;
uniform float f[6];
uniform vec3 v;

未打包情况:


(五)OpenGL ES着色语言_第6张图片
未打包情况.png

打包情况:


(五)OpenGL ES着色语言_第7张图片
打包情况.png

因为GPU通常会按照向量位置索引对常量存储进行所以。打包必须使数组跨越行边界。

了解打包很重要,这样才能编写再任何OpenGL ES 3.0实现上都不超过最小允许存储的可移植着色器。

精度限定符

着色器变量可以声明为低、中和高。

// highp  高精度
// mediump 中精度
// lowp 低精度
highp vec4 position;

也可以用精度限定符指定某种类型的精度:

precision highp float; // 所有float都是高精度 32bit
precision mediump float; // 所有float都是中精度 16bit
precision lowp float; // 所有float都是低精度 10bit

不变性

OpenGL ES着色器语言种引入了invariant关键字可以用于任何可变的顶点着色器输出,可以避免编译器可能进行导致指令重新排序的优化。
一旦某个输出变量声明了不变性,编译器便保证相同的计算和着色器输入条件下结果相同。
警告:慎用invariant,它回导致性能下降。

小结

上一篇:(四)着色器和程序:https://www.jianshu.com/p/b1b4656cef9e

你可能感兴趣的:((五)OpenGL ES着色语言)