參考課程是這位老哥的 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.