1、前期基础储备
笔者以前的四篇文综述了Android中使用OpenGL ES绘制基本图形和实现了简单的相机预览,初次接触OpenGL ES开发的读者可能对其中新的概念比较迷惑,尤为是其中的顶点着色器(Vertex Shader)和片元着色器(Fragment Shader),咱们知道,在OpenGL中顶点着色器是针对每一个顶点执行一次,用于肯定顶点的位置。片元着色器是针对每一个片元,片元能够理解为每一个像素,用于肯定每一个片元(像素)的颜色或者纹理。以下是在相机预览中使用的两个着色器的代码段:java
private final String vertexShaderCode =
"attribute vec4 vPosition;" +
"attribute vec2 inputTextureCoordinate;" +
"varying vec2 textureCoordinate;" +
"void main()" +
"{"+
"gl_Position = vPosition;"+
"textureCoordinate = inputTextureCoordinate;" +
"}"; android
private final String fragmentShaderCode =
"#extension GL_OES_EGL_image_external : require\n"+ //相机预览时的片元指令
"precision mediump float;" + //相机预览时的片元指令
"varying vec2 textureCoordinate;\n" +
"uniform samplerExternalOES s_texture;\n" + //相机预览时的片元指令
"void main() {" +
" gl_FragColor = texture2D( s_texture, textureCoordinate );\n" +
"}";
两段着色器中,使用的代码不是Java代码,语法也不是Java层面的,而是一种OpenGL特有的语法:GLSL语法编程
GLSL又叫OpenGL着色语言(OpenGL Shading Language),是用来在OpenGL中着色编程的语言,是一种面向过程的语言,基本的语法和C/C++基本相同,他们是在图形卡的GPU (Graphic Processor Unit图形处理单元)上执行的,代替了固定的渲染管线的一部分,使渲染管线中不一样层次具备可编程性。好比:视图转换、投影转换等。GLSL(GL Shading Language)的着色器代码分红2个部分:Vertex Shader(顶点着色器)和Fragment Shader(片元着色器)。
在前面的文章中,咱们基本上使用的都是很是简单的着色器,基本上没有使用过GLSL的内置函数,可是在后面咱们完成其余的功能的时候应该就会用到这些内置函数了。好比说相机的各类滤镜、各类静态、动态贴纸,其实现少不了特定片元着色器的支持。数组
在笔者的文章《Android使用GPUImage实现滤镜效果精炼详解(一)》中经过一些示例代码实现了调用GPUImage处理静态图片,为其设置各类滤镜效果,在GitHub上找到GPUImage的开源库打开以后,就能够发现,其实现各类滤镜效果底层依靠的也是OpenGL,每种不一样滤镜效果,其片元着色器也是特定的,以黑白滤镜的片元着色器为例:函数
public static final String GRAYSCALE_FRAGMENT_SHADER = "" +
"precision highp float;\n" +
"\n" +
"varying vec2 textureCoordinate;\n" +
"\n" +
"uniform sampler2D inputImageTexture;\n" +
"\n" +
"const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);\n" +
"\n" +
"void main()\n" +
"{\n" +
" lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);\n" +
" float luminance = dot(textureColor.rgb, W);\n" +
"\n" +
" gl_FragColor = vec4(vec3(luminance), textureColor.a);\n" +
"}";
看起来,一头雾水,实际上该代码段是由OpenGL的特有语言GLSL构成的,GLSL的变量申明、GLSL语言的内置函数、GLSL的各类修饰符…只要掌握了GLSL语法,那么后期构造片元着色器会十分的便利,笔者是作相机开发的,工做中建立新滤镜效果,离不开GPUImage,其原生的片元着色器一部分能够拿来直接使用,可是更多的都是要在其基础上作修改的,使之符合本身的滤镜须要,而在改造过程当中,GLSL语法掌握就更为迫切了。
注:任何一种OpenGL程序本质上均可以被分为两部分:CPU端运行的部分,采用C++、Java之类的语言编写;以及GPU端运行的部分,使用GLSL语言编写。ui
2、上代码,具体实现
(1)基本数据类型
GLSL中的数据类型主要分为标量、向量、矩阵、采样器、结构体、数组、空类型七种类型:
1)标量
标量表示的是只有大小没有方向的量,在GLSL中标量只有bool、int和float三种。对于int,和C同样,能够写为十进制、八进制或者十六进制。对于标量的运算,咱们最须要注意的是精度,防止溢出问题。
2)向量
向量咱们能够看作是数组,在GLSL一般用于储存颜色、坐标等数据,针对维数,可分为二维、三维和四位向量。针对存储的标量类型,能够分为bool、int和float。共有vec二、vec三、vec4,ivec二、ivec三、ivec四、bvec二、bvec3和bvec4九种类型,数组表明维数、i表示int类型、b表示bool类型。须要注意的是,GLSL中的向量表示竖向量,因此与矩阵相乘进行变换时,矩阵在前,向量在后(与DirectX正好相反)。orm
做为颜色向量时,用rgba表示份量,就如同取数组的中具体数据的索引值。三维颜色向量就用rgb表示份量。好比对于颜色向量vec4 color,color[0]和color.r都表示color向量的第一个值,也就是红色的份量。
做为位置向量时,用xyzw表示份量,xyz分别表示xyz坐标,w表示向量的模。三维坐标向量为xyz表示份量,二维向量为xy表示份量。
做为纹理向量时,用stpq表示份量,三维用stp表示份量,二维用st表示份量。对象
3)矩阵
在GLSL中矩阵拥有2二、3三、4*4三种类型的矩阵,分别用mat二、mat三、mat4表示。咱们能够把矩阵看作是一个二维数组,也能够用二维数组下表的方式取里面具体位置的值。
4)采样器
采样器是专门用来对纹理进行采样工做的,在GLSL中通常来讲,一个采样器变量表示一副或者一套纹理贴图。所谓的纹理贴图能够理解为咱们看到的物体上的皮肤。纹理查找须要指定哪一个纹理或者纹理单元将指定查找。索引
sampler1D // 访问一个一维纹理
sampler2D // 访问一个二维纹理
sampler3D // 访问一个三维纹理
samplerCube // 访问一个立方贴图纹理
sampler1DShadow // 访问一个带对比的一维深度纹理
sampler2DShadow // 访问一个带对比的二维深度纹理图片
uniform sampler2D grass;
vcc2 coord = vec2(100, 100);
vec4 color = texture2D(grass, coord);
5)结构体
和C语言中的结构体相同,用struct来定义结构体,这是惟一的用户定义类型。
struct light
{
vec3 position;
vec3 color;
};
light ceiling_light;
6)数组
数组知识也和C中相同,不一样的是数组声明时能够不指定大小,可是建议在没必要要的状况下,仍是指定大小的好。
// 建立一个10个元素的数组
vec4 points[10];
// 建立一个不指定大小的数组
vec4 points[];
points[2] = vec4(1.0); // points如今大小为3
points[7] = vec4(2.0); // points如今大小为8
7)空类型
空类型用void表示,仅用来声明不返回任何值得函数。
数据声明示例:
float a=1.5;
int b=2;
bool c=true;
vec2 d=vec2(1.1,2.2);
vec3 e=vec3(1.1,2.2,3.3)
vec4 f=vec4(vec3,1.1);
vec4 g=vec4(1.0); //至关于vec(1.0,1.0,1.0,1.0)
vec4 h=vec4(a,a,1.5,a);
mat2 i=mat2(1.1,1.5,2.2,4.4);
mat2 j=mat2(1.8); //至关于mat2(1.8,1.8,1.8,1.8)
mat3 k=mat3(e,e,1.2,1.4,1.6);
(2)运算符
GLSL中的运算符包括(越靠前,运算优先级越高):
索引:[ ]
前缀自加和自减:++,–-
一元非和逻辑非:~,!
加法和减法:+,-
等于和不等于:==,!=
逻辑异或:^^
三元运算符号,选择:? :
成员选择与混合:.
后缀自加和自减:++,–-
乘法和除法:*,/
关系运算符:>,<,=,>=,<=,<>
逻辑与:&&
逻辑或:||
赋值预算:=,+=,-=,*=,/=
(3)类型转换
GLSL的类型转换与C不一样。在GLSL中类型不能够自动提高,好比float a=1;就是一种错误的写法,必须严格的写成float a=1.0,也不能够强制转换,即float a=(float)1; 也是错误的写法,可是能够用内置函数来进行转换,如float a=float(1);还有float a=float(true);(true为1.0,false为0.0)等,值得注意的是,低精度的int不能转换为高精度的float。
(4)限定符
观察文首的两段着色器,能够看到对于变量使用了不一样的修饰符,如下是GLSL修饰符的主要类型:
attritude:通常用于各个顶点各不相同的量。如顶点颜色、坐标等。
uniform:通常用于对于3D物体中全部顶点都相同的量。好比光源位置,统一变换矩阵等。
varying:表示易变量,通常用于顶点着色器传递到片元着色器的量。
const:常量。 限定符与java限定符相似,放在变量类型以前,而且只能用于全局变量。在GLSL中,没有默认限定符一说。
(5)流程控制
GLSL中的流程控制与C中基本相同,主要有条件判断和各类循环:
if( )、if( )else、if( )else if( )else
while()和dowhile( )
for( ; ; )
break和continue
(6)函数修饰符
GLSL中也能够定义函数,定义函数的方式也与C语言基本相同。函数的返回值能够是GLSL中的除了采样器的任意类型。对于GLSL中函数的参数,能够用参数用途修饰符来进行修饰,经常使用函数修饰符以下:
in:输入参数,无修饰符时默认为此修饰符。
out:输出参数。
inout:既能够做为输入参数,又能够做为输出参数。
(6)浮点精度
与顶点着色器不一样的是,在片元着色器中使用浮点型时,必须指定浮点类型的精度,不然编译会报错。精度有三种,分别为:
lowp:低精度。8位。
mediump:中精度。10位。
highp:高精度。16位。
固然,也能够在片元着色器中设置默认精度,只须要在片元着色器最上面加上precision <精度> <类型>便可制定某种类型的默认精度。其余状况相同的话,精度越高,画质越好,使用的资源也越多。
(7)着色器代码结构
GLSL程序的结构和C语言差很少,main()方法表示入口函数,能够在其上定义函数和变量,在main中能够引用这些变量和函数。定义在函数体之外的叫作全局变量,定义在函数体内的叫作局部变量。与高级语言不一样的是,变量和函数在使用前必须声明,不能在使用的后面声明变量或者函数。
3、GLSL内建变量和内置函数
在上面,咱们了解了GLSL的基础语法:数据类型、运算符、控制流,接下来,咱们要了解另一些关键的内容:GLSL为方便调用,内置了一些变量和函数,在着色器构造中,少不了这些知识的运用。在上面glsl提供了很是丰富的函数库,供咱们使用,这些功能都是很是有用且会常常用到的. 这些函数按功能区分大改能够分红7类:
(1)顶点着色器的内建变量
gl_Position:顶点坐标
gl_PointSize:点的大小,没有赋值则为默认值1,一般设置绘图为点绘制才有意义。
(2)片元着色器的内建变量
gl_FragCoord:当前片元相对窗口位置所处的坐标。
gl_FragFacing:bool型,表示是否为属于光栅化生成此片元的对应图元的正面。 输出变量:
gl_FragColor:当前片元颜色
gl_FragData:vec4类型的数组。向其写入的信息,供渲染管线的后继过程使用。
GLSL提供了很是丰富的函数库,供咱们使用,这些功能都是很是有用并且常常会使用到的,这些函数按照功能区分大体能够分为7类:
(1)通用函数库
(2)三角函数&角度
(3)指数函数
(4)几何函数
(5)矩阵函数
(6)向量函数
(7)纹理查询函数
纹理采样函数中,3D在OpenGLES2.0并非绝对支持。咱们再次暂时无论3D纹理采样函数。重点只对texture2D函数进行说明。texture2D拥有三个参数,第一个参数表示纹理采样器。第二个参数表示纹理坐标,能够是二维、三维、或者四维。第三个参数加入后只能在片元着色器中调用,且只对采样器为mipmap类型纹理时有效。
在看完以上的OpenGL内建变量和内置函数以后,在返回前面,查看黑白滤镜的片元着色器,就会发现,各个变量的声明和使用均可以看得懂了,GPUImage中每种不一样类型的滤镜实现都是采用了相似的方式,感兴趣的读者能够查看GPUImage的Android库:android-gpuimage
4、延伸知识,JSON语法
(1)JSON简介
JSON指的是JavaScript对象表示法(JavaScript Object Notation)
JSON是轻量级的文本交换格式,相似于XML,是纯文本
JSON独立于语言,具备层级结构(值中存在值)
JSON具备自我描述性,更易理解(人类可读)
JSON使用JavaScript语法来描述数据对象,可是JSON仍然独立于语言和平台。
(2)相比于XML的不一样之处
JSON比XML更小、更快、更易解析
没有结束标签
更短,读写速度更快
使用数组
不使用保留字
(3)JSON语法规则
JSON语法是JavaScript对象表示语法的子集
数据在 名称/值 对中
数据由逗号隔开
花括号保存对象
方括号保存数组
(4)JSON 名称/值 对
JSON 数据的书写格式是:名称/值对。
名称/值对包括:字段名称(在双引号中),后面写一个冒号,而后是值:
“firstName” : “John”
(5)JSON值
JSON 值能够是:
数字(整数或浮点数)
字符串(在双引号中)
逻辑值(true 或 false)
数组(在方括号中)
对象(在花括号中)
Null
(6)JSON对象
JSON 对象在花括号中书写:对象能够包含多个名称/值对:
{ “firstName”:”John” , “lastName”:”Doe” }
(7)JSON 数组
JSON 数组在方括号中书写:数组可包含多个对象:
{
“employees”: [
{ “firstName”:”John” , “lastName”:”Doe” },
{ “firstName”:”Anna” , “lastName”:”Smith” },
{ “firstName”:”Peter” , “lastName”:”Jones” }
]
}
相关语法能够搜索 GLSL 中文手册 获取更多的语法知识