OpenGL GLSL语言

GLSL - OpenGL Shading Language也称作GLslang,是一个以C语言为基础的高阶着色语言。为开发者提供对绘图管线更多的直接控制,而无需使用汇编语言或硬件规格语言。

GLSL类型

OpenGL着色器语言规格定义了 22 个基本变量类型,大部分用法与C语言相同,其它的是绘图处理器特有的。

  • void – 用于没有返回值的函数
  • bool – 条件类型,其值可以是真或假
  • int – 有符号的整数
  • float – 浮点数
  • vec2 – 2 个浮点数组成的向量
  • vec3 – 3 个浮点数组成的向量
  • vec4 – 4 个浮点数组成的向量
  • bvec2 – 2个布尔类型数组成的向量
  • bvec3 – 3个布尔类型数组成的向量
  • bvec4 – 4个布尔类型数组成的向量
  • ivec2 – 2个整数组成的向量
  • ivec3 – 3 个整数组成的向量
  • ivec4 – 4 个整数组成的向量
  • mat2 – 浮点数的2x2矩阵
  • mat3 – 浮点数的3x3矩阵
  • mat4 – 浮点数的4x4矩阵
  • sampler1D – 用来存取一维纹理的采样器
  • sampler2D – 用来存取二维纹理的采样器
  • sampler3D – 用来存取三维纹理的采样器
  • samplerCube – 用来存取立方映射纹理的采样器
  • sampler1Dshadow – 用来存取一维深度纹理的采样器
  • sampler2Dshadow – 用來存取二维深度纹理的采样器

从上面的类型可以看出,GLSL着色器语言是由标量、向量、矩阵、采样器等基本数学数据元素类型组成的。这也是数学描素图像学的基本数据类型。当然和C语言一样,GLSL语言也有能力用基本数据类型构成复杂的数据。例如:支持结构和数组。

注意:GLSL语言不支持双精度浮点型、字节类型、短整型、长整型。并且没有联合体、枚举类型、无符号整型以及位运算等特性。

数据类型的使用

GLSL语言数据类型的使用和C语言类似,都先声明再使用,有使用域,变量初始化也符合C语言语法。下面说一下C语言没有的类型初始化和引用示例:

vec4 va = vec4(1.0,2.0,3.0,4.0);
vec3 vb = va.rgb; // 颜色相关 argb
vec3 vc = va.xyz; // 坐标相关 xyzw
vec4 vd = va.stuv // 纹理相关 stuv
vec4 vf = va.aagg // 可以组合,但只能同类型组合
mat3 ma = mat3(1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0);
mat3 mb = mat3(1.0) // 1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0;

特殊的内建变量

着色器代码的开发中会使用到很多变量,其中大部分可能是由开发人员根据需求自定义的,但着色器也提供了一些用来满足特定需求的内建变量,这些内建变量不需要声明即可使用,一般用来实现渲染管线固定功能部分与自定义顶点或片元着色器之间的信息交互。

内建变量根据信息传递的方向可以分为两类:输入与输出变量。输入变量负责将渲染管线中固定功能部分产生的信息传递进着色器,输出变量负责将着色器产生的信息传递给渲染管线中固定功能部分。

内建输入变量

gl_Position
gl_PointSize
gl_FragCoord
gl_FrontFacing

内建输出变量

gl_FragColor
gl_FragData

限定符

和其它编程语言一样,着色器中对变量也有很多可选的限定符,这些限定符中大部分只能用来修饰全局变量

限定符 说明
attribute 一般用于每个顶点各不相同的量,如顶点位置、颜色等
uniform 一般用于对同一组顶点组成的单个3D物体中所有顶点都相同的量,如当前的光源位置
varying 一般用于顶点着色器传递给片元着色器的量
const 用于声明常量
uniform mat4 uMVPMatrix;
attribute vec3 aPosition;
varying vec4 vColor;
const int lightsCount = 4;

限定符在使用时应该放在变量类型之前,且使用attribute、uniform以及varying限定符修饰的变量必须为全局变量。同时要注意,着色器语言中没有默认限定符的概念,因此如果有需要,必须为全局变量明确指定需要的限定符。

attribute变量

attribute变量用来接收渲染管线传递进顶点着色器的待处理顶点的各种属性,用于描述顶点的各项特征,如顶点坐标、法向量、颜色、纹理坐标等。

用attribute限定符修饰的变量其值是由宿主程序 (可以是C、java)批量传入渲染管线的,渲染管线进行基本处理后再传给顶点着色器。数据中有多少顶点,管线就调用多少次顶点着色器,每次将顶点的各种属性数据传递给顶点着色器中对应的attribute变量。

attribute限定符只能用于顶点着色器,不能在片元着色器中使用,且attribute限定符只能用来修饰浮点数标量、浮点数向量及矩阵变量,也就是说它只能用来修饰浮点数。

attribute限定符修饰的变量其值是由宿主程序批量传入渲染管线中的,相关代码如下:

int positionHandle;
positionHandle = GLES20.glGetAttributeLocation(mProgram,"aPosition");
GLE20.glVertextAttributePointer(
     positionHandle,
     3,
     GLES20.GL_FLOAT,
     false,
     3*4,
     mVertexBuffer
);

uniform限定符

uniform为一致变量限定符,一致变量指的是对同一组顶点组成的单个3D物体中所有顶点都相同的量。uniform可以用在顶点着色器或片元着色器中,其支持用来修饰所有的基本数据类型。与属性变量类似,也是宿主程序传入的。

uniform mat4 uMVPMatrix;
uniform vec3 uLightLocation;
uniform vec3 uCameraPosition;
int mvpMatrixHandle = GLES20.glGetUniformLocation(mProgram,"uMVPMatrix");
GLES20.glUniformMatrix4fv(mvpMatrixHandle,1,false,mMatrix,0);

一致变量类型的不同将值传入渲染管线的方法也有所不同,这些方法的名称都以glUniform开头,然后变量数据支持的数据和类型。

varying变量

想要将顶点着色器的信息传入到片元着色器中,就必须使用varying限定符。易变变量是顶点着色器和片元着色器之间的动态接口。

// vertex shader
uniform mat4 uMVPMatrix;
attribute vec aPosition;
attribute vec4 aColor;
varying vec4 vColor;
main() {
    gl_Position = uMVPMatrix * vec4(aPosition,1);
    vColor = aColor
}

// fragment shader
precision mediump float;
varying vec4 vColor;
void main() {
    gl_FragColor = vColor;
}

上面GLSL代码中,顶点着色器的易变变量vColor,和片元着色器的易变变量vColor,就是顶点着色器和片元着色器之间的动态接口。

顶点着色器的vColor是每个顶点的颜色,但片元着色器的vColor并不是某个顶点赋的特定值。片元是通过插值得到的,片元数量远大于顶点,不是一个数量级。也就是说,对顶点赋予的颜色值,会插成很多值,并不是一一对应的。这也易变变量本质所在。

一般情况下对一个3D物体进行渲染,片元着色器执行次数会大大超过顶点着色器,因此片元着色器硬件单元数量会多于顶点着色器的,通过并行处理,提高渲染速度。我们开发程序也要注意,能在顶点着色器处理的效果算法,尽管不要在片元着色器中处理。

const限定符

用const修饰的变量是不可变的,和c语言中的const类似。只不过这些常量在着色器外部是不可见的。

算法

程序就是数据加算法。上面介绍了GLSL支持的数据类型,就可以构造数据了。算法就是对数据的操作,是符合C语言的。运算符、控制流程、函数等都和C语言类似。只不过为了运算的高效,给开发者提供高效的内置函数。

内置函数

内置函数通常是以最优的方式实现的,有部分函数甚至是由硬件直接支持,这大大地提高了执行效率。大部分内置函数同时适用于顶点着色器与片元着色器,但是也有部分内置函数只适用于顶点着色器或者片元着色器。

内置函数按照设计目的可以分为三个类型:

  • 提供独特的硬件功能访问接口,如纹理采样系列函数,这些函数用户是无法自己开发的。着色器语言通过提供特定内置函数对这些硬件功能进行封装,建立了用户调用这些硬件功能的接口。

  • 简单的数学函数,如abs、floor等。这些数学函数本身非常简单,开发人员也可以自己开发,但可能由于对底层硬件不了解,采用的实现方式很低效。而内置函数是厂商根据硬件的特点用最高效的方式实现的,调用内置函数来完成这些简单的操作不但可以提高开发效率还可以提高执行效率。

  • 一些复杂的函数,如三角函数等,用户可以自己编写,但是编写过程特别烦琐,要用到很多高等数学的知识。不但开发烦琐,可以想像效率也会低下。而当下的主流硬件往往都有进行这些计算的指令,因此对这些操作也提供了高效的内置函数。

具体内置函数 https://cloud.tencent.com/developer/article/1148928

编译和执行

GLSL也是编译执行性语言,只不过编译是动态的,并且不是独立的应用,需要使用OpenGL API的宿主程序。

工具

GLSL着色器可以事先建立和测试,现有以下GLSL开发工具:

  • RenderMonkey 是由ATI制作的,提供界面用以建立、编译和调试GLSL着色器,和DirectX着色器一样,仅能在Windows平台上执行。

  • GLSLEditorSample

  • Lumina

你可能感兴趣的:(OpenGL GLSL语言)