body{
overflow: hidden;
margin: 0;
}
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="daaeb2a8bfbf9aeaf4ebece3f4ea">[email protected]</a>/build/three.webgpu.js",
"three/addons/": "https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="097d617b6c6c493927383f302739">[email protected]</a>/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from "three";
import {uv, vec2, vec3, fract, attribute, select} from "three";
import {OrbitControls} from "three/addons/controls/OrbitControls.js";
import { mergeGeometries } from "three/addons/utils/BufferGeometryUtils.js";
console.clear();
let scene = new THREE.Scene();
scene.background = new THREE.Color("skyblue");
let camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 1, 1).setLength(10);
let renderer = new THREE.WebGPURenderer({antialias: true});
renderer.setPixelRatio( devicePixelRatio );
renderer.setSize( innerWidth, innerHeight );
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", event => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
})
let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
let light = new THREE.DirectionalLight(0xffffff, Math.PI);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, Math.PI * 0.5));
let circle = 100;
let sand = new THREE.Mesh(
new THREE.CircleGeometry(circle, 32).rotateX(-Math.PI * 0.5),
new THREE.MeshLambertMaterial({color: "#F6DCBD"})
);
scene.add(sand);
let gs = [
new THREE.CylinderGeometry(0.025, 0.025, 0.9, 3, 1, true).translate(0, 0.45, 0),
new THREE.LatheGeometry([
[1, 0.8], [0.66, 0.9], [0.33, 0.96], [0, 1]
].map(p => {return new THREE.Vector2(...p)}),
8)
];
gs.forEach((g, gIdx) => {
g.setAttribute("color", new THREE.Float32BufferAttribute(new Array(g.attributes.position.count * 3).fill(gIdx), 3));
})
let g = mergeGeometries(gs);
let m = new THREE.MeshLambertNodeMaterial({vertexColors: true});
let amount = 10000;
let io = new THREE.InstancedMesh(g, m, amount);
let dummy = new THREE.Object3D();
let dummyColor = new THREE.Color();
let instColor = [];
for(let i = 0; i < amount; i++){
dummy.position.setFromCylindricalCoords(Math.sqrt(circle * circle * Math.random()), Math.random() * 2 * Math.PI, 0);
dummy.rotation.y = Math.PI * 2 * Math.random();
dummy.rotation.z = Math.PI * 0.05 * Math.sign(Math.random() - 0.5);
dummy.updateMatrix();
io.setMatrixAt(i, dummy.matrix);
dummyColor.setHSL(Math.random(), 0.75, 0.5);
instColor.push(dummyColor.r, dummyColor.g, dummyColor.b);
}
g.setAttribute("instColor", new THREE.InstancedBufferAttribute(new Float32Array(instColor), 3));
// TLS magic is here
let uvScaled = uv().mul(vec2(1., 3.)).toVar();
let iColor = attribute("instColor").toVar();
let col = select(fract(uvScaled.y).greaterThan(0.5), vec3(1., 1., 1.), iColor).toVar();
m.colorNode = col;
scene.add(io);
renderer.setAnimationLoop(() => {
controls.update();
renderer.render(scene, camera);
})
</script>