作者meowyih (meowyih)
看板GameDesign
标题[程式] 超新手 shader language 教学文 (四)
时间Wed Sep 29 20:09:18 2021
有看完前三篇的新手,
是不是想说:
"我学 shader,
是想画出浩克大战索尔的3D动画,
怎麽学了半天只画了一个雷射光?!
我阿嬷教得都比你快啦!"
虽然我保证看完这篇,
你还是画不出来复仇者联盟的浩克,
但是要画出
守护者罗夏的脸(Rorschach in Watchmen)
应该没问题的唷! (笑)
写程式,
一定知道乱数(random)是甚麽。
写 shader,
也一定会听过杂讯/噪音(noise)这个词。
这二个有甚麽不同?
这是乱数
https://i.imgur.com/sgywCet.png
这是杂讯
https://i.imgur.com/vPWjuzI.png
... huh? XD
让我们先做一个简单的乱数函式吧
乱数
下面函式的功用,
是输入一个值 float x,
然後输出个零到一间的乱数。
演算法是把 sin(x)
把第7位以後的小数点
当作小数点的第1位,
像这样
sin(x) = 0.4235238812
return 0.8812
https://i.imgur.com/4j6bxXw.jpg
画图是用 uv.x 取得乱数 random 後,
在 uv.x 的 x 轴画一条长度为 random 的直线。
https://i.imgur.com/sgywCet.png
杂讯
而杂讯是比较平滑的乱数,
意思是当输入的乱数种子是连续的数字的时候,
(ex: 前一个例子 uv.x 就是个从 0 连续到 1 的数字)
希望得到的是 "稍微" 有点规律的结果。
做法请看程式说明,
程式是取二个相邻的乱数,
然後用二者间的小数值作内插。
(读程式比较好懂)
https://i.imgur.com/LlUU92K.jpg
用和前面完全一样的方法,
画出的结果是
https://i.imgur.com/vPWjuzI.png
一维的杂讯用处不大,
但是这是二维乱数和二维杂讯的基础。
接着看二维的乱数和杂讯。
二维杂讯
二维杂讯指的是这样的杂讯函式
float noise2d( vec2 x );
而三维 (float noise3d( vec3 x ))
、四维 (float noise4d( vec4 x ))、
以至於更高维度的差别,
也都只是输入的是几维的阵列而已。
下面是个简单的二维乱数和二维杂讯的写法。
https://i.imgur.com/6OHGMbH.jpg
乱数的产生跟一维的差不多,
如果看不懂 dot (内插) 是甚麽,
请去LINE国中老师道歉!
开玩笑的,
程式有简单的说明,
再想不起来,
试试看 a.b = |a| |b| cos(O) 有没有印象。
杂讯也跟一维的差不多,
差别是本来是一维是二个点间的内插,
二维变成四个点 (也就是平面上正方形顶点)。
因为接下来程式开始不是几行就能写完的了,
我也没办法每个演算法都重写一次了,
让我偷懒用剪贴的,
抱歉。
我们把每个 uv 的点的乱数画出来
乱数code
https://i.imgur.com/ZoroeHF.jpg
乱数画面
https://i.imgur.com/optWAiL.png
然後把每个 uv 的杂讯画出来
杂讯code
https://i.imgur.com/gEUAQcC.jpg
杂讯画面
https://i.imgur.com/4JZmYjb.png
比较一下画面,
乱数就真的是平均分配的 0~1 的随机数字,
杂讯每个值都跟相邻的值有关系,
所以?
当要画一面有脏污的墙壁时,
要用到的是杂讯。
要画沙地上的随机散布的闪光时,
要用到的是乱数。
多维杂讯的常见运用
前面我们只是单纯的把某个 uv 当种子,
然後在 uv 位置上画出数值,
实际上的运用是可以很多元的。
1. 把某一个维度当作时间变成动画效果。
我们还是运用前面那个 2D 杂讯的函式,
配合下面这个简单的程式,
程式有一行是画乱数动画用,
一行可以画杂讯动画,
一次执行一行就好。
https://i.imgur.com/RgA34yV.jpg
Visualized 2D random numer
https://www.youtube.com/watch?v=oSNcR1Z-_NQ
Visualized 2D Noise
https://www.youtube.com/watch?v=Z87XGQxGUPg
如果觉得画面顿顿的,
那是录影和youtube的技术问题,
在 ShaderToy 上杂讯跑起来风骚的勒 (误)。
同样的逻辑,
我们把三维的杂讯画成 3D 模组,
大概就是一堆不规则的 3D 雾气。
但如果把其中一维当时间,
就可以画出2D变形虫般的平面动画。
所以我们为什麽要四维的杂讯?
我们可以把其中一维当时间,
可以做成一陀会蠕动变形的史莱姆,
就不用辛苦建立模组了~
2. 把回传结果当作高度
在一个 3D 世界的水平平面上,
我们对每一个点做3维杂讯,
然後把回传的值当作高度,
形成的就是一堆的高山群。
自动产生地形在实务上,
最简单的方法是用 vertex shader 做,
我也不知道这系列会不会写到 vertex shader,
希望会罗。
上面二种外当然还有很多很多,
像是把杂音回传做成颜色就是一个例子。
常用到的杂讯演算法
下面几个是在各种游戏引擎里,
常会看到的杂讯演算法,
我不熟历史,
所以如果有错得请见谅。
Perlin Noise (1983)
1983 年 Perlin 发表的杂讯演算法,
有专利不能乱用,
使用类似上面提到的乱数内插法,
应该有支援到四维以上的杂讯。
Simplex Noise (2001)
Perlin 把自己的演算法改善後的东西,
最明显的差别是,
本来用正方形/正方体的顶点做内插的做法,
改成三角形/三角锥的顶点内插,
想像一下就知道有效能很大的改进。
因为是同一个人做的,
所以还是常被叫 Perlin Noise,
一样有专利! 不能乱用~
(Unity 好像有 Perlin 名字的 API,
不知道是不是付钱给他了?)
Open Simplex Noise (2004)
Kurt Spencer 在 GitHub 上分享的演算法,
可以用在各种 Project 上,
不用怕被告~
世界会进步就是有这种无私的大大 T_T
令人敬佩。
Open Simplex Noise 其实还是 Simplex Noise,
但不管是为了效能,
还是为了避开专利,
有改了不少地方,
GitHub 上是 Java 写的,
现在当然各种语言的版本都有了。
不管是哪种,
使用上基本上都是:
float noise( vec2 x );
float noise( vec3 x );
float noise( vec4 x );
引擎里用杂讯的各种参数
游戏引擎中,
杂讯演算的参数,
大抵上都是设定要取样到多细,
请参考下面 1D 杂讯的code (前面用过的)
https://i.imgur.com/LlUU92K.jpg
在第 23 行的地方,
写了 uv.x * 100,
为什麽要 *100?
因为要 zoom-out,
为什麽要 zoom-out?
因为我们找 "相邻" 的二个乱数值 (第12行)
是用 1 当间隔的,
间隔越小,
杂讯越细致,
间隔越大,
杂讯越粗旷,
请自己改这个值试试就懂了,
如果不乘100,
就只是个有点平滑的上坡而已。
不管是几维的杂讯,
间隔的大小都直接影响到杂讯的 "外型"。
所以实际上在使用时,
引擎或 API 都会让使用者调整的。
罗夏的脸
跟着我这样做!
使用三维杂讯,
把其中一维当作时间,
另外二个当 uv,
先画图再做镜像,
需要的话用 round 让黑白分明点,
看,就是这麽简单!
https://i.imgur.com/zGHwpAb.png
......
好啦,
我知道我开头有说要写,
但时间不够,
让我过几天找时间再写。
抱歉啦~
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 61.228.98.231 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/GameDesign/M.1632917360.A.40C.html
1F:推 dklassic: XD 辛苦了 09/29 20:24
2F:→ dklassic: 看反应热烈你得再多写五篇了吧(? 09/29 20:24
反正我只是个写不出好游戏的老头,
既然有人看,
我就为了台湾的游戏界写个几篇吧! (T_T)
也许哪个不世天才
看了这几篇就横空出世,
自己写了个3A大作也不一定 XDDD
真的有这种天才的话别忘了游戏後面的
special thanks 提到我一下谢谢 m(_ _)m
3F:推 oopFoo: 赞!产生地形是用Fragment Shader没错。 09/29 21:08
※ 编辑: meowyih (61.228.98.231 台湾), 09/29/2021 21:33:26
4F:推 coolrobin: 30天铁人邦 (X 09/29 22:08
5F:推 louisalflame: 加油 对萌新帮助颇多 09/30 01:57
6F:推 OSDim: 这系列超优文== 09/30 02:08
7F:推 nicetw20xx: 系列文对没碰过的我满有趣的 09/30 08:36
8F:推 wangm4a1: 推 09/30 09:04
9F:推 CarpeDiemAL: 推推~~~ 09/30 09:46
10F:推 iLeyaSin365: 我想问:都听说技美必备会学shader(应该就是你写的 09/30 18:36
11F:→ iLeyaSin365: 程式码吧?),但问题是引擎不都能画雷射光(用调的) 09/30 18:36
12F:→ iLeyaSin365: 吗?,3dsmax ,substance的材质也是那麽如何把写sh 09/30 18:36
13F:→ iLeyaSin365: ader的知识跟那些连起来? 09/30 18:36
雷射光那篇的目的并不是要教怎麽话雷射光,
那篇只是要熟悉数学函式,
像是 abs/step/sin 等等。
从第一篇到现在这篇,
其实都只是在讲数学理论,
没有任何实务的应用。
因为实务的应用跟开发要怎麽做,
和使用哪种软体和引擎有关,
不管是 Unreal/Unity/Godot/Blender
或其他的人物模组软体,服装模组软体...etc.
每个软体都有差异,
我没打算写那部分的教学,
因为我也不是任何一种软体的专家 XD
shader 的中文翻译叫 "着色器",
顾名思义它可以用来制作和修改材质,
而像 substance 或 3dsmax 之类的软体,
它们为了让使用者在没学过相关知识的前题下也能使用,
软体里面已经有了很多很多的材质,
而且可以用视觉化的方法套用在3d模组上。
换句话说,
就是他们已经写好一堆 shader 给设计者用了。
像是你指定某个物件要套用某个材质,
这里面 shader 相关的应用在背後。
虽然他们也有自订 shader 的功能,
不过定义上算高级功能?
在这种前提下,
非软体相关的艺术工作者,
其实没有学 shader 的必要,
就算有,
顶多学学 "把某个材质和另一个材质相加" 之类的操作就够了,
对写成程式的人来说,
这句话也许代表了相乘,相加,相减...etc.
但不知道这些,
使用预设的材质也能做出很惊人的效果了。
所以你说美术工作者是不是要学 shader,
我觉得知道点当科普看看就好,
就像大厨也不用真的知道酱油是怎麽做的吧? XD
知道点大概,
哪天遇到做不出来的材质时,
再去研究?
※ 编辑: meowyih (61.228.98.231 台湾), 09/30/2021 19:08:58
※ 编辑: meowyih (61.228.98.231 台湾), 09/30/2021 19:11:54
※ 编辑: meowyih (61.228.98.231 台湾), 09/30/2021 19:12:58
15F:→ oopFoo: 但有限制在API 的 砂盒里。原理跟这里是一样的 09/30 20:36
16F:推 LayerZ: @iLey 写这东西你要想的是,画面上每个点在画的时候要怎麽 10/01 13:27
17F:→ LayerZ: 搞,这几篇都是从最最最基础入门,而不是在告你做雷射 10/01 13:28
18F:→ LayerZ: 我把我入门的东西也分享一下好了,虽然可能不适合新手.. 10/01 13:30
19F:推 johnny94: 跪求当成连载来写,好好看 10/03 01:06
20F:推 aegis123321: 等等perlin noise 1983有专利!? 刚刚喂狗只看到200 10/04 18:50
21F:→ aegis123321: 1的才有? 10/04 18:50
22F:推 scott20144: 优文 10/22 07:39