-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
864 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import * as THREE from 'three'; | ||
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; | ||
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; | ||
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; | ||
|
||
|
||
// Setup Scene, Camera, Renderer | ||
const scene = new THREE.Scene(); | ||
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); | ||
const renderer = new THREE.WebGLRenderer(); | ||
renderer.setSize(window.innerWidth, window.innerHeight); | ||
document.body.appendChild(renderer.domElement); | ||
|
||
// Postprocessing with Bloom effect | ||
const composer = new EffectComposer(renderer); | ||
const renderPass = new RenderPass(scene, camera); | ||
composer.addPass(renderPass); | ||
|
||
const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85); | ||
composer.addPass(bloomPass); | ||
|
||
// Create Material and Line Geometry | ||
const material = new THREE.LineBasicMaterial({ color: 0xffd700 }); // Golden color | ||
|
||
// Set up the Golden Ratio Dragon Curve parameters | ||
const goldenRatio = (1 + Math.sqrt(5)) / 2; | ||
const r1 = Math.pow(1 / goldenRatio, 1 / goldenRatio); | ||
const r2 = Math.pow(r1, 2); | ||
const angle1 = Math.acos((1 + r1**2 - r1**4) / (2 * r1)); | ||
const angle2 = Math.acos((1 + r1**4 - r1**2) / (2 * r1**2)); | ||
|
||
function goldenDragon(x1, y1, x2, y2, turn, n, vertices) { | ||
const dist = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2); | ||
if (dist < 1 || n <= 0) { | ||
vertices.push(new THREE.Vector3(x2, y2, 0)); | ||
return; | ||
} | ||
|
||
const angle = Math.atan2(y2 - y1, x2 - x1); | ||
let px, py; | ||
if (turn) { | ||
px = x1 + dist * r1 * Math.cos(angle + angle1); | ||
py = y1 + dist * r1 * Math.sin(angle + angle1); | ||
} else { | ||
px = x1 + dist * r2 * Math.cos(angle - angle2); | ||
py = y1 + dist * r2 * Math.sin(angle - angle2); | ||
} | ||
|
||
goldenDragon(x1, y1, px, py, true, n - 1, vertices); | ||
goldenDragon(px, py, x2, y2, false, n - 1, vertices); | ||
} | ||
|
||
// Setup the geometry to hold the vertices | ||
const points = []; | ||
goldenDragon(-5, -2, 15, -2, true, 10, points); // Adjust the recursion level as needed | ||
const geometry = new THREE.BufferGeometry().setFromPoints(points); | ||
const line = new THREE.Line(geometry, material); | ||
scene.add(line); | ||
|
||
// Position the camera and animate | ||
camera.position.z = 10; | ||
|
||
function animate() { | ||
requestAnimationFrame(animate); | ||
composer.render(); | ||
} | ||
animate(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/** | ||
* Based upon Bobby Roe's Simple Particle System | ||
* Watch the tutorial on YouTube: (https://youtu.be/h1UQdbuF204) | ||
*/ | ||
|
||
import * as THREE from "three"; | ||
|
||
const loader = new THREE.TextureLoader(); | ||
|
||
function getSprite({ hasFog, color, opacity, path, pos, size }) { | ||
const spriteMat = new THREE.SpriteMaterial({ | ||
color, | ||
fog: hasFog, | ||
map: loader.load(path), | ||
transparent: true, | ||
opacity, | ||
}); | ||
spriteMat.color.offsetHSL(0, 0, Math.random() * 0.2 - 0.1); | ||
const sprite = new THREE.Sprite(spriteMat); | ||
sprite.position.set(pos.x, -pos.y, pos.z); | ||
size += Math.random() - 0.5; | ||
sprite.scale.set(size, size, size); | ||
sprite.material.rotation = 0; | ||
return sprite; | ||
} | ||
|
||
function getLayer({ | ||
hasFog = true, | ||
hue = 0.0, | ||
numSprites = 10, | ||
opacity = 1, | ||
path = "./img/rad-grad.png", | ||
radius = 1, | ||
sat = 0.5, | ||
size = 1, | ||
z = 0, | ||
}) { | ||
const layerGroup = new THREE.Group(); | ||
for (let i = 0; i < numSprites; i += 1) { | ||
let angle = (i / numSprites) * Math.PI * 2; | ||
const pos = new THREE.Vector3( | ||
Math.cos(angle) * Math.random() * radius, | ||
Math.sin(angle) * Math.random() * radius, | ||
z + Math.random() | ||
); | ||
const length = new THREE.Vector3(pos.x, pos.y, 0).length(); | ||
// const hue = 0.0; // (0.9 - (radius - length) / radius) * 1; | ||
|
||
let color = new THREE.Color().setHSL(hue, 1, sat); | ||
const sprite = getSprite({ hasFog, color, opacity, path, pos, size }); | ||
layerGroup.add(sprite); | ||
} | ||
return layerGroup; | ||
} | ||
export default getLayer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,236 @@ | ||
/** | ||
* Based upon Bobby Roe's Simple Particle System | ||
* Watch the tutorial on YouTube: (https://youtu.be/h1UQdbuF204) | ||
*/ | ||
|
||
import * as THREE from 'three'; | ||
|
||
const _VS = ` | ||
uniform float pointMultiplier; | ||
attribute float size; | ||
attribute float angle; | ||
attribute vec4 aColor; | ||
varying vec4 vColor; | ||
varying vec2 vAngle; | ||
void main() { | ||
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0); | ||
gl_Position = projectionMatrix * mvPosition; | ||
gl_PointSize = size * pointMultiplier / gl_Position.w; | ||
vAngle = vec2(cos(angle), sin(angle)); | ||
vColor = aColor; | ||
}`; | ||
|
||
const _FS = ` | ||
uniform sampler2D diffuseTexture; | ||
varying vec4 vColor; | ||
varying vec2 vAngle; | ||
void main() { | ||
vec2 coords = (gl_PointCoord - 0.5) * mat2(vAngle.x, vAngle.y, -vAngle.y, vAngle.x) + 0.5; | ||
gl_FragColor = texture2D(diffuseTexture, coords) * vColor; | ||
}`; | ||
|
||
|
||
function getLinearSpline(lerp) { | ||
|
||
const points = []; | ||
const _lerp = lerp; | ||
|
||
function addPoint(t, d) { | ||
points.push([t, d]); | ||
} | ||
|
||
function getValueAt(t) { | ||
let p1 = 0; | ||
|
||
for (let i = 0; i < points.length; i++) { | ||
if (points[i][0] >= t) { | ||
break; | ||
} | ||
p1 = i; | ||
} | ||
|
||
const p2 = Math.min(points.length - 1, p1 + 1); | ||
|
||
if (p1 == p2) { | ||
return points[p1][1]; | ||
} | ||
|
||
return _lerp( | ||
(t - points[p1][0]) / ( | ||
points[p2][0] - points[p1][0]), | ||
points[p1][1], points[p2][1]); | ||
} | ||
return { addPoint, getValueAt }; | ||
} | ||
|
||
function getParticleSystem(params) { | ||
const { camera, emitter, parent, rate, texture } = params; | ||
const uniforms = { | ||
diffuseTexture: { | ||
value: new THREE.TextureLoader().load(texture) | ||
}, | ||
pointMultiplier: { | ||
value: window.innerHeight / (2.0 * Math.tan(30.0 * Math.PI / 180.0)) | ||
} | ||
}; | ||
const _material = new THREE.ShaderMaterial({ | ||
uniforms: uniforms, | ||
vertexShader: _VS, | ||
fragmentShader: _FS, | ||
//blending: THREE.AdditiveBlending, | ||
depthTest: true, | ||
depthWrite: false, | ||
transparent: true, | ||
vertexColors: true | ||
}); | ||
|
||
let _particles = []; | ||
|
||
const geometry = new THREE.BufferGeometry(); | ||
geometry.setAttribute('position', new THREE.Float32BufferAttribute([], 3)); | ||
geometry.setAttribute('size', new THREE.Float32BufferAttribute([], 1)); | ||
geometry.setAttribute('aColor', new THREE.Float32BufferAttribute([], 4)); | ||
geometry.setAttribute('angle', new THREE.Float32BufferAttribute([], 1)); | ||
|
||
const _points = new THREE.Points(geometry, _material); | ||
|
||
parent.add(_points); | ||
|
||
const alphaSpline = getLinearSpline((t, a, b) => { | ||
return a + t * (b - a); | ||
}); | ||
alphaSpline.addPoint(0.0, 0.0); | ||
alphaSpline.addPoint(0.6, 1.0); | ||
alphaSpline.addPoint(1.0, 0.0); | ||
|
||
const colorSpline = getLinearSpline((t, a, b) => { | ||
const c = a.clone(); | ||
return c.lerp(b, t); | ||
}); | ||
colorSpline.addPoint(0.0, new THREE.Color(0xffff00)); | ||
colorSpline.addPoint(0.0, new THREE.Color(0x00ff00)); | ||
colorSpline.addPoint(0.0, new THREE.Color(0x00ffff)); | ||
colorSpline.addPoint(1.0, new THREE.Color(0xffffff)); | ||
|
||
const sizeSpline = getLinearSpline((t, a, b) => { | ||
return a + t * (b - a); | ||
}); | ||
sizeSpline.addPoint(0.0, 0.0); | ||
sizeSpline.addPoint(1.0, 1.0); | ||
// max point size = 512; => console.log(ctx.getParameter(ctx.ALIASED_POINT_SIZE_RANGE)); | ||
const maxVelocity = 1.5; | ||
const radius = 0.5; | ||
const maxLife = 9.5; | ||
const maxSize = 0.15; | ||
let gdfsghk = 0.0; | ||
function _AddParticles(timeElapsed) { | ||
gdfsghk += timeElapsed; | ||
const n = Math.floor(gdfsghk * rate); | ||
gdfsghk -= n / rate; | ||
for (let i = 0; i < n; i += 1) { | ||
const life = (Math.random() * 0.75 + 0.25) * maxLife; | ||
_particles.push({ | ||
position: new THREE.Vector3( | ||
(Math.random() * 2 - 1) * radius, | ||
(Math.random() * 2 - 1) * radius, | ||
(Math.random() * 2 - 1) * radius).add(emitter.position), | ||
size: (Math.random() * 0.5 + 0.5) * maxSize, | ||
colour: new THREE.Color(), | ||
alpha: 1.0, | ||
life: life, | ||
maxLife: life, | ||
rotation: Math.random() * 2.0 * Math.PI, | ||
rotationRate: Math.random() * 0.01 - 0.005, | ||
velocity: new THREE.Vector3( | ||
(Math.random() * 2 - 1) * radius * maxVelocity, | ||
(Math.random() * 2 - 1) * radius * maxVelocity, | ||
(Math.random() * 2 - 1) * radius * maxVelocity), | ||
}); | ||
} | ||
} | ||
|
||
function _UpdateGeometry() { | ||
const positions = []; | ||
const sizes = []; | ||
const colours = []; | ||
const angles = []; | ||
|
||
for (let p of _particles) { | ||
positions.push(p.position.x, p.position.y, p.position.z); | ||
colours.push(p.colour.r, p.colour.g, p.colour.b, p.alpha); | ||
sizes.push(p.currentSize); | ||
angles.push(p.rotation); | ||
} | ||
|
||
geometry.setAttribute( | ||
'position', new THREE.Float32BufferAttribute(positions, 3)); | ||
geometry.setAttribute( | ||
'size', new THREE.Float32BufferAttribute(sizes, 1)); | ||
geometry.setAttribute( | ||
'aColor', new THREE.Float32BufferAttribute(colours, 4)); | ||
geometry.setAttribute( | ||
'angle', new THREE.Float32BufferAttribute(angles, 1)); | ||
|
||
geometry.attributes.position.needsUpdate = true; | ||
geometry.attributes.size.needsUpdate = true; | ||
geometry.attributes.aColor.needsUpdate = true; | ||
geometry.attributes.angle.needsUpdate = true; | ||
} | ||
_UpdateGeometry(); | ||
|
||
function _UpdateParticles(timeElapsed) { | ||
for (let p of _particles) { | ||
p.life -= timeElapsed; | ||
} | ||
|
||
_particles = _particles.filter(p => { | ||
return p.life > 0.0; | ||
}); | ||
|
||
for (let p of _particles) { | ||
const t = 1.0 - p.life / p.maxLife; | ||
p.rotation += p.rotationRate; | ||
p.alpha = alphaSpline.getValueAt(t); | ||
p.currentSize = p.size * sizeSpline.getValueAt(t); | ||
p.colour.copy(colorSpline.getValueAt(t)); | ||
|
||
p.position.add(p.velocity.clone().multiplyScalar(timeElapsed)); | ||
|
||
const drag = p.velocity.clone(); | ||
drag.multiplyScalar(timeElapsed * 0.1); | ||
drag.x = Math.sign(p.velocity.x) * Math.min(Math.abs(drag.x), Math.abs(p.velocity.x)); | ||
drag.y = Math.sign(p.velocity.y) * Math.min(Math.abs(drag.y), Math.abs(p.velocity.y)); | ||
drag.z = Math.sign(p.velocity.z) * Math.min(Math.abs(drag.z), Math.abs(p.velocity.z)); | ||
p.velocity.sub(drag); | ||
} | ||
|
||
_particles.sort((a, b) => { | ||
const d1 = camera.position.distanceTo(a.position); | ||
const d2 = camera.position.distanceTo(b.position); | ||
|
||
if (d1 > d2) { | ||
return -1; | ||
} | ||
if (d1 < d2) { | ||
return 1; | ||
} | ||
return 0; | ||
}); | ||
} | ||
|
||
function update(timeElapsed) { | ||
_AddParticles(timeElapsed); | ||
_UpdateParticles(timeElapsed); | ||
_UpdateGeometry(); | ||
} | ||
return { update }; | ||
} | ||
|
||
export { getParticleSystem }; |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.