在一个像素世界中使用多个着色器
(本文未经许可禁止转载)
警告
该文章的做法已被废弃。请前往官方文档
目的
有时候我们做的效果,往往只使用一个着色器搞定不了。我们需要多个着色器叠加起来组成新的特效。比如着色器A的结果当作着色器B的输入,着色器B的结果需要当作着色器C的输入,最后着色器C的结果作为最终结果返回给Ae。
在像素世界中,这完全是可行的。让我们来看看是如何实现的。
世界的中枢Lua
这里我们需要用到一个全新的渲染模式:Lua渲染
与GLSL不同,Lua语言运行在CPU上,是像素世界的中心,连接了所有模块。
像素世界的所有渲染指令都可以通过Lua这个中枢发出。比如您想渲染GLSL代码,只需要输入
pw.glsl([==[
在这里输入您的代码
]==])
并在中间输入GLSL代码即可;
如果您想执行shadertoy的代码,只需要输入
pw.shadertoy([==[
在这里输入您的代码
]==])
并在中间输入来自shadertoy的代码即可。
这就为在同一个像素世界使用多个shader提供了可能性。
例
比如着色器A制作一个UV渐变图案,而着色器B则负责把A的结果反色。这种情况我们就需要这么写:
pw.glsl(
[==[
void main(){
outColor = vec4(uv,0,1);
}
]==]
);
pw.glsl(
[==[
void main(){
vec3 outlayerColor = getColor(OUTPUT_LAYER_INDEX,uv).rgb;
outColor = vec4(vec3(1)-outlayerColor,1);
}
]==],true
);
其中倒数第二行的true
代表:“我要使用上一个着色器的结果,不要把它删掉”
调用上一层着色器结果的方法就是:getColor(OUTPUT_LAYER_INDEX,uv)
,或者使用texture(outLayer,uv)
都可以。
- 注意:在v2.3.0及这之后的版本,我们提供了一个非常快的方法来渲染多个shader,请往下阅读。
高效化
使用上面的方法效率会非常低。
因为我们每执行一次代码,像素世界都会把结果返回给Ae一次!然后第二个shader还需要把结果再从Ae读回去!这很低效。
除非你想在两个shader之间想用Lua读取输出像素做点计算——而大部分时间我们没这个需求。我们想让GPU算完所有的特效再返回结果给Ae。
这时候你需要用下面的方式渲染:
pw.beginShaders()
pw.glsl(第一个glsl代码)
pw.glsl(第二个glsl代码)
...
pw.glsl(第N个glsl代码)
pw.endShaders()
我们在首尾分别加上pw.beginShaders()
和pw.endShaders()
,这样像素世界会在endShaders
给Ae汇报渲染结果,省去了中间每一步都返回一张图像的时间。
这会非常高效!它的原理跟我们玩游戏是一样的,像素世界会在beginShaders
和endShaders
之间建立游戏循环,使用纯GPU的形式,不断交换两张贴图进行计算。
小技巧:最后的
pw.endShaders
可以省略。如果你用了pw.beginShaders
但是没有用pw.endShaders
,像素世界会自动为你在最后一行加上。
pw.shadertoy
和pw.full_glsl
也同样支持高效化运算,你也可以混合使用这些渲染函数。比如:
pw.beginShaders()
pw.glsl(glsl代码)
pw.glsl(glsl代码)
pw.shadertoy(shadertoy代码)
pw.glsl(glsl代码)
pw.full_glsl(full_glsl代码)
pw.endShaders()
注意,这种情况下你不需要在结尾加上一小节提到的
true
。所有在beginShaders
和endShaders
之间的渲染函数都强制支持上一渲染器的输出结果(并且完全不耗费时间成本)。
图示: