前言
上一篇文章「手撸一个星系,送给心爱的姑娘!(Three.js Shader 粒子系统实现)- 牛衣古柳 - 20240417」里古柳带大家用 Three.js Shader 粒子系统实现了这个非常漂亮的星系效果。文章在上周三发布后上了掘金热榜,并且截至目前点赞数、收藏数双双破百,成为本 Shader 系列教程里阅读量最高的一篇(马上破万)。果然大家更喜欢看这种实现完整、实际、酷炫效果的文章,这倒是和我设想的一样。
正好上篇讲到粒子系统,我想不妨继续趁热打铁讲解下这个带我入坑 Shader 的效果 Pepyaka
,想来这应该也是很多前端、程序员梦寐以求想要实现的酷炫效果吧。下图是本文最终实现的效果,GIF 不够清晰,大家可以去 Codepen 查看源码和效果,代码后续也会同步到 GitHub。
Pepyaka
这个效果本来出自于 Grant Yi 的个人网站,其实我一直不知道这个词到底啥意思,但就当成该效果的代名词这么叫着。可惜原网站换成了这个效果,虽然仍是 Shader 实现、同样酷炫,但看不到 Pepyaka
还是可惜。
在「断更19个月,携 Three.js Shader 归来!(上)- 牛衣古柳 - 20230416」一文里我提过,当初入门 Three.js 后因为对粒子系统感兴趣,于是在油管搜教程,然后看到 Yuri Artiukh
复现 Pepyaka
的教程——「#s3e6 ALL YOUR HTML, Making Pepyaka with Three.js - 20191201」——在视频里残存的片段里窥见到 Pepyaka
如此丝滑、酷炫、漂亮的效果,于是入坑 Shader,再然后有了现在输出 Shader 教程这桩事。
时隔两年古柳终于可以在原视频的基础上融入自己会的一些 Shader 效果,更近一步实现出更贴近原作的各种效果,并通过文章教给大家,让大家也能上手实现这样酷炫的效果。
中心球体
闲言少叙,进入正题。让我们同样从线框模式下的白色球体开始讲起。
1 | js复制代码import * as THREE from "three"; |
顶点偏移
接着我们用 noise 噪声函数对顶点位置进行偏移从而改变球体形状、并用 noise 值控制 HSV 颜色模式里的 Hue 色相值,这些内容在「手把手带你入门 Three.js Shader 系列(六) - 牛衣古柳 - 20231220」、「手把手带你入门 Three.js Shader 系列(七) - 牛衣古柳 - 20240206」两篇文章里已经详细地讲过,大家可以去自行学习。
这里简单过一遍,我们谷歌搜索 GLSL noise function
,从这里拷贝 Simplex 4D Noise
函数,接着对每个顶点产生一个 noise 数值(不同 noise 函数返回的值范围可能是0-1,也可能是-1-1,大家可以用第七篇里讲的方法去查看,这里不太重要也就不带大家看了),将该数值乘上法线 normal 作为在该偏移方向上偏移的程度,再加上原始 position 就是偏移后的顶点坐标。
1 | C#复制代码// vertex shader |
这里用接收 vec4 的 snoise 函数方便后续把 uTime 作为第4个参数,让形状动起来。
用法线 normal 作为颜色方便看变形后的形状。
1 | C#复制代码// vertex shader |
给 position 乘不同值,改变传给 noise 函数的顶点相邻程度,使形状变化更加“剧烈”。
1 | C#复制代码// float noise = snoise(vec4(position * 1.0, 0.0)); |
增加球体细分数,使球体上有更多顶点可以被用来偏移位置。
1 | js复制代码// const sphereGeometry = new THREE.SphereGeometry(1, 32, 32); |
将 uTime 作为第4个参数使形状实时发生变化。
1 | C#复制代码float noise = snoise(vec4(position * 10.0, uTime * 0.2)); |
noise 值作为颜色
noise 数值除了用来偏移顶点坐标,还能用来设置颜色。将 vec3(noise) 灰度值颜色作为 vColor 传到片元着色器进行使用,那么 noise 值越大偏移高度越高、颜色越白;反之值越小高度越低、颜色越黑。
1 | C#复制代码// vertex shader |
也可以设置成 rgb 里的 red 值,就是红黑效果。
1 | C#复制代码vColor = vec3(noise, 0.0, 0.0); |
还可以把 noise 值设置到 HSV 模式里的 hue 色相值(第七篇里都讲过,不过那里用的 HSL 这里用的 HSV,需要注意 HSV=HSB!=HSL),然后转换回 rgb 模式。谷歌搜索 glsl hsv2rgb function
找到现成的实现,拷贝后就能使用,直接把 noise 作为 hue 会是五彩斑斓的效果,因为 noise 值0-1的话会把所有色相值覆盖到。虽然也挺好看,但不是我们这里想要的。
1 | C#复制代码vec3 hsv2rgb(vec3 c) { |
缩小 noise 范围,使颜色变化不那么剧烈。
1 | C#复制代码vColor = hsv2rgb(vec3(vNoise * 0.1, 1.0, 1.0)); |
最后微调 hue,改下饱和度,至效果满意即可。
1 | C#复制代码vColor = hsv2rgb(vec3(noise * 0.1 + 0.04, 0.8, 1.0)); |
球形粒子系统
中心的效果完成后,接着在外面加一层球形的粒子,之前有群友在学第七篇时就有问到,这次终于可以讲解下(欢迎加我「xiaoaizhj
」,备注「可视化加群
」,一起交流)。
上一篇文章「手撸一个星系,送给心爱的姑娘!(Three.js Shader 粒子系统实现)- 牛衣古柳 - 20240417」对粒子系统已经做过介绍,这里就不再重复。
我们通过 BufferGeometry() 设置粒子的 position,使 radius 稍大于中心球体,这样能包裹着球体。这里想要粒子在球体上均匀分布,从网上找现成的公式即可。
1 | js复制代码const particleGeometry = new THREE.BufferGeometry(); |
视频里用的是某博客文章里的公式,由于文章已看不到,这里放截图方便感兴趣的小伙伴看眼,总之把这里 python 代码改成上面 JS 代码即可。
我们也可以搜 evenly distribute points on a sphere
、fibonacci spiral sphere
等关键词,能找到其他大同小异的实现方式,下面是另一种方案,作为参考,可以看到效果都差不多。后续演示仍沿用第一种方案。
1 | js复制代码// 另一种生成球体上均匀粒子坐标的方式 |
材质用 ShaderMaterial,粒子颜色设置了透明度,然后和 particleGeometry 一起丢给 Points 就行。
1 | js复制代码const particleVertex = /* GLSL */ ` |
不设置透明度的话效果如下。还是设置透明度、弱化粒子视觉效果后看着更舒服。
1 | C#复制代码gl_FragColor = vec4(vec3(1.0), 1.0); |
粒子上下波动
接着让粒子运动起来,可以通过对 y 坐标取 sin 值再加回到 y 上,这样同一高度的粒子会一起随 sin 波浪上下偏移,整体上就是波浪的效果。
1 | C#复制代码// particleVertex |
还可以对 z 坐标进行一定偏移,虽然这里可能效果不明显,但常常会组合的对 xyz 进行 sin/cos 等操作,以后大家也会碰到(实际上后文就多次重复用到),所以这里先演示下。大家也可以自由发挥、随意尝试,不必局限本文所讲到的方法。
1 | C#复制代码newPos.z += 0.05 * sin(newPos.y * 10.0 + uTime); |
效果已经很漂亮了,大家可以休息下,喝杯奶茶或咖啡,好好欣赏享受下自己的成果。
背景用随机粒子进行点缀
上面都是油管教程里涉及到的内容,休息结束后,这次让我们加个餐、更进一步把原作里其他一些效果也简单实现下。
首先可以看到背景有点空,我们可以用随机粒子进行点缀,丰富画面效果。
通过 r 使得粒子在中心球体和球形粒子之外的范围,xyz 坐标随机在立方体空间内分布,这里都是简单的设置,所以怎么方便怎么来;sizes 控制每个粒子的随机大小并且会用作粒子移动速度,为了使值不为0,这样粒子速度不为0就不会停着不动,所以加了0.4。
1 | js复制代码const firefliesGeometry = new THREE.BufferGeometry(); |
顶点着色器里 gl_PointSize 乘上 aSize 改变大小,片元着色器里用每个粒子离中心的距离通过一个反比例函数 0.05/d-0.05*2.0
使得靠近中心为1,往外逐渐变成0,再设置成透明度值,从而实现出光斑、模糊圆形的效果。
1 | js复制代码const firefliesVertexShader = /* GLSL */ ` |
通过曲线图看看透明度 strength 计算方式,当距离d=0.5时数值为0也就是圆圈边缘;d>0.5时数值为负数,通过clamp会变成0,完全透明;d=0.05/1.1=0.045时数值为1;d<0.045时数值大于1会被clamp取1,完全不透明,距离从0.045到0.5透明度快速降为0,从而实现模糊效果。
1 | C#复制代码float d = length(gl_PointCoord - vec2(0.5)); |
这种粒子实现方式还蛮常见。记得2022年4-5月份刚接触 shader 那会,碰上浙大125周年校庆,发现当年很特别官方搞了个求是星海
的网站效果,就是粒子系统为主,里面粒子颜色透明度就是用这种反比例函数实现。
通过 Chrome 浏览器的 Spector.js
插件就能看到这里粒子的着色器里的 starIntensity()
函数,就是如此实现的。
在该网站输入校友专业和名字、认证通过后会生成每个校友专属的由“灿若繁星”的浙大人姓名的粒子系统组成的效果。网页还在,但需要输入信息才能生成,所以非校友的话看不到,只能看这里截图。
扯回来,和上面球形粒子一样这里用 sin 函数使背景随机粒子也运动起来,参数可以任意修改看效果。
1 | C#复制代码vec3 newPos = position; |
显示文字
接着模仿原作在中心球体和球形粒子之间放上文字。这里通过在长度2宽度1的 Plane 上显示纹理图片实现,准备一张白色文字、背景透明、1024x512 的图片。下面是截图的效果,而不是原图,不要保存这张图去直接使用。原图已传到 postimg 这个网站,下面是链接直接用就行。如果后续想将本文的效果修改后放自己的个人网站,那么这里就可以和原作一样换成自己的名字。
将图片通过 TextureLoader().load()
加载后作为 uTexture
传给 shader,然后通过 uv 采样纹理图,再搭配 textMaterial 设置 transparent 即可显示文字。text mesh 通过设置 position.z=1.7 移动到所需位置。
1 | js复制代码const textGeometry = new THREE.PlaneGeometry(2, 1, 100, 100); |
接着再一次应用 sin 函数改变顶点位置使 plane 发生扭曲,顶点移动后 uv 也随之移动,用 uv 采样纹理后就会有文字飘浮的效果。
1 | C#复制代码vec3 newPos = position; |
上方光线
最后古柳观察到原作顶部有光线效果,于是也顺带实现了下,不过因为文章篇幅已经不短,加上这部分解释起来比前几个效果复杂些,就暂时不在本文讲了,后续其他文章有机会再讲。大家这里看眼效果即可,感兴趣可自行实现。
小结
至此,古柳带大家把这个自己入坑 shader 的酷炫效果“简单”复现了下,中间偷懒多次用了 sin 函数,所以其实并没有大家想的那么复杂。
虽然和原作比起来还是很粗陋,各种参数、粒子动画、颜色搭配都有很大优化空间,效果远远不如原作漂亮,但是作为一篇教程里的例子,或许已足够让大家学到些东西,其他的优化就留给大家去自由发挥了。
还是可惜原作早就看不到了,只能从油管视频里瞥见一些片段效果,无法自己去交互、去体验、去学习。但两年前初见 Pepyaka
、初识 shader 时的那份惊艳却伴随古柳至今,希望更多人看到本文后也能感受到那份惊艳,并能借本教程之力让那份惊艳不再局限于观赏,而是可以自己去一步步实现、一步步靠近,相信大家跟着古柳的系列文章一点点学下来,就不会再觉得这样的效果是自己无法理解、无法实现的了吧。
最后完整源码可见 Codepen。
相关阅读
「手把手带你入门 Three.js Shader 系列」目录如下:
- 「断更19个月,携 Three.js Shader 归来!(上)- 牛衣古柳 - 20230416」
- 「断更19个月,携 Three.js Shader 归来!(下)- 牛衣古柳 - 20230421」
- 「手撸一个星系,送给心爱的姑娘!(Three.js Shader 粒子系统实现)- 牛衣古柳 - 20240417」
- 「手把手带你入门 Three.js Shader 系列(八)- 牛衣古柳 - 20240229」
- 「手把手带你入门 Three.js Shader 系列(七)- 牛衣古柳 - 20230206」
- 「手把手带你入门 Three.js Shader 系列(六)- 牛衣古柳 - 20231220」
- 「手把手带你入门 Three.js Shader 系列(五)- 牛衣古柳 - 20231126」
- 「手把手带你入门 Three.js Shader 系列(四)- 牛衣古柳 - 20231121」
- 「手把手带你入门 Three.js Shader 系列(三)- 牛衣古柳 - 20230725」
- 「手把手带你入门 Three.js Shader 系列(二)- 牛衣古柳 - 20230716」
- 「手把手带你入门 Three.js Shader 系列(一)- 牛衣古柳 - 20230515」
照例
如果你喜欢本文内容,欢迎以各种方式支持,这也是对古柳输出教程的一种正向鼓励!
最后欢迎加入「可视化交流群」
,进群多多交流,对本文任何地方有疑惑的可以群里提问。加古柳微信:xiaoaizhj
,备注「可视化加群」
即可。
欢迎关注古柳的公众号「牛衣古柳」
,并设置星标,以便第一时间收到更新。
本文转载自: 掘金