跳到主要内容

fireworks shaders

· 阅读需 6 分钟
Yangbingrui
Front End Engineer @ grg

github

Notes

  • gl_PointSize *= 1.0 / -viewPosition.z; 这个有什么用? 越远的点越小,近的点越大。

  • modelMatrix

  • viewMatrix

  • projectionMatrix

Three.ShaderMaterial 自带上面3个矩阵。

  • position 是模型在空间中的位置。自行设置
  const geometry = new THREE.BufferGeometry()
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positionsArray, 3))
  • 模型矩阵:模型矩阵是用来将模型从局部坐标系转换到世界坐标系的矩阵。
  • 视图矩阵:视图矩阵是用来将世界坐标系转换到视图坐标系的矩阵。
  • 投影矩阵:投影矩阵是用来将视图坐标系转换到裁剪坐标系的矩阵。
  • 位置:位置是模型在空间中的位置。

uResolution 是视图的分辨率。 这个例子里面,点的大小是根据视图的分辨率来设置的。

const sizes = {
width: window.innerWidth,
height: window.innerHeight,
pixelRatio: Math.min(window.devicePixelRatio, 2)
}
sizes.resolution = new THREE.Vector2(sizes.width * sizes.pixelRatio, sizes.height * sizes.pixelRatio)

window.addEventListener('resize', () => {
// Update sizes
sizes.width = window.innerWidth
sizes.height = window.innerHeight

sizes.pixelRatio = Math.min(window.devicePixelRatio, 2)

sizes.resolution.set(sizes.width * sizes.pixelRatio, sizes.height * sizes.pixelRatio)

// Update camera
camera.aspect = sizes.width / sizes.height
camera.updateProjectionMatrix()

// Update renderer
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(sizes.pixelRatio)
})

gl_PointSize = uSize * uResolution.y;

gl_PointCoord 是片元着色器中的一个内置变量,表示当前片元在点上的坐标。

这段代码是在创建球面上的随机点的球面坐标:


for (let i = 0; i < count; i++) {
const i3 = i * 3

const spherical = new THREE.Spherical(
radius,
Math.random() * Math.PI,
Math.random() * Math.PI * 2
)

const vector = new THREE.Vector3()

vector.setFromSpherical(spherical)

positionsArray[i3 + 0] = vector.x
positionsArray[i3 + 1] = vector.y
positionsArray[i3 + 2] = vector.z

sizesArray[i] = Math.random()
}

这里使用了球面坐标系统,其中: Math.random() * Math.PI 生成 0 到 π 之间的随机角度(极角) Math.random() * Math.PI * 2 生成 0 到 2π 之间的随机角度(方位角) 这样的组合可以在球体表面生成均匀分布的随机点,用于创建烟花效果。每个点都会: 有一个固定的半径(radius) 在垂直方向上随机分布(第一个角度) 在水平方向上随机分布(第二个角度) 这是创建3D烟花效果的关键部分,让粒子能够在球形空间中均匀分布。

gsap 是用来控制动画的。

exploding

float remap(float value, float originMin, float originMax, float destinationMin, float destinationMax) {
return destinationMin + (value - originMin) * (destinationMax - destinationMin) / (originMax - originMin);
}

remap() 函数是一个非常实用的重映射函数,用于将一个值从一个范围映射到另一个范围。让我们分解这个函数:

在这个着色器中,这个函数被用来控制烟花的爆炸效果,具体是将 uProgress 从 [0.0, 0.1] 的范围映射到 [0.0, 1.0] 的范围,用于平滑地控制爆炸动画。

这两行代码是一个优化性能的技巧,主要用于处理太小的粒子:

if(gl_PointSize < 1.0)
gl_Position = vec4(9999.9);

将粒子移动到视野之外 具体解释:

  • gl_PointSize < 1.0 检查粒子是否小于1个像素
  • 如果粒子太小,就把它的位置设置为 vec4(9999.9),这个位置在视野范围之外
  • 这样做的目的是:
    • 避免渲染那些肉眼看不见的微小粒子
    • 提高性能,因为不需要处理这些看不见的粒子
    • 减少GPU的工作负担 这是一种常见的图形编程优化技巧,特别是在处理粒子系统时很有用。当烟花粒子变得太小时,与其浪费资源去渲染它们,不如直接把它们移出视野范围。
vertex.glsl

uniform float uSize;
uniform vec2 uResolution;
attribute float aSize;
uniform float uProgress;
attribute float aTimeMultiplier;

#include ../includes/remap.glsl

void main() {

float progress = uProgress * aTimeMultiplier;
// position
vec3 newPosition = position;

// exploding 爆炸效果
float explodingProgress = remap(progress, 0.0, 0.1, 0.0, 1.0);
explodingProgress = clamp(explodingProgress, 0.0, 1.0);
explodingProgress = 1.0 - pow(1.0 - explodingProgress, 3.0);
newPosition *= explodingProgress;

// faling 下落效果
float fallingProgress = remap(progress, 0.1, 1.0, 0.0, 1.0);

fallingProgress = clamp(fallingProgress, 0.0, 1.0);

fallingProgress = 1.0 - pow(1.0 - fallingProgress, 3.0);

newPosition.y -= fallingProgress * 0.2;

// scaling 缩放效果
float sizeOpeningProgress = remap(progress, 0.0, 0.125, 0.0, 1.0);

float sizeClosingProgress = remap(progress, 0.125, 1.0, 1.0, 0.0);

float sizeProgress = min(sizeOpeningProgress, sizeClosingProgress);

sizeProgress = clamp(sizeProgress, 0.0, 1.0);

// twinkling 闪烁效果
float twinklingProgress = remap(progress, 0.2, 0.8, 0.0, 1.0);

twinklingProgress = clamp(twinklingProgress, 0.0, 1.0);

float sizeTwinkling = sin(progress * 30.0) * 0.5 + 0.5;

sizeTwinkling = 1.0 - sizeTwinkling * twinklingProgress;

vec4 modelPosition = modelMatrix * vec4(newPosition, 1.0);

vec4 viewPosition = viewMatrix * modelPosition;

gl_Position = projectionMatrix * viewPosition;
gl_PointSize = uSize * uResolution.y * aSize * sizeProgress * sizeTwinkling;
gl_PointSize *= 1.0 / -viewPosition.z;

if(gl_PointSize < 1.0)
gl_Position = vec4(9999.9);
}