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

时间:2022-07-26
本文章向大家介绍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/