OpenGL ES 3.0之Shading Language(八)

每一个OpenGL ES 3.0程序要求一个顶点着色器和一个片断着色器去渲染一个图形。着色器概念是API 的中心,本篇将介绍着色器语言部分包含下面几项编程

  一、变量和变量类型
  二、矢量和矩阵建立及选择
  三、常量
  四、结构和阵列
  五、运算符、流控制和函数
  六、属性、只读变量和变量
  七、预处理和指令
  八、只读变量和变量压缩
  九、精度控制和不变性数组

1、变量和变量类型编程语言

  计算机图形学中,转换有两种基本的数据类型:矢量和矩阵。下图是OpenGL ES 着色器编程语言数据类型ide

                        

 

 变量能够在声明时初始化,或之后初始化,初始化是经过构造函数进行,也可作类型转换。函数

        

float myFloat = 1.0;
float myFloat2 = 1; // ERROR: invalid type conversion
bool myBool = true;
int myInt = 0;
int myInt2 = 0.0; // ERROR: invalid type conversion
myFloat = float(myBool); // Convert from bool -> float
myFloat = float(myInt); // Convert from int -> float
myBool = bool(myInt); // Convert from int -> bool

矢量一样能够转换工具

  

vec4 myVec4 = vec4(1.0); // myVec4 = {1.0, 1.0, 1.0,
// 1.0}
vec3 myVec3 = vec3(1.0,0.0,0.5); // myVec3 = {1.0, 0.0, 0.5}
vec3 temp = vec3(myVec3); // temp = myVec3
vec2 myVec2 = vec2(myVec3); // myVec2 = {myVec3.x,
// myVec3.y}
myVec4 = vec4(myVec2, temp); // myVec4 = {myVec2.x,
// myVec2.y,
// temp.x, temp.y}

矩阵转换优化

  

mat3 myMat3 = mat3(1.0, 0.0, 0.0, // First column
0.0, 1.0, 0.0, // Second column
0.0, 1.0, 1.0); // Third column

 

2、矢量和矩阵元素spa

矩阵元素可以经过两种方式获取,使用“.”操做符或者数组下标。依据被给的元素的组成,每一个被给的矩阵都能使用{x, y, z, w}, {r, g, b, a},或{s, t, r, q}表示。使用三种不一样的命名表是由于有三种坐标顶点、颜色和贴图。x, r, 或s 表示矩阵里的第一个元素,不一样的命名方式仅仅是为了使用方便。或者说你可使用矩阵时混合使用矩阵命名方式,(但不能使用.xgr,只能一次使用一种命名规则)。当使用“.”时,你也能够从新排列一个矩阵。例如,scala

  

vec3 myVec3 = vec3(0.0, 1.0, 2.0); // myVec3 = {0.0, 1.0, 2.0}
vec3 temp;
temp = myVec3.xyz; // temp = {0.0, 1.0, 2.0}
temp = myVec3.xxx; // temp = {0.0, 0.0, 0.0}
temp = myVec3.zyx; // temp = {2.0, 1.0, 0.0}

矩阵也可使用[]操做符,在这种下标模式[0]表明x, [1]表明y。矩阵被认为是多个矢量组成的,例如mat2 被考虑是两个vec2s,mat3 是3 个vec3s。对矩阵,单独的列被使用列下标[]选中。下面是例子:code

mat4 myMat4 = mat4(1.0); // Initialize diagonal to 1.0
(identity)
vec4 colO = myMat4[0]; // Get colO vector out of the matrix
float ml_l = myMat4[1][1]; // Get element at [1][1] in matrix
float m2_2 = myMat4[2].z; // Get element at [2][2] in matrix

 

3、常量

  常量是在着色器中不可改变的数据类型。使用const修饰,必须在声明时初始化。

const float zero = 0.0;
const float pi = 3.14159;
const vec4 red = vec4(1.0, 0.0, 0.0, 1.0);
const mat4 identity = mat4(1.0);

 

4、结构和数组

   结构

 像C 语言同样,能够集合几种变量成为结构。在OpenGL ES 结构以下:

struct fogStruct
{
    vec4 color;
    float start;
    float end;
}  fogVar;

这将产生新的变量类型fogStruct,和新的变量名fogVar。使用可以初始化构造函数变量。定义一个结构类型,定义一个结构,一个同名的构造函数也被定义,必须是一对一的。上面的结构可以被使用下面的语法初始化:

 

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

结构的构造基于结构的类型,它把每一个成员作输入参数。访问结构中的元素和C 语言相同。

vec4 color = fogVar.color;
float start = fogVar.start;
float end = fogVar.end;

  

  数组

OpenGL ES 数组语法和C 语言很是相似,索引以0 开始。下面是建立数组的例子:

 

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));

 

5、操做符

 

这些运算符使用和C 语言很是相似。但OpenGL ES 语法有严格的语法限制,执行运算符的变量必须有相同的类型,二进制运算符(*, /, +, -)必须是浮点变量或者是整型变量。乘运算符可以在浮点、矢量、矩阵的组合中运行。例如:

 

float myFloat;
vec4 myVec4;
mat4 myMat4;
myVec4 = myVec4 * myFloat; // Multiplies each component of myVec4
// by a scalar myFloat
myVec4 = myVec4 * myVec4; // Multiplies each component of myVec4
// together (e.g., myVec4 ^ 2 )
myVec4 = myMat4 * myVec4; // Does a matrix * vector multiply of
// myMat4 * myVec4
myMat4 = myMat4 * myMat4; // Does a matrix * matrix multiply of
// myMat4 * myMat4
myMat4 = myMat4 * myFloat; // Multiplies each matrix component by
// the scalar myFloat

比较运算符(==, !=, <, etc.)仅可以执行标量,矢量有专门的比较函数

 

6、函数

函数声明和C 语言同样,函数使用前,必须定义,它的原型必须给出。使用时很是相似C 语言。不一样是参数使用上,提供特殊的变量限定词,指示变量是否可以被函数修改。那些限定词以下表:

使用以下:

vec4 myFunc(inout float myFloat, // inout parameter
out vec4 myVec4, // out parameter
mat4 myMat4); // in parameter (default)

散射光计算函数。

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

OpenGL ES 函数不能递归,缘由是一些编译工具执行这个函数时,这会让这个函数在线执行,最后使GPU 产生问题。

着色器语言也提供了内置函数。下面的例子是,在片断着色器中计算基本反射光的着色器代码。

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

 

7、流控制声明

   控制语句语法和C 相似,if-then-else 逻辑也被使用,例如:

  

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

条件表达式的结果必须是布尔值。或者说表达式基于布尔值计算,或者计算结果是布尔值(例如比较运算)。

 

8、Uniforms

  Uniform

  是变量类型的一种修饰符。。uniform 是OpenGL ES  中被输入着色器的只读值。。uniform被使用存储各类着色器须要的数据,例如:转换矩阵、光照参数或者颜色。基本上各类输入着色器的常量参数像顶点和片断(但在编译时并不知道)应该是uniform。uniform 应该使用修饰词被声明为全局变量,以下:

uniform mat4 viewProjMatrix;
uniform mat4 viewMatrix;
uniform vec3 lightPosition;

uniform 的空间被顶点着色器和片断着色器分享。也就是说顶点着色器和片断着色器被连接到一块儿进入项目,它们分享一样的uniform。所以一个在顶点着色器中声明的uniform,至关于在片断着色器中也声明过了。当应用程序装载uniform 时,它的值在顶点着色器和片断着色器均可用。

另外一个须要注意的是,uniform 被存储在硬件被称为常量存储,这是一种分配在硬件上的存储常量值的空间。由于这种存储须要的空间是固定的,在程序中这种uniform 的数量是受限的。这个限制能经过读gl_MaxVertexUniformVectors 和gl_MaxFragmentUniformVectors编译变量得出。( 或者用GL_MAX_VERTEX_UNIFORM_VECTORS 或GL_MAX_FRAGMENT_UNIFORM_ VECTORS 为参数调用glGetIntegerv)OpenGL ES 3.0必须至少提供256 个顶点着色器uniform 和224个片断着色器uniform。但能够更多,

  Uniform Blocks

  

uniform TransformBlock
{
mat4 matViewProj;
mat3 matNormal;
mat3 matTexGen;
};
layout(location = 0) in vec4 a_position;
void main()
{
gl_Position = matViewProj * a_position;
}

 9、属性

  OpenGL ES 着色器语言的另外一个变量是属性。属性变量仅仅在顶点着色器中被使用,逐顶点的指定顶点着色器的输入。典型的被用来储存位置、法线、贴图坐标和颜色数据。关键是懂得属性是每一个顶点被绘制的详细数据。它其实是着色器的使用者决定什么数据是属性。

uniform mat4 u_matViewProjection;
attribute vec4 a_position;
attribute vec2 a_texCoord0;
varying vec2 v_texCoord;
void main(void)
{
    gl_Position = u_matViewProjection * a_position;
    v_texCoord = a_texCoord0;
}

  像uniform 同样,硬件对顶点着色器的属性变量数量有限制。最大的属性数经过工具编译支持gl_MaxVertexAttribs 肯定。(或者使用GL_MAX_VERTEX_ATTRIBS 为参数调用glGetIntegerv 查询)最小数是8。若是为确保你的程序能在任何OpenGL ES 2.0 工具上编译,确保你的属性不超过8。

变量被用来存储顶点着色器的输出和片断着色器的输入。基本上每一个顶点着色器把输出数据转变成一个或更多片断着色器的输入。那些变量也被片断着色器声明(类型需匹配),而且在光栅化阶段被线性插补变成图元。

varying vec2 texCoord;
varying vec4 color;

  变量也有数量的限制(硬件上它们被用来作插补)。工具支持的最大数目是gl_MaxVaryingVectors ( 使用GL_MAX_VARYING_VECTORS 为参数调用glGetIntegerv 查询)。最大数目是8.

  下面是顶点着色器和片断着色器的变量如何匹配的声明

 

// Vertex shader
uniform mat4 u_matViewProjection;
attribute vec4 a_position;
attribute vec2 a_texCoord0;
varying vec2 v_texCoord; // Varying in vertex shader
void main(void)
{
    gl_Position = u_matViewProjection * a_position;
    v_texCoord = a_texCoord0;
}

 

// Fragment shader
precision mediump float;
varying vec2 v_texCoord; // Varying in fragment shader
uniform sampler2D s_baseMap;
uniform sampler2D s_lightMap;
void main()
{
    vec4 baseColor;
    vec4 lightColor;
    baseColor = texture2D(s_baseMap, v_texCoord);
    lightColor = texture2D(s_lightMap, v_texCoord);
    gl_FragColor = baseColor * (lightColor + 0.25);
}

 

10、预处理指令

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

  __LINE__ // 被着色器当前的行数取代
  __FILE__ // 在 OpenGL ES 中老是0
  __VERSION__ // OpenGL ES 着色器语言版本 (e.g., 100)
  GL_ES // 被 ES 着色器定义为值1

其余

  精度控制符,lowp,highp,mediump.对全部基于浮点的变量默认的精度是浮点值,基于整型的变量默认的精度是整型。在着色器中, 若是没有定义指定精度,默认对int 和float 的精度都是高。换句话说在顶点着色器中没有指定进度控制的变量将是高质量。而片断着色器规则却不一样。对浮点值没有默认的精度控制。每一个着色器必须声明默认的着色器浮点精度或指定每一个浮点变量的精度。即OpenGL ES 2.0 不要求片断着色器支持高精度。决定是否高精度被支持是片断着色器是否认义了GL_FRAGMENT_PRECISION_HIGH 宏(和工具输出OES_fragment_precision_high 扩展字符串)。

 

#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif

 

  最后要讨论的题目是不变性。在OpenGL ES 着色器编程语言里,invariant 是被用于任何顶点着色器的变量输出的关键字。它意味着什么和为何须要他呢。着色器被编译时可能进行优化,一些指令被从新整理。指令从新整理意味着两个着色器之间平等的计算不保证产生相同的结果。这是个问题在多通道着色器特殊状况下,依稀物体使用透明混合来绘制时。若是精度被使用来计算输出位置是不许确同样,精度的不一样将致使artifacts。这在Z fighting状况下常常发生。每一个像素很小的Z 精度不一样引发了不一样的反光。下面的例子显示了进行多通道渲染时invariance 对获得正确的结果是很是重要的。下面的圆环被绘制用两种方式。片断着色器先计算镜面反射光,再计算环境光和散射光。顶点着色器不使用invariance,所以小的精度不一样引发了Z 光在图

 

 

invariant gl_Position;
invariant varying texCoord;

一旦输出被宣布为invariance,一样的计算输入相同,编译器保证输出结果相同。例如你有两个顶点着色器使用多通道图像投射矩阵依据输入计算输出。你能保证那些位置是invariance。

 

 

uniform mat4 u_viewProjMatrix;
attribute vec4 a_vertex;
invariant gl_Position;
void main
{
//
    gl_Position = u_viewProjMatrix * a_vertex; // Will be the same
// value in all
// shaders with the
// same viewProjMatrix
// and vertex
}
相关文章
相关标签/搜索