|
@ -2,26 +2,40 @@ import * as THREE from 'three' |
|
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' |
|
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' |
|
|
import { Vector3 } from 'three' |
|
|
import { Vector3 } from 'three' |
|
|
import { randFloat, randInt } from 'three/src/math/MathUtils.js' |
|
|
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 |
|
|
let scene, camera, renderer, composer |
|
|
let aspect, frustumSize |
|
|
let aspect, frustumSize |
|
|
let mesh |
|
|
let mesh |
|
|
let delta |
|
|
let delta |
|
|
let controls |
|
|
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() |
|
|
var clock = new THREE.Clock() |
|
|
|
|
|
const matrix = new THREE.Matrix4() |
|
|
|
|
|
|
|
|
function init(){ |
|
|
function init(){ |
|
|
SetupRenderer() |
|
|
SetupRenderer() |
|
|
scene = new THREE.Scene() |
|
|
scene = new THREE.Scene() |
|
|
scene.background = new THREE.Color('white') |
|
|
scene.background = new THREE.Color('white') |
|
|
SetupCamera() |
|
|
SetupCamera() |
|
|
|
|
|
//AddLights()
|
|
|
SetupControls() |
|
|
SetupControls() |
|
|
|
|
|
SetupComposer() |
|
|
SetupObjects() |
|
|
SetupObjects() |
|
|
animate() |
|
|
animate() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function SetupRenderer(){ |
|
|
function SetupRenderer(){ |
|
|
renderer = new THREE.WebGLRenderer({ antialias: true }) |
|
|
renderer = new THREE.WebGLRenderer({alpha: true}) |
|
|
renderer.setSize(window.innerWidth, window.innerHeight) |
|
|
renderer.setSize(window.innerWidth, window.innerHeight) |
|
|
document.getElementById('container').appendChild(renderer.domElement) |
|
|
document.getElementById('container').appendChild(renderer.domElement) |
|
|
} |
|
|
} |
|
@ -55,20 +69,122 @@ function SetupControls(){ |
|
|
controls.update() |
|
|
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() { |
|
|
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 geo = new THREE.BoxGeometry(1, 1, 1) |
|
|
let mat = new THREE.MeshNormalMaterial() |
|
|
let mat = new THREE.MeshBasicMaterial({map: txt}) |
|
|
mesh = new THREE.Mesh(geo, mat) |
|
|
count = 1000 |
|
|
scene.add(mesh) |
|
|
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(){ |
|
|
function animate(){ |
|
|
requestAnimationFrame(animate) |
|
|
requestAnimationFrame(animate) |
|
|
controls.update() |
|
|
controls.update() |
|
|
delta = clock.getDelta() / 10 |
|
|
delta = clock.getDelta() * 0.4 |
|
|
mesh.rotation.x += delta |
|
|
const time = performance.now() * 0.001; |
|
|
mesh.rotation.z += delta |
|
|
const rotationSpeedX = 0.02; |
|
|
renderer.render(scene, camera) |
|
|
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', () => { |
|
|
window.addEventListener('resize', () => { |
|
|