关于three.js:threejs-glsl-用-shader-写出夜空中的萤火虫

10次阅读

共计 2821 个字符,预计需要花费 8 分钟才能阅读完成。

参考课程是这位老哥的 three.js 教程 十分厉害 感兴趣的能够买来看看
如果自身对 shader 还不够理解的话,the book of shaders 是十分好的入门读物

说到做“萤火虫”,必定会想到用“粒子”(particles)。

用 buffer geometry 创立粒子

首先定义一个 buffer geometry, 而后创立一个 BufferAttribute 用于存储每一个萤火虫的地位信息;

const firefliesGeometry = new THREE.BufferGeometry()
const firefliesCount = 30 // 创立萤火虫的个数
const positionArray = new Float32Array(firefliesCount * 3)

for(let i = 0; i < firefliesCount; i++)
{positionArray[i * 3 + 0] = Math.random() * 4
    positionArray[i * 3 + 1] = Math.random() * 4
    positionArray[i * 3 + 2] = Math.random() * 4}

firefliesGeometry.setAttribute('position', new THREE.BufferAttribute(positionArray, 3))

咱们先给它一个 pointsMaterial 看看这些点都在哪里;

const firefliesMaterial = new THREE.PointsMaterial({size: 0.1, sizeAttenuation: true})

const fireflies = new THREE.Points(firefliesGeometry, firefliesMaterial)
scene.add(fireflies)

看到了地面的小点点,发现齐全的随机地位有点奇怪,所以改改生成地位的代码:

for(let i = 0; i < firefliesCount; i++)
{positionArray[i * 3 + 0] = (Math.random() - 0.5) * 4
    positionArray[i * 3 + 1] = Math.random() * 1.5
    positionArray[i * 3 + 2] = (Math.random() - 0.5) * 4
}

根底的小点点有了,接下来就是 shader 的魔法时刻。

自定义 shader material: vertex shader

首先是 vertex shader,用于决定物体的节点的地位属性。

The vertex shader’s purpose is to position the vertices of the geometry. The idea is to send the vertices positions, the mesh transformations (like its position, rotation, and scale), the camera information (like its position, rotation, and field of view). Then, the GPU will follow the instructions in the vertex shader to process all of this information in order to project the vertices on a 2D space that will become our render —in other words, our canvas.

这段代码简直没有什么含意,重头戏在 fragment shader 上。
然而有一个中央须要留神一下,即 gl_PointSize 须要依据屏幕分辨率而变动,所以须要从 js 文件中传进去一个 uniform: window.devicePixelRatio

const firefliesMaterial = new THREE.ShaderMaterial({
    uniforms:
    {uPixelRatio: { value: Math.min(window.devicePixelRatio, 2) },
        uSize: {value: 100},
        uTime: {value: 0},
    },
    blending: THREE.AdditiveBlending, // 叠加模式也很重要
    vertexShader: firefliesVertexShader,
    fragmentShader: firefliesFragmentShader
})

同时,要监听 resize 事件,实时更新分辨率:

window.addEventListener('resize', () =>
{
    // ...

    // Update fireflies
    firefliesMaterial.uniforms.uPixelRatio.value = Math.min(window.devicePixelRatio, 2)
})

vertex.glsl

uniform float uPixelRatio;
uniform float uSize; // 粒子大小
uniform float uTime;

void main()
{vec4 modelPosition = modelMatrix * vec4(position, 1.0);
    vec4 viewPosition = viewMatrix * modelPosition;
    vec4 projectionPosition = projectionMatrix * viewPosition;

    gl_Position = projectionPosition;
    gl_PointSize = uSize * uPixelRatio; // 每一个点的渲染尺寸
    gl_PointSize *= (1.0 / - viewPosition.z); // 近大远小
    
    // 萤火虫的浮动
    vec4 modelPosition = modelMatrix * vec4(position, 1.0); 
    modelPosition.y += sin(uTime);
}

想让萤火虫浮动起来,还须要在里面更新 uTime

const clock = new THREE.Clock()

const tick = () =>
{const elapsedTime = clock.getElapsedTime()

    // Update materials
    firefliesMaterial.uniforms.uTime.value = elapsedTime

    // ...
}

自定义 shader material: fragment shader

咱们的指标是发明一个核心突变的图案,听起来很简略(的确

void main()
{float distanceToCenter = distance(gl_PointCoord, vec2(0.5)); // 计算每一个像素点到核心的间隔
    float strength = 0.05 / distanceToCenter - 0.1; // 核心大,四周小

    gl_FragColor = vec4(1.0, 1.0, 1.0, strength); // 将 strength 作为 alpha
}

voila.

正文完
 0