Android自定义camera2相机 系列(三)GLSL语言基础学习

前言

该篇文章是继camera2 的基础上 添加的 Opengles,实现滤镜或者黑白灰等效果,由于Opengles 需要设置 GLSL 语言的 shader,这里单独加一张 GLSL 的学习笔记。阅读时间20分钟+

参考文章 湖广午王 CSDN
参考文章

1. 关于GLSL

着色器是用来实现图像渲染的,用来替代固定渲染管线的可编程程序。着色器替代了传统的固定渲染管线,可以实现3D图形学计算中的相关计算,由于其可编程性,可以实现各种各样的图像效果而不用受显卡的固定渲染管线限制。这极大的提高了图像的画质。

在Opengloes 中着色器分为顶点着色器片元着色器,我们可以理解为:顶点着色器是针对每个顶点执行一次,用于确定顶点的位置;片元着色器是针对每个片元(可以理解为每个像素)执行一次,用于确认渲染每个片元(像素)的颜色。

2. GLSL 简要说明

OpenGLES的着色器语言GLSL是一种高级的图形化编程语言,其源自应用广泛的C语言。与传统的C语言不同的是,它提供了更加丰富的针对于图像处理的原生类型,诸如向量、矩阵之类。OpenGLES 主要包含以下特性:

1.GLSL是一种面向过程的语言,和Java的面向对象是不同的。
2.它完美的支持向量和矩阵操作。
3.它是通过限定符操作来管理输入输出类型的。
4.GLSL提供了大量的内置函数来提供丰富的扩展功能。

3.1 基本类型

类型 描述
void 同 java,表示空类型。作为函数的返回类型,表示这个函数不返回值。
bool 布尔类型 rue 和false,可以产生布尔型的表达式。
int 整型 代表至少包含16位的有符号的整数。十进制,十六进制 八进制
float 浮点型
bvec2 /3 /4 包含2 / 3 / 4个布尔成分的向量
ivec2 / 3 / 4 包含2 / 3 / 4个整型成分的向量
mat2 或者 mat2x2 2x2的浮点数矩阵类型
mat3 或者 mat3x3 3x3的浮点数矩阵类型
mat4 或者 mat4x4 4x4的浮点矩阵
mat2x3 2列3行的浮点矩阵(OpenGL的矩阵是列主顺序的) 还有 很多种类型 2× 4 2列 4行 不一一列举了
sampler1D / 2D / 3D 用于内建的纹理函数中引用指定的1D /2D / 3D 纹理的句柄。只可以作为一致变量或者函数参数使用
samplerCube cube map纹理句柄
sampler1DShadow / sampler2DShadow 一维 / 二维深度纹理句柄

代码举例 :

float a=1.0;
int b=1;
bool c=true;
vec2 d=vec2(1.0,2.0);
vec3 e=vec3(1.0,2.0,3.0)
vec4 f=vec4(vec3,1.0);
vec4 g=vec4(0.2); //相当于vec(0.2,0.2,0.2,0.2)

mat2 i=mat2(0.1,0.5,1.2,2.4);
mat2 j=mat2(0.8); //相当于mat2(0.8,0.8,0.8,0.8)

3.2 运算符

GLSL语言的操作符与Java 语言相似。如下表(操作符的优先级从高到低排列

操作符 描述
() 用于表达式组合,函数调用,构造
[ ] 数组下标,向量或矩阵的选择器
. 结构体和向量的成员选择
++ -- 前缀或后缀的自增自减操作符
+ – ! 一元操作符,表示正 负 逻辑非
* / 乘 除操作符
+ - 二元操作符 表示加 减操作
<> <= >= == != 小于,大于,小于等于, 大于等于,等于,不等于 判断符
&& ^^ 逻辑与 ,或, 异或
?: 条件判断符
= += –= *= /= 赋值操作符

3.3 结构体

结构体可以组合基本类型和数组来形成用户自定义的类型。在定义一个结构体的同时,你可以定义一个结构体实例。或者后面再定义。

struct surface {float indexOfRefraction;
vec3 color;float turbulence;
} mySurface;

surface secondeSurface;

可以通过=为结构体赋值,或者使用 ==,!=来判断两个结构体是否相等

只有结构体中的每个成分都相等,那么这两个结构体才是相等的。访问结构体的内部成员使用. 来访问(和 java 相同)。

结构体至少包含一个成员。固定大小的数组也可以被包含在结构体中。GLSL的结构体不支持嵌套定义。只有预先声明的结构体可以嵌套其中。

struct myStruct {

  vec3 points[3]; //固定大小的数组是合法的
  surface surf;  //可以,之前已经定义了
  
  struct velocity {  //不合法float speed;
    vec3 direction;
  } velo;

  subSurface sub; 
  //不合法,没有预先声明;
  struct subSurface { 
   int id;
  };
};

3.4 数组

GLSL中只可以使用一维的数组。数组的类型可以是一切基本类型或者结构体。下面的几种数组声明是合法的:

surface mySurfaces[];
vec4 lightPositions[8];
vec4 lightPos[] = lightPositions;const int numSurfaces = 5;
surface myFiveSurfaces[numSurfaces];float[5] values;

指定显示大小的数组可以作为函数的参数或者使返回值,也可以作为结构体的成员.数组类型内建了一个length()函数,可以返回数组的长度。

vec4 myColor, ambient, diffuse[6], specular[6];

myColor = ambient + diffuse[4] + specular[4];

3.5 修饰符

修饰符 描述
const 常量值必须在声明是初始化。它是只读的不可修改的。
attribute 表示只读的顶点数据,只用在顶点着色器中。数据来自当前的顶点状态或者顶点数组。它必须是全局范围声明的,不能再函数内部。一个attribute可以是浮点数类型的标量,向量,或者矩阵。不可以是数组或则结构体
uniform 一致变量。在着色器执行期间一致变量的值是不变的。与const常量不同的是,这个值在编译时期是未知的是由着色器外部初始化的。一致变量在顶点着色器和片段着色器之间是共享的。它也只能在全局范围进行声明。
varying 顶点着色器的输出。例如颜色或者纹理坐标,(插值后的数据)作为片段着色器的只读输入数据。必须是全局范围声明的全局变量。可以是浮点数类型的标量,向量,矩阵。不能是数组或者结构体。
centorid varying 在没有多重采样的情况下,与varying是一样的意思。在多重采样时,centorid varying在光栅化的图形内部进行求值而不是在片段中心的固定位置求值。
invariant (不变量)用于表示顶点着色器的输出和任何匹配片段着色器的输入,在不同的着色器中计算产生的值必须是一致的。所有的数据流和控制流,写入invariant变量的是一致的。编译器为了保证结果是完全一致的,需要放弃那些可能会导致不一致值的潜在的优化。除非必要,不要使用这个修饰符。在多通道渲染中避免z-fighting可能会使用到。
in 用在函数的参数中,表示这个参数是输入的,在函数中改变这个值,并不会影响对调用的函数产生副作用。(相当于C语言的传值),这个是函数参数默认的修饰符
out 用在函数的参数中,表示该参数是输出参数,值是会改变的。
inout 用在函数的参数,表示这个参数即是输入参数也是输出参数。

3.6 内置变量

内置变量可以与固定函数功能进行交互。在使用前不需要声明。顶点着色器可用的内置变量如下表:

名称 类型 描述
gl_Color vec4 输入属性-表示顶点的主颜色
gl_SecondaryColor vec4 输入属性-表示顶点的辅助颜色
gl_Normal vec3 输入属性-表示顶点的法线值
gl_Vertex vec4 输入属性-表示物体空间的顶点位置
gl_MultiTexCoordn vec4 输入属性-表示顶点的第n个纹理的坐标
gl_FogCoord float 输入属性-表示顶点的雾坐标
gl_Position vec4 输出属性-变换后的顶点的位置,用于后面的固定的裁剪等操作。所有的顶点着色器都必须写这个值。
gl_ClipVertex vec4 输出坐标,用于用户裁剪平面的裁剪
gl_PointSize float 点的大小
gl_FrontColor vec4 正面的主颜色的varying输出
gl_BackColor vec4 背面主颜色的varying输出
gl_FrontSecondaryColor vec4 正面的辅助颜色的varying输出
gl_BackSecondaryColor vec4 背面的辅助颜色的varying输出
gl_TexCoord [] vec4 纹理坐标的数组varying输出
gl_FogFragCoord float 雾坐标的varying输出

片段着色器 的内置变量如下表:

名称 类型 描述
gl_Color vec4 包含主颜色的插值只读输入
gl_SecondaryColor vec4 包含辅助颜色的插值只读输入
gl_TexCoord[] vec4 包含纹理坐标数组的插值只读输入
gl_FogFragCoord float 包含雾坐标的插值只读输入
gl_FragCoord vec4 只读输入,窗口的x,y,z和1/w
gl_FrontFacing bool 只读输入,如果是窗口正面图元的一部分,则这个值为true
gl_PointCoord vec2 点精灵的二维空间坐标范围在(0.0, 0.0)到(1.0, 1.0)之间,仅用于点图元和点精灵开启的情况下。
gl_FragData[] vec4 使用glDrawBuffers输出的数据数组。不能与gl_FragColor结合使用。
gl_FragColor vec4 输出的颜色用于随后的像素操作
gl_FragDepth float 输出的深度用于随后的像素操作,如果这个值没有被写,则使用固定功能管线的深度值代替

3.7 构造函数

构造函数可以用于初始化包含多个成员的变量,包括数组和结构体。构造函数也可以用在表达式中。调用方式如下:

vec3 myNormal = vec3(1.0, 1.0, 1.0);
greenTint = myColor + vec3(0.0, 1.0, 0.0);
ivec4 myColor = ivec4(255);

还可以使用混合标量和向量的方式来构造,只要你的元素足以填满该向量。

vec4 color = vec4(1.0, vec2(0.0, 1.0), 1.0);
vec3 v = vec3(1.0, 10.0, 1.0);
vec3 v1 = vec3(v);
vec2 fv = vec2(5.0, 6.0);

对于矩阵,OpenGL中矩阵是列主顺序的。如果只传了一个值,则会构造成对角矩阵,其余的元素为0.

mat3 m3 = mat3(1.0);

构造出来的矩阵式:

1.0 0.0 0.0

0.0 1.0 0.0

0.0 0.0 1.0

mat2 matrix1 = mat2(1.0, 0.0, 0.0, 1.0);

mat2 matrix2 = mat2(vec2(1.0, 0.0), vec2(0.0, 1.0));

mat2 matrix3 = mat2(1.0); 

mat2 matrix4 = mat2(mat4(2.0)); //会取 4x4矩阵左上角的2x2矩阵。

构造函数可以用于标量数据类型的转换。GLSL不支持隐式或显示的转换,只能通过构造函数来转。其中int转为float值是一样的。float转为int则小数部分被丢弃。int或float转为bool,0和0.0转为false,其余的值转为true. bool转为int或float,false值转为0和0.0,true转为1和1.0.

float f = 1.7;

int I = int(f); // I = 1

数组的初始化,可以在构造函数中传入值来初始化数组中对应的每一个值。

ivec2 position[3] = ivec2[3]((0,0), (1,1), (2,2));

ivec2 pos2[3] = ivec2[]((3,3), (2,1), (3,1));

构造函数也可以对结构体进行初始化。其中顺序和类型要一一对应。

struct surface {  
  int  index;
  vec3 color;  
  float rotate;
};

surface mySurface = surface(3, vec3(red, green, blue), 0.5);

3.8 控制流

GLSL中的流程控制与 java 中基本相同,主要有:

  1. if(){}、if(){}else{}、if(){}else if(){}else{}
  2. while(){}和do{}while()
  3. for(;;){}
  4. break和continue

3.9 函数

在每个shader中必须有一个main函数。main函数中的void参数是可选的,但返回值是void时必须的。

GLSL中的函数,必须是在全局范围定义和声明的。不能在函数定义中声明或定义函数。函数必须有返回类型,参数是可选的。参数的修饰符(in, out, inout, const等)是可选的

void main(void)
{
 ...
}

3.9.1 常见函数

  1. radians(x):角度转弧度
  2. degrees(x):弧度转角度
  3. sin(x):正弦函数,传入值为弧度。相同的还有cos余弦函数、tan正切函数、asin反正弦、acos反余弦、atan反正切
  4. pow(x,y):xyxy
  5. exp(x):exex
  6. exp2(x):2x2x
  7. log(x):logexlogex
  8. log2(x):log2xlog2x
  9. sqrt(x):x−−√x
  10. inversesqr(x):1x−−√1x
  11. abs(x):取x的绝对值
  12. sign(x):x>0返回1.0,x<0返回-1.0,否则返回0.0
  13. ceil(x):返回大于或者等于x的整数
  14. floor(x):返回小于或者等于x的整数
  15. fract(x):返回x-floor(x)的值
  16. mod(x,y):取模(求余)
  17. min(x,y):获取xy中小的那个
  18. max(x,y):获取xy中大的那个
  19. mix(x,y,a):返回x∗(1−a)+y∗ax∗(1−a)+y∗a
  20. step(x,a):x< a返回0.0,否则返回1.0
  21. smoothstep(x,y,a):a < x返回0.0,a>y返回1.0,否则返回0.0-1.0之间平滑的Hermite插值。
  22. dFdx(p):p在x方向上的偏导数
  23. dFdy(p):p在y方向上的偏导数
  24. fwidth(p):p在x和y方向上的偏导数的绝对值之和

3.9.2 几何函数

  1. length(x):计算向量x的长度
  2. distance(x,y):返回向量xy之间的距离
  3. dot(x,y):返回向量xy的点积
  4. cross(x,y):返回向量xy的差积
  5. normalize(x):返回与x向量方向相同,长度为1的向量

3.9.3 矩阵函数

  1. matrixCompMult(x,y):将矩阵相乘
  2. lessThan(x,y):返回向量xy的各个分量执行x< y的结果,类似的有greaterThan,equal,notEqual
  3. lessThanEqual(x,y):返回向量xy的各个分量执行x<= y的结果,类似的有类似的有greaterThanEqual
  4. any(bvec x):x有一个元素为true,则为true
  5. all(bvec x):x所有元素为true,则返回true,否则返回false
  6. not(bvec x):x所有分量执行逻辑非运算

3.9.4 纹理采样函数

纹理采样函数有texture2D、texture2DProj、texture2DLod、texture2DProjLod、textureCube、textureCubeLod及texture3D、texture3DProj、texture3DLod、texture3DProjLod等。

  1. texture表示纹理采样,2D表示对2D纹理采样,3D表示对3D纹理采样
  2. Lod后缀,只适用于顶点着色器采样
  3. Proj表示纹理坐标st会除以q

后记

该篇文章知识点 多为文章头部参考文章原文,这里比较多的是 记录和归纳。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • 在OpenGL ES中着色器分为顶点着色器和片元着色器。顶点着色器是针对每个顶点执行一次,用于确定顶点的位置。片元...
    大脸萌家的大眼萌阅读 2,987评论 0 3
  • 概述: 以c为基础: OpenGL着色语言是基于ANSI C编程语言的语法,使用这种语言编写的程序与C程...
    一川烟草i蓑衣阅读 2,199评论 0 1
  • 一:着色的原理 OpenGL着色语言(OpenGL Shading Language)是用来在OpenGL中着色编...
    wo不懂阅读 986评论 0 2
  • 图元处理(Primitive Processing) 如何在场景中使用曲面细分来添加几何细节 如何使用几何着色器处...
    RichardJieChen阅读 6,804评论 2 4
  • 关于着色器 着色器是用来实现图像渲染的,用来替代固定渲染管线的可编程程序。着色器替代了传统的固定渲染管线,可以实现...
    Oneminute阅读 1,832评论 1 2