0%

Shader 入门:GLSL ES(迭代、选择和跳转)

前言

在上一章节中我们说到了 GLSL ES 的【运算符和限定符】,那么本章节就来到了【迭代、选择和跳转】的内容。

上一篇:《Shader 入门:GLSL ES(运算符和限定符)》

写《Shader 入门:GLSL ES》系列文章主要目的为让没怎么接触过 GLSL ES 的读者快速入门这门语言。

同时我将默认读者有编程基础,不会对语言内容进行完全的讲解以节省篇幅。

*另外本系列文章中主要针对 GLSL ES 3.0 进行讲解


正文

迭代(Iteration)

循环语句(Loop Statement)

在 GLSL ES 中有以下 3 种循环语句

for

首先执行初始化表达式,当条件表达式为 true 时执行循环体,之后再执行循环表达式,然后再次进行条件判断,循环往复,直到条件表达式为 false 时结束循环。

for (初始化表达式; 条件表达式; 循环表达式) {
// 循环体...
}

// 如下:
int a = 0;
for (int i = 0; i < 10; i++) {
a++;
}
// a = 10

while

只要条件表达式为 true 就执行循环体,直到条件表达式为 false 时结束循环。

while (条件表达式) {
// 循环体...
}

// 如下:
int a = 0;
while (a < 10) {
a++;
}
// a = 10

do-while

先执行一次循环体,之后只要条件表达式为 true 就继续执行循环体,直到条件表达式为 false 时结束循环。

do {
// 循环体...
} while (条件表达式)

// 如下:
int a = 0;
do {
a++;
} while (a < 0)
// a = 1;

💥循环上限必须明确

需要注意的是,在 GLSL ES 循环语句的条件表达式中,循环的最大次数必须是明确的,如下面的栗子:

// 表达式使用常量
// 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 时执行下方的语句块。

if (布尔表达式) {
// 语句...
}

// 如下:
int a = 0;
if (a == 0) {
a++;
}
// a = 1

if-else

当条件表达式为 true 时执行第一个语句块,为 false 时则执行 else 后面的语句块。

if (布尔表达式) {
// 语句...
} else {
// 语句...
}

// 如下:
int a = 0;
if (a == 1) {
a++;
} else {
a += 2;
}
// a = 2

switch(GLSL ES 3.0 新增)

switch 语句中的初始化表达式必须为整数,如果 case 标签的值与之相等,则执行标签后面的语句。

当没有匹配的 case 标签时,有 default 标签则执行 default 标签后面的语句,没有则跳过。

初始化表达式的类型必须与所有 case 标签的类型相等,可以使用的类型为 intuint,且不会进行隐式类型转换(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 循环),然后执行下一次循环。

int a = 0;
for (int i = 0; i < 10; i++) {
if (i == 6) {
continue;
// 当 i 为 6 时不会执行后面的语句
}
a++;
}
// a = 9

break

break 可用于循环和 switch 语句中,执行该语句时将立即退出最内层循环,不再继续执行循环。

int a = 0;
for (int i = 0; i < 10; i++) {
if (i == 6) {
break;
// 当 i 为 6 时直接跳出循环
}
a++;
}
// a = 6

return

return 可以用在函数(Function)里的任何位置,执行该语句会直接跳出当前函数。如果 return 有表达式,则会返回表达式的值。

int plus(int a, int b) {
return a + b;
}
// int c = plus(1, 2);
// c = 3

discard

discard 只能在片元着色器(Fragment Shader)中使用,执行该语句将会直接跳出片元着色器,丢弃当前片元。

片元被丢弃之后就不会被渲染出来了,就好像是完全透明了一样~

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/


传送门

微信推文版本

个人博客:菜鸟小栈

开源主页:陈皮皮

Eazax-CCC 游戏开发脚手架

Eazax-CCC 示例在线预览


系列文章

《Shader 入门:GLSL ES(简介和基本语法)》

《Shader 入门:GLSL ES(数据类型)》

《Shader 入门:GLSL ES(运算符和限定符)》


更多分享

《为什么选择使用 TypeScript ?》

《高斯模糊 Shader》

《一文看懂 YAML》

《Cocos Creator 性能优化:DrawCall》

《互联网运营术语扫盲》

《在 Cocos Creator 里画个炫酷的雷达图》

《用 Shader 写个完美的波浪》


公众号

菜鸟小栈

😺我是陈皮皮,一个不断学习的游戏从业者,一个热爱分享的 Cocos Star Writer。

🎨这是我的个人公众号,专注但不仅限于游戏开发和前端技术分享。

💖每一篇原创都非常用心,你的关注就是我原创的动力!

Input and output.

欢迎关注我的其它发布渠道