参考课程是这位老哥的 three.js 教程 很是厉害 感兴趣的能够买来看看
若是自己对 shader 还不够了解的话, the book of shaders 是很是好的入门读物
说到作“萤火虫”,确定会想到用“粒子”(particles)。javascript
首先定义一个 buffer geometry, 而后建立一个 BufferAttribute 用于存储每个萤火虫的位置信息;java
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 看看这些点都在哪里;git
const firefliesMaterial = new THREE.PointsMaterial({ size: 0.1, sizeAttenuation: true }) const fireflies = new THREE.Points(firefliesGeometry, firefliesMaterial) scene.add(fireflies)
看到了空中的小点点,发现彻底的随机位置有点奇怪,因此改改生成位置的代码:github
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 的魔法时刻。web
首先是 vertex shader,用于决定物体的节点的位置属性。canvas
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
。less
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 事件,实时更新分辨率:dom
window.addEventListener('resize', () => { // ... // Update fireflies firefliesMaterial.uniforms.uPixelRatio.value = Math.min(window.devicePixelRatio, 2) })
vertex.glslide
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); }
想让萤火虫浮动起来,还须要在外面更新 uTimewebgl
const clock = new THREE.Clock() const tick = () => { const elapsedTime = clock.getElapsedTime() // Update materials firefliesMaterial.uniforms.uTime.value = elapsedTime // ... }
咱们的目标是创造一个中心渐变的图案,听起来很简单(确实
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.