用280字符建模世界
作为图形程序员,我的工作本质上是使用数学公式让像素变得更美观。我为游戏和软件中的动画背景处理光照、反射、后处理等视频效果。闲暇时,我喜欢编写能嵌入“推文”(280字符或更少)的紧凑着色器程序来放松身心。你可能在X/Twitter上见过一些这样的作品。将代码压缩同时保持功能的过程被称为“代码高尔夫”。
以下是我用仅197字符的GLSL代码编写的动画星系:
|
|
这段代码实时为屏幕上的每个像素运行,通过一些复杂的数学和逻辑生成独特的输出颜色。我使用名为Twigl.app的在线着色器编辑器构建这些演示,该工具专为分享微型着色器设计,能轻松导出视频,并在“极客”模式下处理通用头代码并缩短内置变量名。
我甚至将体素DDA光线追踪与边缘检测压缩到仅190字符:
|
|
动机
我编写这些微型着色器的原因有多方面:
- 好奇与热情:新想法涌现时,我喜欢在Twigl上涂鸦,降低期望值,避免过度规划。
- 学习与发现:约束条件下优化代码大小迫使我以不同方式思考问题,常找到简化或近似方法,并学会充分利用每个字节。
- 挑战:编写微小代码既刺激又具挑战性,保持大脑敏锐,意外学到大量数学知识。
- 社区:通过分享作品,我结识了艺术家、设计师、数学爱好者、游戏开发者等众多有趣人群。
简言之,这有趣、发人深省,是激发图形编程兴趣的好方法。
着色器简介
着色器是在GPU(图形处理单元)而非CPU(中央处理单元)上运行的程序。CPU擅长复杂或分支操作,按顺序计算;GPU设计用于并行处理每秒数十亿或数万亿的可预测操作。4K屏幕以60帧/秒输出近5亿像素/秒,每个像素可能涉及数百或数千次操作。
着色器有多种类型:顶点着色器、片段着色器、计算着色器等。这些推文着色器特指片段着色器(又称“像素着色器”),它们在每个像素上运行。片段着色器输入片段坐标(FC),输出颜色和透明度(RGBA)。输出颜色“o”有4个分量(红、绿、蓝、alpha),范围0.0到1.0。例如,(1.0, 1.0, 1.0, 1.0)为纯白,(0.0, 0.0, 0.0, 1.0)为不透明黑。
核心输入包括:
- “o”:输出颜色
- “FC”:片段坐标
- 统一输入(所有像素共享):
- “r”:屏幕分辨率(像素)
- “t”:时间(秒)
- “m”:鼠标位置(较少使用)
- “b”:后缓冲纹理
从此处开始,通过大量数学和逻辑控制输出颜色,生成酷炫图像。
我的流程
人们常问我是否从一开始就以紧凑形式编写着色器,还是先扩展后压缩。答案是前者。通过大量代码高尔夫练习,我更容易以紧凑形式原型化想法,且不会在微小着色器中迷失。代码高尔夫着色器需在代码大小、渲染性能、艺术吸引力、设计和数学函数间找到平衡,这挑战了我的左右脑。
开始时需有一个想法。例如编写“Milky”恒星着色器时,我知道想创建某种星系,这是初始火花。我的着色器通常从居中和缩放开始,以适应不同分辨率和宽高比。对于恒星,我循环100个绕中心旋转的点光源。发光效果很容易创建,只需知道当前像素到光源的距离,并使用倒数作为像素亮度(近像素更亮,远像素更暗)。
我使用三角函数调整粒子位置,并给圆盘轻微倾斜。着色时,我喜欢对RGB通道使用带相移的正弦波。正弦波也可用于选择伪随机数,从而为每个恒星选择颜色。最终,我使用了左侧第二个调色板的轻微变体,具有不错的温度和亮度范围。我还添加了恒星亮度变化,使图像更有趣。
最后,我应用双曲正切函数进行色调映射,防止颜色通道达到最大亮度值时的过度曝光和色调偏移。任何具有高动态范围光照的着色器都应应用色调映射,推文着色器也不例外!我还添加了动画,最终选择了收缩效果,并创建循环使新恒星在旧恒星到达中心时淡入。
代码高尔夫
在压缩代码的过程中,我开发了数百种小技巧:
- 缩减名称:习惯单字母变量和函数名,迫使重读代码并找到更好写法。
- 缩减数字:例如
1.0 == 1.,1000.0 == 1e3。向量构造函数中可使用任何数据类型作为输入(如vec4(1))。 - 最小化初始化:共享数据类型,避免float/int转换。
- 避免if语句:使用三元运算符替代,更短且功能强大。
- for循环优于while:for循环有初始化和小步进槽位,可免去分号。避免使用break,改用条件位。
此外,我还使用函数替换进一步减少代码。每个着色器都不同,需使用不同技巧、近似和概念,这正是乐趣所在!
问答
- 最喜欢的技巧? 目前是“湍流”,可用于魔法效果、云或火。
- 如何培养数学直觉? 需要时间和耐心,小块学习、休息和睡眠有帮助。
- 编写方式? 以代码高尔夫模式编写,因直觉已发展,原型制作更快。喜欢Twigl.app和ShaderToy。
- 如何开始? 始于对游戏开发的兴趣,着色器在游戏图形中有大量应用。
- 牺牲可读性后悔吗? 不,更关心导致代码变慢的大小优化,不介意不可读代码。
我的故事
从小对视频游戏感兴趣,尤其是“花哨”的3D图形。10岁左右朋友展示了GameMaker工具,学习了拖放编程、变量和条件基础。后来在GM中试验3D图形,尽管它主要是2D游戏引擎。这足以学习3D渲染和渲染管道基础。GameMaker引入“着色器”后,我开始在论坛上发布作品并获得反馈(感谢“xygthop3”的有用示例!)。
游戏开发是学习着色器的好地方,因有性能约束(不想游戏卡顿),并在上下文中学习整个渲染过程。2014年开始发布最早着色器教程,2015年探索ShaderToy后技能真正发展。2021年为GameMaker推出GLSL 1.00入门教程系列。现在发布更通用的图形教程,涵盖数学、艺术、设计、代码等。
感谢帮助过我的人:xygthop3(免费示例极大帮助)、Inigo Quilez(ShaderToy作者、射线行进之王)、Fabrice Neyret(最佳着色器代码高尔夫选手)、Yonatan “zozuar”(激励我尝试代码高尔夫)、Yohei Nishitsuji(微型分形传奇)。
武器库
以下是一些最喜爱的推文着色器:
- “Quantum”:量子效果
- 未命名分形着色器
- “Origami”:折纸效果
- 195字符抽象效果
- “Vortex”:漩涡效果
更多作品可见个人网站、X、Bluesky或Instagram。学习着色器可尝试我的教程,定制工作可联系我。
感谢阅读!祝愉快!
- Xor