Files

153 lines
4.9 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Spark • Procedural Splats</title>
<style>
body {
margin: 0;
}
canvas {
touch-action: none;
}
</style>
</head>
<body>
<script type="importmap">
{
"imports": {
"three": "/examples/js/vendor/three/build/three.module.js",
"@sparkjsdev/spark": "/dist/spark.module.js"
}
}
</script>
<script type="module">
import * as THREE from "three";
import { SparkControls, SplatMesh, constructGrid, constructAxes, textSplats, imageSplats } from "@sparkjsdev/spark";
import { getAssetFileURL } from "/examples/js/get-asset-url.js";
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);
window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// Generate a Sierpinski pyramid one splat at a time via iteration
const pyramid = new SplatMesh({
constructSplats: (splats) => {
const NUM_SPLATS = 500000;
const ITERATIONS = 15;
const CORNERS = [
new THREE.Vector3(0, 1, -0.7),
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(0.6, 1, 0.5),
new THREE.Vector3(-0.6, 1, 0.5),
];
const center = new THREE.Vector3();
const scales = new THREE.Vector3().setScalar(0.0005);
const quaternion = new THREE.Quaternion();
const opacity = 1;
const color = new THREE.Color(1, 1, 1);
for (let i = 0; i < NUM_SPLATS; i++) {
center.set(0, 0, 0);
for (let iter = 0; iter < ITERATIONS; iter++) {
const corner = CORNERS[Math.floor(Math.random() * 4)];
center.add(corner).multiplyScalar(0.5);
}
color.set(0.3 + center.x - center.z * 0.5, center.z * 1.5, 0.5 + center.z * 2);
splats.pushSplat(center, scales, quaternion, opacity, color);
}
},
});
pyramid.position.set(0, -0.5, -2);
scene.add(pyramid);
const stars = new SplatMesh({
constructSplats: (splats) => {
const NUM_SPLATS = 100000;
const center = new THREE.Vector3();
const scales = new THREE.Vector3().setScalar(0.008);
const quaternion = new THREE.Quaternion();
const opacity = 1;
const color = new THREE.Color(1, 1, 1);
for (let i = 0; i < NUM_SPLATS; i++) {
// Distribute stars on a 50x50x50 cube
center.set(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).multiplyScalar(50);
// Random purple-ish color
color.set(0.4 + Math.random() * 0.3, 0.5, 0.7 + Math.random() * 0.3);
splats.pushSplat(center, scales, quaternion, opacity, color);
}
},
});
stars.position.set(0, -0.5, -2);
scene.add(stars);
// Generate splats from rendered text
const text = textSplats({
text: "I GSplats!",
font: "Arial",
fontSize: 60,
color: new THREE.Color(0xE420B7),
});
text.scale.setScalar(0.5 / 80);
text.position.set(0.15, -1, -2);
scene.add(text);
const text2 = textSplats({
text: "WASD + mouse to move",
font: "Verdana",
fontSize: 30,
color: new THREE.Color(0x49EAFF)
});
text2.scale.setScalar(0.2 / 40);
text2.position.set(0, 1.0, -2);
scene.add(text2);
// Generate splats from image pixels with alpha channel
const imageURL = await getAssetFileURL("butterfly.png");
const image = imageSplats({ url: imageURL });
image.scale.setScalar(0.7 / 400);
image.position.set(-0.5, -1, -2);
scene.add(image);
const controls = new SparkControls({ canvas: renderer.domElement });
renderer.setAnimationLoop(function animate(time) {
controls.update(camera);
renderer.render(scene, camera);
pyramid.rotation.y = time / 5000;
// Move stars back and forth
stars.position.z = Math.sin(time / 10000) * 10;
// Rotate butterfly at different rates on each axis
image.rotation.set(
-0.3 + 0.35 * Math.sin(time / 190),
0.35 * Math.sin(time / 390),
0.1 * Math.sin(time / 200)
);
// Oscilate butterfly y position
image.position.y = -0.9 + 0.04 * Math.sin(time / 330);
// Animate opacity of top text
text2.opacity = Math.abs(Math.sin(time / 1000) * 0.3);
});
</script>
</body>
</html>