From f4f096dac6718cbaacbc17c309e8a63f52665c9f Mon Sep 17 00:00:00 2001 From: Charles Doucet Date: Tue, 18 Feb 2025 18:35:17 -0500 Subject: [PATCH 1/2] feat: new post processing rendering workflow --- .../three-background/animation-loop.ts | 15 +++++ .../index.ts | 5 +- .../particules.ts | 61 ++++++++++++++++--- .../three-background/post-process.ts | 43 +++++++++++++ .../scene.ts | 30 +++------ .../styles.css | 0 src/views/utils/backgrounds-utils.ts | 2 +- 7 files changed, 124 insertions(+), 32 deletions(-) create mode 100644 src/components/three-background/animation-loop.ts rename src/components/{particule-background => three-background}/index.ts (61%) rename src/components/{particule-background => three-background}/particules.ts (52%) create mode 100644 src/components/three-background/post-process.ts rename src/components/{particule-background => three-background}/scene.ts (85%) rename src/components/{particule-background => three-background}/styles.css (100%) diff --git a/src/components/three-background/animation-loop.ts b/src/components/three-background/animation-loop.ts new file mode 100644 index 0000000..f2d74f0 --- /dev/null +++ b/src/components/three-background/animation-loop.ts @@ -0,0 +1,15 @@ +import {updateBgObjects} from "./particules.ts"; +import {renderWithPostProcess} from "./post-process.ts"; +import {camera, cameraTarget} from "./scene.ts"; +import * as THREE from "three"; + +let cameraLookAt = new THREE.Vector3(0, 0, 0); + +export const animate = () => { + requestAnimationFrame(animate); + camera.position.lerp(cameraTarget, 0.2); + camera.lookAt(cameraLookAt); + + updateBgObjects(); + renderWithPostProcess(); +} diff --git a/src/components/particule-background/index.ts b/src/components/three-background/index.ts similarity index 61% rename from src/components/particule-background/index.ts rename to src/components/three-background/index.ts index d471766..96c9d00 100644 --- a/src/components/particule-background/index.ts +++ b/src/components/three-background/index.ts @@ -1,6 +1,8 @@ import "./styles.css"; -import {animate, initCamera, initCanvas, initScene, initStage, renderer} from "./scene.ts"; +import {initCamera, initCanvas, initScene, initStage, renderer} from "./scene.ts"; import {initBgObjects} from "./particules.ts"; +import {initPostProcess} from "./post-process.ts"; +import {animate} from "./animation-loop.ts"; //----------------------------------------------------------------------- @@ -10,6 +12,7 @@ export function animatedBackground() { initCanvas(); initCamera(); initBgObjects(); + initPostProcess(); animate(); return renderer.domElement; diff --git a/src/components/particule-background/particules.ts b/src/components/three-background/particules.ts similarity index 52% rename from src/components/particule-background/particules.ts rename to src/components/three-background/particules.ts index 500f675..a96afb5 100644 --- a/src/components/particule-background/particules.ts +++ b/src/components/three-background/particules.ts @@ -2,37 +2,67 @@ import {scene, windowHeight, windowWidth} from "./scene.ts"; +type FloatingMesh = { + readonly size: number, + readonly geometry: THREE.PolyhedronGeometry, + readonly material: THREE.MeshStandardMaterial, +} + +const icoSphere: FloatingMesh = { + size: Math.random() * 300 + 5, + geometry: new THREE.IcosahedronGeometry(1, 0), + material: new THREE.MeshStandardMaterial( { + color: 0xdddddd, + wireframe: true, + emissive: 0xdddddd, // Add emissive color + emissiveIntensity: 0.5 // Set + } ) +} + //----------------------------------------------------------------------- export const initBgObjects = () => { for (let i = 0; i < 1000; i++) { - createBgObject(); + createBgObject(icoSphere); } - createCurvedPlane(); + createGiantWireSphere(); + createBackdropPlane(); } +export const updateBgObjects = () => { + scene.children.forEach((child) => { + if (child instanceof THREE.Mesh && child.userData.rotationSpeed) { + child.rotation.x += child.userData.rotationSpeed.x; + child.rotation.y += child.userData.rotationSpeed.y; + } + }); +} //----------------------------------------------------------------------- const createBgObject = () => { + const size = Math.random() * 30 + 5; - const geometry = new THREE.IcosahedronGeometry(size, 1); - const material = new THREE.MeshBasicMaterial( { + const geometry = new THREE.IcosahedronGeometry(size, 0); + const material = new THREE.MeshStandardMaterial( { color: 0xdddddd, - wireframe: true + wireframe: true, + emissive: 0xdddddd, // Add emissive color + emissiveIntensity: 0.5 // Set } ); - const sphere = new THREE.Mesh( geometry, material ); - scene.add( sphere ); + const x = pointInMargins(); const y = Math.random() * windowHeight * 15 - windowHeight * 7.5; const z = Math.random() * -2000 - 200; + const sphere = new THREE.Mesh( geometry, material ); sphere.userData.rotationSpeed = { x: Math.random() * 0.02 - 0.01, y: Math.random() * 0.02 - 0.01 }; + scene.add( sphere ); sphere.position.set(x, y, z); } @@ -51,11 +81,22 @@ const pointInMargins = () => { } -const createCurvedPlane = () => { - const geometry = new THREE.PlaneGeometry(windowWidth * 20, windowHeight * 20, 32, 32); +const createGiantWireSphere = () => { + const geometry = new THREE.IcosahedronGeometry(3250, 3); + const material = new THREE.MeshBasicMaterial( { + color: 0xdddddd, + wireframe: true, + } ); + + const sphere = new THREE.Mesh( geometry, material ); + scene.add(sphere); + sphere.position.set(0, 0, 0); +} - const material = new THREE.MeshBasicMaterial({ color: 0x000000 }); +const createBackdropPlane = () => { + const geometry = new THREE.PlaneGeometry(windowWidth * 20, windowHeight * 20, 32, 32); + const material = new THREE.MeshBasicMaterial({ color: 0x000000 }); const plane = new THREE.Mesh(geometry, material); scene.add(plane); plane.position.set(windowWidth, 0, -2000); // Position the plane in the far distance diff --git a/src/components/three-background/post-process.ts b/src/components/three-background/post-process.ts new file mode 100644 index 0000000..4a3ba7d --- /dev/null +++ b/src/components/three-background/post-process.ts @@ -0,0 +1,43 @@ +import { camera, renderer, scene, windowHeight, windowWidth } from "./scene.ts"; +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; +import {Vector2} from "three"; + +let composer: EffectComposer; + +const postProcessConfigs = { + size: Vector2, + threshold: 0.05, + strength: 0.4, + radius: 0.35, + exposure: 0 +}; + + +export const initPostProcess = () => { + const renderScene = new RenderPass( scene, camera ); + + const bloomPass = new UnrealBloomPass( + new Vector2( windowWidth, windowHeight ), // size + postProcessConfigs.strength, // strength + postProcessConfigs.radius, // radius + postProcessConfigs.threshold // threshold + ); + + const outputPass = new OutputPass(); + + composer = new EffectComposer( renderer ); + composer.addPass( renderScene ); + composer.addPass( bloomPass ); + composer.addPass( outputPass ); +} + +export const resizeComposer = () => { + composer.setSize( windowWidth, windowHeight ); +} + +export const renderWithPostProcess = () => { + composer.render(); +} diff --git a/src/components/particule-background/scene.ts b/src/components/three-background/scene.ts similarity index 85% rename from src/components/particule-background/scene.ts rename to src/components/three-background/scene.ts index 432e966..413ab52 100644 --- a/src/components/particule-background/scene.ts +++ b/src/components/three-background/scene.ts @@ -1,12 +1,12 @@ import * as THREE from 'three'; - +import {resizeComposer} from "./post-process.ts"; export let renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.PerspectiveCamera, cameraTarget = new THREE.Vector3(0, 0 ,800), - windowWidth: number, - windowHeight: number; + windowWidth = window.innerWidth, + windowHeight = window.innerHeight; let graphicCanvas, canvasWidth = 240, @@ -14,8 +14,7 @@ let graphicCanvas, mouseX = 0, mouseY = 0, windowHalfWidth: number, - windowHalfHeight: number, - cameraLookAt = new THREE.Vector3(0, 0, 0); + windowHalfHeight: number; const mouseSensitivity = 0.1; const cameraTilt = 35; @@ -32,6 +31,7 @@ export const initStage = () => { export const initScene = () => { scene = new THREE.Scene(); scene.fog = new THREE.Fog(0x010102, 1, 3000); + scene.add( new THREE.AmbientLight( 0xcccccc ) ); renderer = new THREE.WebGLRenderer({ @@ -62,23 +62,15 @@ export const initCanvas = () => { graphicCanvas.height = canvasHeight; } -export const setWindowSize = () => { +//----------------------------------------------------------------------- + +const setWindowSize = () => { windowWidth = window.innerWidth; windowHeight = window.innerHeight; windowHalfWidth = windowWidth / 2; windowHalfHeight = windowHeight / 2; } -export const animate = () => { - requestAnimationFrame(animate); - camera.position.lerp(cameraTarget, 0.2); - camera.lookAt(cameraLookAt); - - render(); -} - -//----------------------------------------------------------------------- - const onMouseMove = (event: MouseEvent) => { mouseX = (event.clientX - windowHalfWidth); mouseY = (event.clientY - windowHalfHeight); @@ -93,13 +85,11 @@ const onMouseMove = (event: MouseEvent) => { const onWindowResize = () => { setWindowSize(); + resizeComposer(); camera.aspect = windowWidth / windowHeight; camera.updateProjectionMatrix(); renderer.setSize(windowWidth, windowHeight); + renderer.toneMapping = THREE.ReinhardToneMapping; } - -const render = () => { - renderer.render(scene, camera); -} \ No newline at end of file diff --git a/src/components/particule-background/styles.css b/src/components/three-background/styles.css similarity index 100% rename from src/components/particule-background/styles.css rename to src/components/three-background/styles.css diff --git a/src/views/utils/backgrounds-utils.ts b/src/views/utils/backgrounds-utils.ts index f878ed0..098ff95 100644 --- a/src/views/utils/backgrounds-utils.ts +++ b/src/views/utils/backgrounds-utils.ts @@ -1,4 +1,4 @@ -import {animatedBackground} from "../../components/particule-background"; +import {animatedBackground} from "../../components/three-background"; export function createBackground() { From ea295f92e495abec492a8c96d6e022357ab06c66 Mon Sep 17 00:00:00 2001 From: Charles Doucet Date: Tue, 18 Feb 2025 21:46:25 -0500 Subject: [PATCH 2/2] feat: perfect post-process --- src/components/three-background/index.ts | 4 +- src/components/three-background/particules.ts | 73 ++++++++++--------- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/src/components/three-background/index.ts b/src/components/three-background/index.ts index 96c9d00..2f1bff3 100644 --- a/src/components/three-background/index.ts +++ b/src/components/three-background/index.ts @@ -1,6 +1,6 @@ import "./styles.css"; import {initCamera, initCanvas, initScene, initStage, renderer} from "./scene.ts"; -import {initBgObjects} from "./particules.ts"; +import {initBgMeshes} from "./particules.ts"; import {initPostProcess} from "./post-process.ts"; import {animate} from "./animation-loop.ts"; @@ -11,7 +11,7 @@ export function animatedBackground() { initScene(); initCanvas(); initCamera(); - initBgObjects(); + initBgMeshes(); initPostProcess(); animate(); diff --git a/src/components/three-background/particules.ts b/src/components/three-background/particules.ts index a96afb5..8d9dd1a 100644 --- a/src/components/three-background/particules.ts +++ b/src/components/three-background/particules.ts @@ -2,28 +2,39 @@ import {scene, windowHeight, windowWidth} from "./scene.ts"; -type FloatingMesh = { - readonly size: number, - readonly geometry: THREE.PolyhedronGeometry, - readonly material: THREE.MeshStandardMaterial, -} +const meshWhiteMaterial = new THREE.MeshStandardMaterial( { + color: 0xdddddd, + wireframe: true, + emissive: 0xdddddd, + emissiveIntensity: 0.5, +}); + +const meshBlueMaterial = new THREE.MeshStandardMaterial( { + color: 0x3BFFC5, + wireframe: false, + emissive: 0x3BFFC5, + emissiveIntensity: 0.5, +}); + -const icoSphere: FloatingMesh = { - size: Math.random() * 300 + 5, - geometry: new THREE.IcosahedronGeometry(1, 0), - material: new THREE.MeshStandardMaterial( { - color: 0xdddddd, - wireframe: true, - emissive: 0xdddddd, // Add emissive color - emissiveIntensity: 0.5 // Set - } ) -} //----------------------------------------------------------------------- -export const initBgObjects = () => { +export const initBgMeshes = () => { for (let i = 0; i < 1000; i++) { - createBgObject(icoSphere); + const meshSize = Math.random() * 30 + 5; + createBgMeshInMargins( + new THREE.IcosahedronGeometry(meshSize, 0), + meshWhiteMaterial + ); + } + + for (let i = 0; i < 100; i++) { + const meshSize = Math.random() * 30 + 5; + createBgMeshInMargins( + new THREE.CylinderGeometry(meshSize, meshSize, 5, 32), + meshBlueMaterial + ); } createGiantWireSphere(); @@ -41,29 +52,23 @@ export const updateBgObjects = () => { //----------------------------------------------------------------------- -const createBgObject = () => { - - const size = Math.random() * 30 + 5; - const geometry = new THREE.IcosahedronGeometry(size, 0); - const material = new THREE.MeshStandardMaterial( { - color: 0xdddddd, - wireframe: true, - emissive: 0xdddddd, // Add emissive color - emissiveIntensity: 0.5 // Set - } ); - +const createBgMeshInMargins = ( + spawnMesh: THREE.CylinderGeometry | THREE.IcosahedronGeometry | THREE.TorusGeometry | THREE.BoxGeometry, + meshMaterial: THREE.MeshStandardMaterial +) => { const x = pointInMargins(); const y = Math.random() * windowHeight * 15 - windowHeight * 7.5; const z = Math.random() * -2000 - 200; - const sphere = new THREE.Mesh( geometry, material ); - sphere.userData.rotationSpeed = { + const mesh = new THREE.Mesh( spawnMesh, meshMaterial ); + + mesh.userData.rotationSpeed = { x: Math.random() * 0.02 - 0.01, y: Math.random() * 0.02 - 0.01 }; - scene.add( sphere ); - sphere.position.set(x, y, z); + scene.add( mesh ); + mesh.position.set(x, y, z); } @@ -96,10 +101,10 @@ const createGiantWireSphere = () => { const createBackdropPlane = () => { const geometry = new THREE.PlaneGeometry(windowWidth * 20, windowHeight * 20, 32, 32); - const material = new THREE.MeshBasicMaterial({ color: 0x000000 }); + const material = new THREE.MeshBasicMaterial({ color: 0x555555 }); const plane = new THREE.Mesh(geometry, material); scene.add(plane); - plane.position.set(windowWidth, 0, -2000); // Position the plane in the far distance + plane.position.set(windowWidth, 0, -2000); };