import * as THREE from 'three' import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' import { Vector3 } from 'three' import { randFloat, randInt } from 'three/src/math/MathUtils.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 { GUI } from 'three/addons/libs/lil-gui.module.min.js'; let scene, camera, renderer, composer let aspect, frustumSize let mesh let delta let controls let gui, params let instancedMesh, dummy, count, lastTime let frames = []; const totalFrames = 300; // Number of frames to capture let frameCount = 0; let iter = 0 var clock = new THREE.Clock() const matrix = new THREE.Matrix4() function init(){ SetupRenderer() scene = new THREE.Scene() scene.background = new THREE.Color('white') SetupCamera() //AddLights() SetupControls() SetupComposer() SetupObjects() animate() } function SetupRenderer(){ renderer = new THREE.WebGLRenderer({alpha: true}) 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.z = 20 } function SetupControls(){ controls = new OrbitControls(camera, renderer.domElement) controls.enableRotate = false; controls.enablePan = true controls.zoomToCursor = true; 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(0xFF0000, 5); // Soft white light scene.add(ambientLight); // Add Directional Light const directionalLight = new THREE.DirectionalLight(0xFF0000, 10); // Soft white light directionalLight.position.set(-5, 0, 5).normalize(); scene.add(directionalLight); } function SetupComposer(){ composer = new EffectComposer( renderer ) const renderPixelPass = new RenderPixelatedPass( 8, scene, camera ) renderPixelPass.normalEdgeStrength = 0 renderPixelPass.depthEdgeStrength = 0 composer.addPass( renderPixelPass ) const outputPass = new OutputPass() composer.addPass( outputPass ) // gui = new GUI() // params = { pixelSize: 3, normalEdgeStrength: .3, depthEdgeStrength: .4, pixelAlignedPanning: true }; // gui.add( params, 'pixelSize' ).min( 1 ).max( 16 ).step( 1 ) // .onChange( () => { // renderPixelPass.setPixelSize( params.pixelSize ); // } ); // gui.add( renderPixelPass, 'normalEdgeStrength' ).min( 0 ).max( 2 ).step( .05 ); // gui.add( renderPixelPass, 'depthEdgeStrength' ).min( 0 ).max( 1 ).step( .05 ); // gui.add( params, 'pixelAlignedPanning' ); } function SetupObjects() { const loader = new THREE.TextureLoader() let txt = loader.load('../images/cn-logo-green.png') txt.magFilter = THREE.NearestFilter txt.minFilter = THREE.NearestFilter txt.wrapS = THREE.RepeatWrapping txt.wrapT = THREE.RepeatWrapping txt.colorSpace = THREE.SRGBColorSpace let geo = new THREE.BoxGeometry(1, 1, 1) let mat = new THREE.MeshBasicMaterial({map: txt}) count = 1000 instancedMesh = new THREE.InstancedMesh(geo, mat, 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(-20, 20), randFloat(-20,20), 0); dummy.updateMatrix(); instancedMesh.setMatrixAt(i, dummy.matrix); } } function captureFrame() { renderer.domElement.toDataURL('image/png').replace("image/png", "image/octet-stream"); let imgData = renderer.domElement.toDataURL("image/png"); frames.push(imgData); frameCount++; // If last frame, prepare the download if (frameCount === totalFrames) { //prepareDownload(); if( iter == 0 ){ frameCount = 0 iter = 1 } } } function prepareDownload() { let zip = new JSZip(); console.log('zipping..') frames.forEach((dataUrl, index) => { let imgData = dataUrl.split(',')[1]; // Get base64 data part zip.file(`frame_${index.toString().padStart(4, '0')}.png`, imgData, {base64: true}); }); console.log('zipped!') zip.generateAsync({type: "blob"}) .then(function(content) { const a = document.createElement("a"); a.href = URL.createObjectURL(content); a.download = "frames.zip"; console.log('zip dl ready!') a.click(); }); } function animate(){ requestAnimationFrame(animate) controls.update() delta = clock.getDelta() * 0.4 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 // renderer.render(scene, camera) composer.render() // Capture the frame if (frameCount < totalFrames) { captureFrame(); } } 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()