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'; let scene, camera, renderer let aspect, frustumSize let time = 0 let lastTime = 0 let controls let mesh let composer let container_element = document.getElementById('container-article') let containerW = container_element.offsetWidth let containerH = container_element.offsetHeight let imgW, imgH, imgRatio var clock = new THREE.Clock(); function init(){ SetupRenderer() scene = new THREE.Scene() SetupCamera() SetupControls() composer = new EffectComposer( renderer ); const renderPixelatedPass = new RenderPixelatedPass( 3, scene, camera ); renderPixelatedPass.normalEdgeStrength = 1; renderPixelatedPass.depthEdgeStrength = 1; renderPixelatedPass.pixelAlignedPanning = false; composer.addPass( renderPixelatedPass ); const outputPass = new OutputPass(); composer.addPass( outputPass ); AddLights() const textureLoader = new THREE.TextureLoader() textureLoader.crossOrigin = "Anonymous" const texture = textureLoader.load(imageUrl, function ( tex ) { // tex and texture are the same in this example, but that might not always be the case imgW = tex.image.width imgH = tex.image.height imgRatio = imgW / imgH tex.minFilter = THREE.NearestFilter tex.colorSpace = THREE.SRGBColorSpace; tex.wrapS = THREE.RepeatWrapping; tex.wrapT = THREE.RepeatWrapping; const geom = new THREE.PlaneGeometry(imgRatio, 1) const mat = new THREE.MeshPhongMaterial({map: texture}) mesh = new THREE.Mesh(geom, mat) scene.add(mesh) container_element.style.opacity = 1; animate() } ); } function SetupRenderer(){ renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }) renderer.setClearColor(0xffffff, 0); renderer.setSize(containerW, containerH) document.getElementById('container-article').appendChild(renderer.domElement) } function SetupCamera(){ aspect = (containerW) / (containerH) frustumSize = 0.5 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 } controls.minZoom = 0.3 controls.maxZoom = 0.75 camera.position.set(0, 0, 5) controls.update() } function AddLights() { // Add Ambient Light const ambientLight = new THREE.AmbientLight(0xffffff, 3); // Soft white light scene.add(ambientLight); // Add Directional Light const directionalLight = new THREE.DirectionalLight(0x0075FF, 4); // Soft white light directionalLight.position.set(-5, 0, 5).normalize(); //scene.add(directionalLight); } function animate(){ requestAnimationFrame(animate) const deltaTime = (time - lastTime) / 1000 controls.update() let sin = Math.sin(clock.getElapsedTime() * 0.5) mesh.rotation.z = sin * 0.1 mesh.rotation.x = sin * 0.15 composer.render() } function getContainerDimensions(){ containerW = container_element.offsetWidth containerH = container_element.offsetHeight } window.addEventListener('resize', () => { getContainerDimensions() aspect = (containerW) / (containerH) 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(containerW, containerH) }) init()