Giant refactor. added layers. ui overhaul. added save/load and we now got presets
This commit is contained in:
Sam
2025-12-28 03:21:25 +13:00
parent f01076df57
commit 14ec23237f
90 changed files with 4971 additions and 22901 deletions

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Phyllotaxis Animation with Three.js and ShaderMaterial</title>
<style>body {
margin: 0;
}
canvas {
display: block;
}
</style>
</head>
<body>
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> -->
<script src="/index.975ef6c8.js" defer=""></script>
</body>
</html>

View File

@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Phyllotaxis Animation with Three.js and ShaderMaterial</title>
<style>
body {
margin: 0;
}
canvas {
display: block;
}
</style>
</head>
<body>
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> -->
<script src="./src/index.js" type="module"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
{
"name": "my-threejs-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "parcel index.html",
"build": "parcel build index.html"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"parcel": "^2.12.0"
},
"dependencies": {
"@parcel/transformer-glsl": "^2.12.0",
"postprocessing": "^6.35.3",
"three": "^0.163.0"
}
}

View File

@@ -0,0 +1,19 @@
varying vec3 vColor;
void main() {
float distance = length(gl_PointCoord - vec2(0.5, 0.5));
float radius = 0.5; // The radius of the circle, where 0.5 is the edge of the point sprite
float intensity = 0.5; // Gaussian for smooth intensity increase
// float intensity = exp(-pow(distance * 3.0, 2.0)); // Gaussian for smooth intensity increase
// If the distance is greater than the radius, discard the fragment (make it transparent)
if (distance > radius) {
discard;
}
// Use the intensity to modulate the color within the circle
vec3 brightColor = vColor * intensity;
brightColor = clamp(brightColor, 0.0, 2.5);
gl_FragColor = vec4(brightColor, 1.0); // Full alpha within the circle
}

View File

@@ -0,0 +1,33 @@
uniform float time;
uniform vec3 color1;
uniform vec3 color2;
varying vec3 vColor;
const float PI = 3.1415926535897932384626433832795;
const float goldenAngle = PI * (3.0 - sqrt(5.0));
void main() {
// Calculate the normalized distance from center
float index = float(gl_VertexID);
float radiuss = sqrt(index) * 5.0;
float normalizedRadius = radiuss / 200.0; // Adjust this divisor based on your pattern's size
// Calculate the angle for each point based on its index
float angle = index * (goldenAngle + time * 0.001);
if (mod(index,2.0) != 0.0){
angle *=-1.0;
}
// Color changes over time - Adding a slow wave effect based on the distance from center
float colorTime = time * -2.1; // Slow down the color change over time
float colorWave = sin(normalizedRadius * PI * 2.0 + colorTime); // Wave based on distance and time
float colorMixFactor = colorWave * 0.5 + 0.5; // Normalize the wave between 0 and 1
vColor = mix(color1, color2, colorMixFactor); // Mix the colors based on the computed factor
// Calculate the animated position
vec3 animatedPosition = vec3(radiuss * cos(angle), radiuss * sin(angle), 0.0);
gl_Position = projectionMatrix * modelViewMatrix * vec4(animatedPosition, 1.0);
gl_PointSize = 10.0;
}

View File

@@ -0,0 +1,110 @@
import * as THREE from 'three';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
import vertexShader from '../shaders/vertex.glsl';
import fragmentShader from '../shaders/fragment.glsl';
// Now you can use `vertexShader` and `fragmentShader` as strings in your Three.js material
// Basic setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setClearColor(0x000000, 0); // Transparent background color
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// scene.add(renderer.domElement);
// Phyllotaxis setup
const pointsGeometry = new THREE.BufferGeometry();
const n_points = 5000;
const positions = new Float32Array(n_points * 3);
const goldenAngle = Math.PI * (3 - Math.sqrt(5));
// const goldenAngle = 0;
for (let i = 0; i < n_points; i++) {
const angle = i * goldenAngle;
const r = Math.sqrt(i) * 5;
positions[i * 3] = r * Math.cos(angle); // x
positions[i * 3 + 1] = r * Math.sin(angle); // y
positions[i * 3 + 2] = 0; // z
}
pointsGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
// ShaderMaterial
const shaderMaterial = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0.0 },
color1: { value: new THREE.Color(0x2D81FC) }, // Blue
color2: { value: new THREE.Color(0xFC0362) }, // Pink
},
vertexShader: vertexShader,
fragmentShader: fragmentShader,
transparent: true,
blending: THREE.AdditiveBlending,
});
const points = new THREE.Points(pointsGeometry, shaderMaterial);
// Assign the points mesh to the bloom layer
scene.add(points);
points.layers.enable(1);
camera.position.z = 200;
// Compile the shader and catch any errors
renderer.compile(scene, camera);
// Create layers
const bloomLayer = new THREE.Layers();
bloomLayer.set(1); // Setting the bloom layer to layer 1
// Set up the bloom pass
const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.0, 0.4, 0.85);
bloomPass.threshold = 0.1
bloomPass.strength = 2.5
bloomPass.radius = 0.55
bloomPass.renderToScreen = true
// Create a render scene pass
const renderScene = new RenderPass(scene, camera);
// Set up the effect composer
const composer = new EffectComposer(renderer);
composer.setSize(window.innerWidth, window.innerHeight);
composer.addPass(renderScene);
composer.addPass(bloomPass);
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
// renderer.toneMappingExposure = Math.pow( 0.9, 4.0 )
renderer.toneMappingExposure = 1.0
renderer.autoClear = false;
points.layers.set(1);
// Animation loop
function animate() {
requestAnimationFrame(animate);
shaderMaterial.uniforms.time.value = performance.now() / 5000;
renderer.clear();
camera.layers.set(1);
composer.render();
renderer.clearDepth();
camera.layers.set(0);
renderer.render(scene, camera);
}
animate();
// Handle window resize
window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}