码迷,mamicode.com
首页 > 编程语言 > 详细

OpenGLES2.0着色器语言glsl

时间:2016-01-15 17:44:08      阅读:692      评论:0      收藏:0      [点我收藏+]

标签:

OpenGLES2.0中是强制使用可编程的渲染管线的,使用的是glsl着色器语言,因为着色器语言是使用的GPU,即图形处理单元,而不是CPU,这样可以使CPU从繁重的几何计算和像素的处理中解脱出来了。这就加大了处理的速度。

下面的这篇教程是转载的。原文地址如下,感谢作者manyou

http://www.apkbus.com/blog-99192-39382.html


一、着色语言基础

数据类型概述

1. 标量

标量也被称为“无向量”其值只有大小,并不具有方向。标量之间的运算遵循简单的代数法则,如质量、密度、体积、时间以及温度等都属于标量。OpenGL ES着色语言支持的标量类型有布尔型(bool)、整形(int)和浮点型(float)。

 

2. 向量

OpenGL ES着色语言中,向量可以看做是用同样类型的标量组成,其基本类型也分为bool、int和float三种。每个向量可以由2个、3个、4个相同的标量组成,具体情况如下:

向量类型

说明

向量类型

说明

vec2

包含了2个浮点数的向量

ivec4

包含了4个整数的向量

vec3

包含了3个浮点数的向量

bvec2

包含了2个布尔数的向量

vec4

包含了4个浮点数的向量

bvec3

包含了3个布尔数的向量

ivec2

包含了2个整数的向量

bvec4

包含了4个布尔数的向量

ivec3

包含了3个整数的向量

  

向量在着色器代码的开发中有着十分重要的作用,可以很方面的存储以及存储颜色、位置、纹理坐标等不仅包含一个组成部分的量。开发中,有时可能需奥单独访问向量中的某个分量,基本的语法为“<向量名>.<分量名>”,根据目的的不同,主要有以下几种用法:

将一个向量看做颜色时,可以使用r,g,b,a四个分量名,分别代表红、绿、蓝、透明度4个色彩通道。具体用法如下:

 

[代码]xml代码:

1
2
//给向量aColor的红色通道赋值
aColor.r = 0.6;

 

 

将一个向量看做位置时,可以使用x,y,z,w等4个分量名,分别代表X轴,Y轴,Z轴和向量的模四个分量,具体用法和颜色类似。

将一个向量看做纹理坐标时,可以使用s,t,p,q四个分量名,期分别代表纹理坐标的不同分量,具体用法同颜色。(对纹理坐标中的s,t等分量巴友可能不是很明白,不用担心,在后面介绍纹理贴图的教程会进行详细的介绍)

访问向量中的各个不同的分量不但可以采用“.”加上不同的分量名,还可以将向量看做一个数组,用下标来进行访问,具体用法如下:

 

[代码]xml代码:

1
2
//给向量aColor的红色通道赋值
aColor[0] = 0.6;

 

 

3. 矩阵

有一些基础的开发人员都知道,3D场景中的移位、旋转、缩放等变换都是由矩阵的运算来实现的。因此3D场景的开发中会非常多的使用矩阵,矩阵按尺寸分为2x2矩阵、3x3矩阵、4x4矩阵,具体情况如下表所示:

矩阵类型

说明

mat2

2x2浮点数矩阵

mat3

3x3浮点数矩阵

mat4

4x4浮点数矩阵

对于矩阵的访问,可以讲矩阵作为列向量的数组来访问。如matrix为一个mat4,可以使用matrix[2]取到该矩阵的第三列,其为一个vec4;也可以使用matix[2][2]取得第三列向量的第3个分量。

4. 采样器

采样器是着色语言中不同于C语言的一种特殊的基本数据类型,其专门用来进行纹理采样的相关操作。一般情况下,一个采样器变量代表一幅或一套纹理贴图,其具体情况如下:

采样器

说明

sampler2D

用于访问二维纹理

smapler3D

用于访问三维纹理

samplerCube

用于访问立方贴图纹理

需要注意的是,与前面介绍的几种变量不同,采样器变量不能再着色器中初始化。一般情况下采样器变量都用uniform限定符来修饰,从宿主语言(如java)接受传递进着色器的值。

5. 结构体

OpenGL ES着色语言还提供了类似C语言中的用户自定义结构体,同样也是使用struct关键字进行声明。其基本用法如下:

 

[代码]xml代码:

1
2
3
4
5
struct info{
vec3 color;
vec3 position;
vec2 textureCoor;
}

 

 

6. 数组

声明数组的方式主要有两种,

在声明数组的同时,指定数组的大小:

 

[代码]xml代码:

1
vec3 position[20];

 

 

在声明数组时,也可以不指定数组的大小,但是必须符合下列两种情况之一。

u 引用数组之前,要再次使用第一种声明方式来生命该数组:

 

[代码]xml代码:

1
2
3
4
//声明了一个大小不定的vec3数组
vec3 position[];
//再次声明该数组,并且指定大小。
vec3 position[5];

 

 

u 代码中访问数组的下标都是编译时常量,这时编译器会自动创建适当大小的数组,使得数组尺寸足够存储编译器看到的最大索引值对应的元素。

 

[代码]xml代码:

1
2
3
4
5
6
//声明了一个大小不定的vec3数组
vec3 position[];
//position需要一个大小为4的数组
position[3] = vec3(3.0);
//position需要一个大小为21的数组
position[20] = vec3(6.0);

 

 

7. 空类型使用void表示,仅用来声明不返回任何值得函数。例如在顶点着色器以及片元着色器中必须存在的main函数就是一个返回值为空的函数,代码如下:

 

[代码]xml代码:

1
2
void main() {
}

 

 

数据类型的基本使用

1. 声明、作用域及初始化

变量的声明以及作用域与Java/C++语法类似,可以再任何需要的位置声明变量,同时期作用域也同样分为局部变量和全局变量:

 

[代码]xml代码:

1
2
3
4
5
6
7
8
9
10
11
12
//声明了全局变量a和b
int a,b;
//声明了全局变量aPosition并赋值
vec3 aPosition = vec3(1.0, 2.2, 3.3);
void myFunction() {
    //声明了局部变量c并赋值
    int c = 14;
    //给全局变量a赋值
    a = 4;
    //给全局变量b赋值
    b = a * c;
}

 

 

向量的初始化还有一些很灵活的技巧,巴友们体会一下下面的代码:

 

[代码]xml代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//声明浮点变量a并赋值
float a = 12.3;
//声明浮点变量b并赋值
float b = 11.4;
//声明2维向量va并赋值
vec2 va = vec2(2.3, 2.5);
//声明2维向量vb并赋值
vec2 vb = vec2(a, b);
//声明3维向量vc并赋值
vec3 vc = vec3(vb, 12.5);
//声明4维向量vd并赋值
vec4 vd = vec4(va, vb);
//声明4维向量ve并赋值, 相当于vec4(0.2 , 0.2 , 0.2, 0.2);
vec4 ve = vec4(0.2);

 

 

2. 运算符

与大多数编程语言类似,常见的运算符都可以在该语言中使用。下面按照优先级顺序列出了OpenGL ES着色语言中可以使用的运算符:

运算符

说明

运算符

说明

[]

用于索引

.

成员选择与混合

++ --

自加1与自减1后缀

++ --

自加1与自减1前缀

- !

一元非与逻辑非

* /

乘法与除法

+ -

加法与减法

< > <= >= 

关系运算符

== !=

等于和不等于

&&

逻辑与

^^

逻辑异或

||

逻辑或

?:

选择

= += -= *= /=

赋值运算符

 

3. 限定符

与其他的编程语言一样,着色器中对变量也有很多可选的限定符,主要如下:

限定符

说明

attribute

一般用于每个顶点都各不相同的量,如顶点位置、颜色等。

uniform

一般用于对同一组顶点组成的单个3D物体中所有顶点都相同的量,如当前光源的位置。

varying

用于从顶点着色器传递到片元着色器的量

const

用于声明常量

 

attribute限定符

顾名思义为属性限定符,其修饰的变量用来接收渲染管线传递进顶点着色器的当前待处理顶点的各种属性值。这些属性值每个顶点各自拥有独立的副本,用于描述顶点的各项特征,如顶点坐标、法向量、颜色、纹理坐标等。

用attribute限定符修士的变量其值是由宿主程序批量出入渲染管线的,管线进行基本处理后再传递给顶点着色器。数据中有多少个顶点,管线就调用多少次顶点着色器,每次讲一个顶点的各种属性数据传递给顶点着色器中对应atribute变量。因此,顶点着色器每次执行将完成对一个顶点各项属性数据的处理。

从上面的介绍中可以看出,atribute限定符只能用于顶点着色器中,不能再片元着色器中使用,且attribute限定符只能用来修饰浮点数标量、浮点向量以及矩阵变量,不能用来修饰其他类型的变量。下面的代码片段给出了在顶点着色器中正确使用attribute限定符的情况:

 

[代码]xml代码:

1
2
3
4
//顶点位置
attribute vec3 aPosition;
//顶点法向量
attribute vec3 aNormal;

 

 

前面已经提到,对于用attribute限定符修饰的变量的值是由宿主程序批量传入渲染管线的,相关代码如下:

 

[代码]java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 声明顶点位置属性引用
int maPositionHandle;
// 获取顶点位置属性引用的值,
// mProgram为着色器程序ID,
// aPosition为着色器中对应属性的变量名称。
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
// 将顶点位置数据传入渲染管线
// maPositionHandle:顶点位置属性引用
// 3:每顶点一组的数据个数(这里是X,Y,Z坐标,因此为3)
// GLES20.GL_FLOAT:数据类型
// false:是否格式化
// 3 * 4:每组数据的尺寸,这个魅族3个浮点数值(X,Y,Z坐标),每个浮点数4个字节
// mVertexBuffer:存放了数据的缓冲
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 3 * 4, mVertexBuffer);

 

 

具体代码可以参考第一个教程中的Triangle类。

http://www.apkbus.com/blog-99192-39498.html

 

uniform限定符

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

下面的代码片给出了在顶点或片元着色器中正确使用uniform限定符的情况:

 

[代码]xml代码:

1
2
3
4
5
6
7
8
//总变换矩阵
uniform mat4 uMVPMatrix;
//变换矩阵
uniform mat4 uMMatrix;
//光源位置
uniform vec3 uLightLocation;
//摄像机位置
uniform vec3 uCamera;

 

 

将一致变量的值由宿主程序传入渲染管线的代码如下:

 

[代码]java代码:

1
2
3
4
5
6
//总变换矩阵一致变量引用
int muMVPMatrixHandle;
//获取着色器程序中总变换矩阵一致变量的引用
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
//通过一致变量引用将一致变量值传入渲染管线
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, Triangle.getFianlMatrix(mMMatrix), 0);

 

 

需要注意的是,随一致性变量类型不同将值传入渲染管线的方法也有所不同,这些方法的名称都以glUniform开头,常用的如下所列:

glUniformNf/glUniformNfv方法,将N个浮点数传入管线,以备管线传递给由N个浮点数组成的一致变量,N的取值为1,2,3,4。

glUniformNi/glUniformNiv方法,将N个整数传入管线,以备管线传递给由N个整数组成的一致变量,N的取值为1,2,3,4。

glUniformMatrixNfv方法,将N * N的矩阵传入管线,以备管线传递给N * N矩阵类型的一致变量,N的取值为2,3,4。

 

verying限定符

想要将顶点着色器中的信息传入到片元着色器中,必须使用varying限定符。欧诺个varying限定符修饰的全局变量又称为易变变量,易变变量可以看成是顶点着色器和片元着色器之间的动态接口,方便顶点着色器与片元着色器之间信息的传递。下图给出了易变变量的工作原理:

 

技术分享

 

从上图可以看出,首先顶点着色器再每个顶点中都对一边变量vPosition进行了赋值。接着在片元着色器中接受易变变量vPosition的值时得到的并不是某个顶点赋的特定值,而是根据片元所在的位置以及图元中各个顶点的位置进行差值计算产生的值。

如图中顶点1、2、3的vPosition值分别为vec3(0,7,0)、vec3(5,0,0)、vec3(-5,0,0),则插值后片元a的vPosition值为vec3(1.45, 2.06, 0)。

从上述介绍中可以看到,光栅化后产生了多少个片元,就会插值计算出多少套易变变量。同时渲染管线就会调用多少次片元着色器。可以看出,3D物体的渲染中,片元着色器执行的次数会大大超过顶点着色器。因此GPU硬件中配置的片元着色器硬件数量往往多于顶点着色器硬件数量以提高渲染速度。

 

const限定符

用const限定符修饰的变量其值是不可以变的,也就是常量,又称为编译时常量。编译时常量在声明的时候必须进行初始化。例如:

 

[代码]xml代码:

1
const int tempx = 1;

 

内建变量

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

 

1. 顶点着色器中的内建变量

顶点着色器中的内建变量主要是输出变量,包括gl_Positiongl_PointSize等。在顶点着色器中应该根据需要给这些内建变量赋值,以便由渲染管线中的图元装配与光栅化等后续固定功能阶段进行进一步的操作。

gl_Position:顶点着色器从应用程序中获得原始的顶点位置的数据,这些原始的顶点数据在顶点着色器中经过平移、旋转、缩放等数学变换后,生成新的顶点位置。

gl_PointSize:顶点着色器中可以计算一个点的大小(单位为像素),并将其赋值给gl_PointSize(标量,float类型)以传递给渲染管线,如果没有明确赋值的话,就是采用默认值1gl_PointSize的值一般只有在采用了点绘制方式之后才有意义。

 

2. 片元着色器中的内建变量

片元着色器中的内疚按输入变量主要有gl_FragCoord以及gl_FrontFacing,这两个内建变量都是只读的,由渲染管线中片元着色器之前的阶段生成。其具体含义如下:

gl_FragCoordvec4类型,其中含有当前片元相对于窗口位置的坐标值X,Y,Z。如下图所示:

 

技术分享

 

gl_FrontFacing:布尔型内建变量,通过读取内建变量的值可以判断正在处理的片元是否属于在光栅化阶段生成此片元的对应图元的正面。如果属于正面,那么该值为true,否则为false

 

 

 

 

 

 

OpenGL ES 2.0有两种重要的着色器:顶点着色器片元着色器

 

顶点着色器是一个可编程的处理单元,功能是执行顶点的变换、光照、材质的应用与计算等顶点的相关操作,每顶点执行一次。其工作过程为首先将原始的顶点几何信息以及其他属性传送到顶点着色器中,经过自己开发的着色器处理之后产生纹理坐标、颜色、点位置等后继流程需要的各项顶点属性信息。

 

片元着色器适用于处理片元值及其相关数据的可编程单元,其可以执行纹理的采样、颜色的汇总、计算雾颜色等操作,每个片元执行一次。

 

 

 

介绍完着色器再给大家简单说一说着色器和渲染管线的概念。
OpenGL ES 1.X的渲染管线
学习Open GL ES 2.0的渲染管线之前,应该先了解一下Open GL ES 1.X的渲染管线,这对于进一步了解Open GL ES 2.0的渲染管线也是很有好处的。
渲染管线有时也称之为渲染流水线,一般是由显示芯片(GPU)内部处理图形信号的并行处理单元组成。这些并行处理单元两两之间是相互独立的,在不同型号的硬件上独立处理硬件单元的数量也有很大的差异,一般越高端的硬件,其中独立处理单元的数量也就越多。
从另一个角度看,Open GL ES 中的渲染管线实质上指的是一些列绘制的过程。这些过程输入的待渲染的3D物体的相关描述信息数据,经过渲染的管线,输出的是一帧想要的图像。具体过程如下:
1. 基本处理
该阶段设定3D空间中物体的顶点坐标、顶点对应的颜色、顶点的纹理坐标等属性,并且指定绘制方式,如:点绘制、线段绘制或者三角形绘制。
2. 顶点缓冲对象
这部分功能在应用中是可选的,对于某些在整个场景中顶点的基本上数据不变的情况。可以在初始化阶段将顶点数据经过基本处理后送入顶点缓冲对象,再绘制每一帧想要的图像时就省去了顶点数据IO的麻烦。
3. 变换和光照
该阶段的主要工作是进行顶点变换以及根据程序中设置的光照属性对顶点进行光照计算。
4. 图元装配
这个阶段主要有两个任务,一个是图元组装,另一个是图元处理。
5. 光栅化
虽然虚拟3D世界中的几何信息是三维的,但由于目前用于显示的设备都是二维的。因此在真正执行光栅化工作之前,首先需要将虚拟3D世界中的物体投影到视平面上。
6. 纹理环境和颜色求和
纹理采样:主要是根据当前需要处理片元的纹理坐标以及采用的纹理ID对相应的纹理图进行纹理采样,获取采样值。
颜色求和:执行颜色的变化,起根据纹理采样以及光照计算的结果综合生成需要处理片元的颜色。
7. 雾
根据程序中设置的雾的相关参数,如:颜色、浓度、范围等来计算当前处理的片元受雾影响后的颜色。
8. Alpha测试
如果程序中启用了Alpha测试,Open GL ES会检查每个片元的Alpha值,只有Alpha值符合测试条件的片元才会送入到下一个阶段。
9. 剪裁测试
如果程序中启用了剪裁测试,Open GL ES 会检查每个片元在帧缓冲中对应的位置,若对应位置在剪裁窗口中,则将此片元送到下一个阶段。
10. 深度测试和模板测试
深度测试是指将输入片元的深度值与帧缓冲区中储存的对应位置片元的深度值进行比较。
模板测试的主要功能为将绘制区域限定在一定的范围内,一般用在湖面倒影、镜像等场合。
11. 颜色缓冲混合
若程序中开启了Alpha混合,则根据混合因子将上一阶段送来的片元与帧缓冲中对应位置的片元进行Alpha混合。
12. 抖动
自己理解吧,打字太累了。
13. 帧缓冲
Open GL ES 中的物体绘制并不是直接在屏幕上进行,而是预先在帧缓冲区中进行绘制,每绘制完一帧再将绘制的结果交换到屏幕上。因此每次绘制新的一帧时都需要清楚缓冲区中的相关数据,否则可能产生不正确的绘制效果。
 
Open GL ES 2.0的渲染管线
Open GL ES 1.X只是对开发人员开放了其中的一部分API接口,但在整个渲染管线的运行过程中开发人员是不能直接干预的。因此,虽然Open GL ES 1.x的渲染管线功能已经很强大,但是其留给开发人员的发挥空间并不大,很多特效难以开发,而Open GL ES 2.0为开发人员提供了更多的发挥空间。
Open GL ES 2.0中“顶点着色器”取代了Open GL ES 1.x渲染管线中的“变换和光照”这使得开发3D场景时对顶点的变换、法向量的计算、纹理坐标的变换、光照材质的应用等均由开发者使用着色器代码完成,灵活性大大提高。
Open GL ES 2.0中“片元着色器”取代了Open GL ES 1.x渲染管线中的“纹理环境和颜色求和”、“雾”以及“Alpha测试”等阶段,这使得纹理处理、颜色求和以及雾效果均由开发者自己开发,大大增强了程序对片元的处理能力。

OpenGLES2.0着色器语言glsl

标签:

原文地址:http://blog.csdn.net/u013467442/article/details/50524498

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!