mirror of
https://github.com/storytold/spark.git
synced 2026-10-09 00:09:53 +00:00
143 lines
4.6 KiB
HTML
143 lines
4.6 KiB
HTML
<!DOCTYPE html>
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Forge • Procedural Splats</title>
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<script type="importmap">
|
|
{
|
|
"imports": {
|
|
"three": "/examples/js/vendor/three/build/three.module.js",
|
|
"@forge-gfx/forge": "/dist/forge.module.js"
|
|
}
|
|
}
|
|
</script>
|
|
<script type="module">
|
|
import * as THREE from "three";
|
|
import { ForgeControls, SplatMesh, constructGrid, constructAxes, textSplats, imageSplats } from "@forge-gfx/forge";
|
|
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.setPixelRatio(window.devicePixelRatio);
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
document.body.appendChild(renderer.domElement)
|
|
|
|
// 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 ForgeControls({ 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>
|