我的OpenGL ES学习之路(一):GLSL着色器语言

着色器语言

GLSL即 OpenGL Shading Language,着色器语言,是和GPU打交道的语言,GLSL的语法比较像C语言。

版本

OpenGL ES的顶点着色器和片段着色器的第一行声明GLSL的版本:

#version 300 es // 使用3.0版本

如果没有声明版本号,则会认为使用1.0版本,着色器的1.0版本用于OpenGL ES 2.0,从OpenGL ES 3.0 开始,规范匹配API和着色器语言的版本号,所以版本才从1.0跳到3.0。

数据类型

基本数据类型

GLSL有三种基本数据类型:float,int,bool,还有由这三种数据类型组成的数组和结构体。

float myFloat = 1.0;
float floatArray[4];
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, vec3, vec4 // 包含2/3/4个浮点数的矢量
ivec2, ivec3, ivec4 // 包含2/3/4个整数的矢量
bvec2, bvec3, bvec4 // 包含2/3/4个布尔值的矢量
vec2 c[2] = vec2[2](vec2(1.0), vec2(2.0));
矩阵
mat2(或mat2x2), mat2x3, mat2x4 // 2x2/2x3/2x4 基于浮点数的矩阵
mat3(或mat3x3), mat3x2, mat3x4 // 3x3/3x2/3x4 基于浮点数的矩阵
mat4(或mat4x4), mat4x3, mat4x2 // 4x4/4x3/4x2 基于浮点数的矩阵

GLSL不支持指针,

GLSL在类型转换方面有非常严格的规则,变量只能赋值给相同类型的其他变量或者与相同类型的变量进行运算。

float myFloat1 = 1.0;
float myFloat2 = 1; // error: invalid type conversion

同时为了解决这种类型转换,类型提供了一些可用的构造器。可以使用构造器初始化变量,并且作为不同类型的变量之间的转换手段

myFloat = float(true); // Convert from bool -> float
bool myBool = bool(1); // Convert from int -> bool

同样,向量也可以使用构造器,向量构造器的参数传递有两种基本的用法

  • 如果只为向量构造器提供一个标量参数,则该值涌来设置向量的所有值
  • 如果提供了多个标量或者向量参数,则向量的值从左到右使用这些参数设置
vec4 myVec4 = vec4(1.0); // {1.0, 1.0, 1.0, .10}
vec3 myVec3 = vec3(1.0, 1.0, 1.0); // {1.0, 1.0, 1.0}
vec3 temp = vec3(myVec3); 
vec2 myVec2 = vec2(myVec3); // {myVec3.x, myVec3.y}
myVec4 = vec4(myVec2, myVec3); // {myVec2.x, myVec2.y, myVec3.x, myVec3.y}
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(2.0));

向量和矩阵分量

向量的单独分量有两种访问形式:使用“.”运算符或者使用数组下标“[]”,根据组成的向量的分量数量,每个分量可以通过使用{x,y,z,w}, {r,g,b,a} 或者{s,t,p,q}组合访问,可以根据向量中不同的数学意义比如向量、颜色值、纹理坐标。但是不能,把这三种访问形式混合使用,比如用x表示第一个分量,用g表示第二个分量

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

在数组下标访问形式中,元素[0]对应x,元素[1]对应y...
同时,矩阵可以看成是由向量组成的,mat2可以看成是两个vec2组成的,mat3是由三个vec3组成的。
在矩阵中,单独一列可以用数组下标“[]”来访问,然后每一行通过上面介绍的向量的方式来访问

mat4 myMat4 = mat4(1.0)
vec4 col_0 = myMat4[0];
float m_1_1 = myMat4[1][1];
float m_2_2 = myMat4[2].z;

常量

GLSL中可以使用常量,常量是着色器语言中不变的值,在程序中不能被修改,声明常量时,需要加入const修饰符,并且常量在声明时必须初始化。

const float zero = 0.0;
const mat4 myMat = mat4(1.0);

结构

GLSL除了提供基本类型之外,还可以和C语言一样提供结构体

  struct fragStruct {
      vec4 color;
      float start;
      float end;
  } fragVar;  //定义了一个fragStruct的结构体类型和fragVar的结构体变量
fragVar = fragStruct( vec4(0.0, 1.0, 1.0, 1.0),    // color
                                  0.5,  // start
                                  2.0); // end
vec4 color = fragVar.color;
float start = fragVar.start;
float end = fragVar.end;

运算符

除了 == 和 != 之外,比较运算符(<, <=, >, >=)只能用于标量,要比较向量,可以使用内建函数,针对每个分量进行比较

函数

函数的声明和C语言的一样,函数的使用必须要先提供函数原型声明。还有一点跟C语言的差别很大:函数参数的传递方式,GLSL对函数的传参提供特殊的修饰符,定义函数是否可以修改可变参数

限定符 描述
in(默认) 指定参数按值传递,函数不能修改
inout 指定参数按引用传递,如果被修改,原参数会被修改
out 该变量的值不被传入函数,函数返回时将被修改
vec4 myFunc(inout float myFloat, out vec4 myVec4, mat4 myMat4);

注意:GLSL中的函数不能递归

内建函数

GLSL中最强大的功能之一就是提供的内建函数,比如用dot来计算两个向量的点乘,用pow来计算标量的幂次

控制流语句

GLSL可以使用if-else的控制逻辑,还可以使用while和do-while循环

if(color.a < 0.5) {
  color *= color.a;
} else {
  color = vec4(1.0);
}

精度限定符

精度限定符可以指定着色器变量的精度,变量可以声明为低、中、高精度,这些限定符允许编译器在比较低的范围和精度上进行计算,在较低的精度上,有些OpenGL ES实现在运行着色器时可能会更快,或者电源效率更高,当然这种性能的提升是以降低精度为代价的。

精度限定符的关键字是:lowpmediumphighp

highp vec4 position;
lowp vec4 color;
mediump float exption; 

可以设置变量的默认精度,如果变量声明时没有使用精度限定符,将会拥有该类型的默认精度,默认精度可以在顶点或者片段着色器的开头指定:

precision highp float;
precision mediump int;

同时,在顶点着色器中,如果没有指定默认精度,int 和 float 值的默认精度都是highp,但是在片段着色器中,float没有默认的精度,每个着色器必须声明一个默认的float精度,或者为float变量手动指定精度。
最后需要注意:指定的精度根据不同的实现有不同的范围和精度,具体的范围可以根据OpenGL ES 的API来获取,例如在PowerVR SGX GPU上,lowp float变量用10位表示,medium float用16位表示,而highp用32位来表示。

限定符

GLSL中有4个限定符可以使用

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

推荐阅读更多精彩内容