前言
在上一章节中我们说到了 GLSL ES 的【运算符和限定符】,那么本章节就来到了【迭代、选择和跳转】的内容。
上一篇:《Shader 入门:GLSL ES(运算符和限定符)》
写《Shader 入门:GLSL ES》系列文章主要目的为让没怎么接触过 GLSL ES 的读者快速入门这门语言。 同时我将默认读者有编程基础,不会对语言内容进行完全的讲解以节省篇幅。 *另外本系列文章中主要针对 GLSL ES 3.0 进行讲解
正文
迭代(Iteration)
循环语句(Loop Statement)
在 GLSL ES 中有以下 3 种循环语句:
for
首先执行初始化表达式,当条件表达式为 true 时执行循环体,之后再执行循环表达式,然后再次进行条件判断,循环往复,直到条件表达式为 false 时结束循环。
代码语言:javascript复制for (初始化表达式; 条件表达式; 循环表达式) {
// 循环体...
}
// 如下:
int a = 0;
for (int i = 0; i < 10; i ) {
a ;
}
// a = 10
while
只要条件表达式为 true 就执行循环体,直到条件表达式为 false 时结束循环。
代码语言:javascript复制while (条件表达式) {
// 循环体...
}
// 如下:
int a = 0;
while (a < 10) {
a ;
}
// a = 10
do-while
先执行一次循环体,之后只要条件表达式为 true 就继续执行循环体,直到条件表达式为 false 时结束循环。
代码语言:javascript复制do {
// 循环体...
} while (条件表达式)
// 如下:
int a = 0;
do {
a ;
} while (a < 0)
// a = 1;
?循环上限必须明确
需要注意的是,在 GLSL ES 循环语句的条件表达式中,循环的最大次数必须是明确的,如下面的栗子:
代码语言:javascript复制// 表达式使用常量
// int max = 20; // [×] 变量可被更改
const int max = 20; // [√] 常量不可被更改
for (int i = 0; i < max; i ) {
// ...
}
// 或者直接使用字面量
for (int j = 0; j < 20; j ) {
// ...
}
因为 GLSL ES 在编译时,编译器会对着色器代码中的 for 循环进行内联展开(Inline Expansion)以提高着色器的执行性能。
所以如果循环的次数不能确定的话就没有办法展开了呢~
选择(Selection)
选择语句(Selection Statement)
在 GLSL ES 中有以下三种选择语句:
if
当条件表达式为 true 时执行下方的语句块。
代码语言:javascript复制if (布尔表达式) {
// 语句...
}
// 如下:
int a = 0;
if (a == 0) {
a ;
}
// a = 1
if-else
当条件表达式为 true 时执行第一个语句块,为 false 时则执行 else 后面的语句块。
代码语言:javascript复制if (布尔表达式) {
// 语句...
} else {
// 语句...
}
// 如下:
int a = 0;
if (a == 1) {
a ;
} else {
a = 2;
}
// a = 2
switch(GLSL ES 3.0 新增)
switch 语句中的初始化表达式必须为整数,如果 case 标签的值与之相等,则执行标签后面的语句。
当没有匹配的 case 标签时,有 default 标签则执行 default 标签后面的语句,没有则跳过。
代码语言:javascript复制初始化表达式的类型必须与所有 case 标签的类型相等,可以使用的类型为
int
和uint
,且不会进行隐式类型转换(Implicit Type Conversion)。
switch (初始化表达式) {
case 常量表达式:
// 语句...
break;
// ...
default:
// 语句...
}
// 如下:
int a = 2;
switch (a) {
case 1:
a = 1;
break;
case 2:
a = 2;
break;
default:
a = 10;
}
// a = 4
⚡注意
过多的 if 或 if-else 语句会减慢着色器的执行速度,在着色器编写时需要注意这一点。
跳转(Jump)
跳转语句(Jump Statement)
在 GLSL ES 中有以下几种跳转语句:
continue
continue 只可用于循环中,执行该语句时会跳过最内层循环,并执行循环表达式(for 循环),然后执行下一次循环。
代码语言:javascript复制int a = 0;
for (int i = 0; i < 10; i ) {
if (i == 6) {
continue;
// 当 i 为 6 时不会执行后面的语句
}
a ;
}
// a = 9
break
break 可用于循环和 switch 语句中,执行该语句时将立即退出最内层循环,不再继续执行循环。
代码语言:javascript复制int a = 0;
for (int i = 0; i < 10; i ) {
if (i == 6) {
break;
// 当 i 为 6 时直接跳出循环
}
a ;
}
// a = 6
return
return 可以用在函数(Function)里的任何位置,执行该语句会直接跳出当前函数。如果 return 有表达式,则会返回表达式的值。
代码语言:javascript复制int plus(int a, int b) {
return a b;
}
// int c = plus(1, 2);
// c = 3
discard
discard 只能在片元着色器(Fragment Shader)中使用,执行该语句将会直接跳出片元着色器,丢弃当前片元。
代码语言:javascript复制片元被丢弃之后就不会被渲染出来了,就好像是完全透明了一样~
void main() {
if (v_FragColor.a < 0.1) {
discard;
// 不透明度小于 0.1 时丢弃当前片元
// 不执行后面的语句
}
gl_FragColor = v_FragColor;
}
相关资料
OpenGL ES Registry(OpenGL ES 资料页)https://www.khronos.org/registry/OpenGL/index_es.php
OpenGL ES 3 Quick Reference Card(OpenGL ES 3 快速参考卡片)https://www.khronos.org/files/opengles3-quick-reference-card.pdf
GLSL ES Specification 3.00(GLSL ES 规范 3.0)https://www.khronos.org/registry/OpenGL/specs/es/3.0/GLSL_ES_Specification_3.00.pdf
OpenGL ES 3.0 Online Reference Pages(OpenGL ES 3.0 在线参考页)https://www.khronos.org/registry/OpenGL-Refpages/es3.0/