import * as THREE from 'three' import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; import { RenderPixelatedPass } from 'three/addons/postprocessing/RenderPixelatedPass.js'; import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; import { randFloat } from 'three/src/math/MathUtils.js'; let scene, camera, renderer let aspect, frustumSize let time = 0 let lastTime = 0 let controls let mesh let composer let cubeGroup = [] let instancedMesh, dummy, count const texture = new THREE.TextureLoader().load("public/images/dither.png") texture.minFilter = THREE.NearestFilter texture.colorSpace = THREE.SRGBColorSpace; texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; function init(){ SetupRenderer() scene = new THREE.Scene() SetupCamera() SetupControls() composer = new EffectComposer( renderer ); const renderPixelatedPass = new RenderPixelatedPass( 8, scene, camera ); renderPixelatedPass.normalEdgeStrength = 0; renderPixelatedPass.depthEdgeStrength = 1; renderPixelatedPass.pixelAlignedPanning = false; composer.addPass( renderPixelatedPass ); const outputPass = new OutputPass(); composer.addPass( outputPass ); SetupCubes() AddLights() animate() } function SetupCubes() { const cubeGeometry = new THREE.BoxGeometry(); const cubeMaterial = new THREE.MeshPhongMaterial({ map: texture }); // Create an InstancedMesh count = 100; instancedMesh = new THREE.InstancedMesh(cubeGeometry, cubeMaterial, count); scene.add(instancedMesh) dummy = new THREE.Object3D(); for (let i = 0; i < count; i++) { let size = randFloat(5, 8); dummy.scale.set(size, size, size); dummy.position.set(randFloat(-10, 10), randFloat(-6, 3), 0); dummy.updateMatrix(); instancedMesh.setMatrixAt(i, dummy.matrix); } } function SetupRenderer(){ renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }) renderer.setClearColor(0xffffff, 0); renderer.setSize(window.innerWidth, window.innerHeight) document.getElementById('container').appendChild(renderer.domElement) } function SetupCamera(){ aspect = (window.innerWidth) / (window.innerHeight) frustumSize = 10 camera = new THREE.OrthographicCamera( frustumSize * aspect / -2, frustumSize * aspect / 2, frustumSize / 2, frustumSize / -2, 0.01, 5000 ) camera.position.x = 5 } function SetupControls(){ controls = new OrbitControls(camera, renderer.domElement) controls.enableRotate = false; controls.enablePan = false controls.zoomToCursor = false; controls.mouseButtons = { RIGHT: THREE.MOUSE.ROTATE, MIDDLE: THREE.MOUSE.DOLLY, LEFT: THREE.MOUSE.PAN } camera.position.set(0, 0, 20) controls.update() } function AddLights() { // Add Ambient Light const ambientLight = new THREE.AmbientLight(0xffffff, 10); // Soft white light scene.add(ambientLight); // Add Directional Light const directionalLight = new THREE.DirectionalLight(0xffffff, 0); // Soft white light directionalLight.position.set(-5, 0, 5).normalize(); scene.add(directionalLight); } const matrix = new THREE.Matrix4() function animate(){ requestAnimationFrame(animate) // Rotate instanced meshes const time = performance.now() * 0.001; const rotationSpeedX = 0.02; const rotationSpeedY = 0.01; lastTime = time for (let i = 0; i < count; i++) { instancedMesh.getMatrixAt(i, matrix) matrix.decompose(dummy.position, dummy.rotation, dummy.scale) dummy.rotation.set(i/1000 * time, i/1000 * time, i/1000 * time); dummy.updateMatrix(); instancedMesh.setMatrixAt(i, dummy.matrix); } instancedMesh.instanceMatrix.needsUpdate = true composer.render() //renderer.render(scene, camera) } window.addEventListener('resize', () => { aspect = (window.innerWidth) / (window.innerHeight) camera.left = -frustumSize * aspect / 2 camera.right = frustumSize * aspect / 2 camera.top = frustumSize / 2 camera.bottom = -frustumSize / 2 camera.updateProjectionMatrix() renderer.setPixelRatio(window.devicePixelRatio) renderer.setSize(window.innerWidth, window.innerHeight) }) init()