body{
overflow: hidden;
margin: 0;
}
<script async src="https://ga.jspm.io/npm:<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="d3b6a0febebcb7a6bfb6fea0bbbeaba093e2fde5fde0">[email protected]</a>/dist/es-module-shims.js" crossorigin="anonymous"></script>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="a3d7cbd1c6c6e3938d9296908d93">[email protected]</a>/build/three.module.js",
"three/addons/": "https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="0b7f63796e6e4b3b253a3e38253b">[email protected]</a>/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
console.clear();
let scene = new THREE.Scene();
scene.background = new THREE.Color(0xface8d);
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(3, 5, 8).setLength(40);
camera.lookAt(scene.position);
let renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(innerWidth, innerHeight);
//renderer.setClearColor(0x404040);
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, 0.5);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));
scene.add(new THREE.GridHelper());
let ig = new THREE.InstancedBufferGeometry().copy(new THREE.PlaneGeometry(2, 1));
ig.instanceCount = Infinity;
const amount = 2048;
let instPos = new Float32Array(amount * 3);
for(let i = 0; i < amount;i++){
instPos[i * 3 + 0] = THREE.MathUtils.randFloatSpread(50);
instPos[i * 3 + 1] = THREE.MathUtils.randFloatSpread(50);
instPos[i * 3 + 2] = THREE.MathUtils.randFloatSpread(50);
}
ig.setAttribute("instPos", new THREE.InstancedBufferAttribute(instPos, 3));
let im = new THREE.ShaderMaterial({
uniforms: {
quaternion: {value: new THREE.Quaternion()},
markerTexture: {value: getMarkerTexture(4096, 32, 64)},
textureDimensions: {value: new THREE.Vector2(32, 64)}
},
vertexShader: `
uniform vec4 quaternion;
uniform vec2 textureDimensions;
attribute vec3 instPos;
varying vec2 vUv;
vec3 qtransform( vec4 q, vec3 v ){
return v + 2.0*cross(cross(v, q.xyz ) + q.w*v, q.xyz);
}
void main(){
vec3 pos = qtransform(quaternion, position) + instPos;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.);
float iID = float(gl_InstanceID);
float stepW = 1. / textureDimensions.x;
float stepH = 1. / textureDimensions.y;
float uvX = mod(iID, textureDimensions.x);
float uvY = floor(iID / textureDimensions.x);
vUv = (vec2(uvX, uvY) + uv) * vec2(stepW, stepH);
}
`,
fragmentShader: `
uniform sampler2D markerTexture;
varying vec2 vUv;
void main(){
vec4 col = texture(markerTexture, vUv);
gl_FragColor = vec4(col.rgb, 1);
}
`
})
let io = new THREE.Mesh(ig, im);
scene.add(io);
let clock = new THREE.Clock();
renderer.setAnimationLoop((_) => {
let t = clock.getElapsedTime();
controls.update();
im.uniforms.quaternion.value.copy(camera.quaternion).invert();
renderer.render(scene, camera);
});
function getMarkerTexture(size, amountW, amountH){
let c = document.createElement("canvas");
c.width = size;
c.height = size;
let ctx = c.getContext("2d");
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, c.width, c.height);
const stepW = c.width / 32;
const stepH = c.height / 64;
ctx.font = "bold 40px Arial";
ctx.textBaseline = "middle";
ctx.textAlign = "center";
ctx.fillStyle = "#000";
let col = new THREE.Color();
let counter = 0;
for(let y = 0; y < amountH; y++){
for(let x = 0; x < amountW; x++){
let textX = (x + 0.5) * stepW;
let textY = ((amountH - y - 1) + 0.5) * stepH;
ctx.fillText(counter, textX, textY);
ctx.strokeStyle = '#' + col.setHSL(Math.random(), 1, 0.5).getHexString();
ctx.lineWidth = 3;
ctx.strokeRect(x * stepW + 4, y * stepH + 4, stepW - 8, stepH - 8);
counter++;
}
}
let ct = new THREE.CanvasTexture(c);
ct.colorSpace = THREE.SRGBColorSpace;
return ct;
}
</script>