mirror of
https://github.com/storytold/spark.git
synced 2026-10-18 07:40:38 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<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>
|
||||
Reference in New Issue
Block a user