post processing

This commit is contained in:
2025-07-29 00:50:30 +01:00
parent 8cab3ce92d
commit daa92c4c41
12 changed files with 20522 additions and 9 deletions

View File

@@ -6,6 +6,7 @@ import { LightingManager } from './lighting'
import { ModelLoader } from './model-loader'
import { PhysicsManager } from './physics'
import { EventManager } from './event-manager'
import { PostProcessingManager } from './post-processing'
class AAFHomepage {
private scene: THREE.Scene
@@ -20,6 +21,8 @@ class AAFHomepage {
private lastTime: number = 0
private modelLoader: ModelLoader
private eventManager: EventManager
private postProcessing!: PostProcessingManager
private usePostProcessing: boolean = true
constructor() {
this.scene = new THREE.Scene()
@@ -85,6 +88,21 @@ class AAFHomepage {
// Setup lighting
LightingManager.setupLighting(this.scene)
// Initialize post-processing
this.postProcessing = new PostProcessingManager(this.renderer, this.scene, this.camera)
// Setup window resize handler
window.addEventListener('resize', () => {
const width = window.innerWidth
const height = window.innerHeight
this.camera.aspect = width / height
this.camera.updateProjectionMatrix()
this.renderer.setSize(width, height)
this.postProcessing.resize(width, height)
})
// Load and create objects from the main model
this.loadAndCreateObjects()
@@ -117,7 +135,14 @@ class AAFHomepage {
clampedDeltaTime
)
this.renderer.render(this.scene, this.camera)
// Use post-processing rendering
if (this.usePostProcessing) {
this.postProcessing.update(clampedDeltaTime)
this.postProcessing.render(clampedDeltaTime)
} else {
// Fallback to direct rendering
this.renderer.render(this.scene, this.camera)
}
}
}

121
app/src/motion-blur.ts Normal file
View File

@@ -0,0 +1,121 @@
import * as THREE from 'three'
export class MotionBlurEffect {
private scene: THREE.Scene
private camera: THREE.PerspectiveCamera
private renderer: THREE.WebGLRenderer
private renderTarget1: THREE.WebGLRenderTarget
private renderTarget2: THREE.WebGLRenderTarget
private blurMaterial: THREE.ShaderMaterial
private quad: THREE.Mesh
private intensity: number = 0.5
private accumulation: number = 0.8
constructor(
scene: THREE.Scene,
camera: THREE.PerspectiveCamera,
renderer: THREE.WebGLRenderer
) {
this.scene = scene
this.camera = camera
this.renderer = renderer
const size = renderer.getSize(new THREE.Vector2())
// Create render targets for accumulation
this.renderTarget1 = new THREE.WebGLRenderTarget(size.x, size.y, {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBAFormat,
type: THREE.FloatType
})
this.renderTarget2 = new THREE.WebGLRenderTarget(size.x, size.y, {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBAFormat,
type: THREE.FloatType
})
// Motion blur shader material
this.blurMaterial = new THREE.ShaderMaterial({
uniforms: {
tCurrent: { value: null },
tPrevious: { value: null },
intensity: { value: this.intensity },
accumulation: { value: this.accumulation }
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform sampler2D tCurrent;
uniform sampler2D tPrevious;
uniform float intensity;
uniform float accumulation;
varying vec2 vUv;
void main() {
vec4 current = texture2D(tCurrent, vUv);
vec4 previous = texture2D(tPrevious, vUv);
// Blend current frame with accumulated previous frames
vec4 result = mix(current, previous, accumulation * intensity);
gl_FragColor = result;
}
`
})
// Create fullscreen quad
const geometry = new THREE.PlaneGeometry(2, 2)
this.quad = new THREE.Mesh(geometry, this.blurMaterial)
}
public render(deltaTime: number): THREE.WebGLRenderTarget {
// Render current frame to renderTarget1
this.renderer.setRenderTarget(this.renderTarget1)
this.renderer.render(this.scene, this.camera)
// Apply motion blur by blending with previous frame
this.blurMaterial.uniforms.tCurrent.value = this.renderTarget1.texture
this.blurMaterial.uniforms.tPrevious.value = this.renderTarget2.texture
// Render blurred result to renderTarget2
this.renderer.setRenderTarget(this.renderTarget2)
this.renderer.render(this.quad, new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1))
// Swap render targets for next frame
const temp = this.renderTarget1
this.renderTarget1 = this.renderTarget2
this.renderTarget2 = temp
return this.renderTarget1
}
public setIntensity(intensity: number): void {
this.intensity = intensity
this.blurMaterial.uniforms.intensity.value = intensity
}
public setAccumulation(accumulation: number): void {
this.accumulation = accumulation
this.blurMaterial.uniforms.accumulation.value = accumulation
}
public resize(width: number, height: number): void {
this.renderTarget1.setSize(width, height)
this.renderTarget2.setSize(width, height)
}
public dispose(): void {
this.renderTarget1.dispose()
this.renderTarget2.dispose()
this.blurMaterial.dispose()
this.quad.geometry.dispose()
}
}

122
app/src/post-processing.ts Normal file
View File

@@ -0,0 +1,122 @@
import * as THREE from 'three'
import {
EffectComposer,
EffectPass,
RenderPass,
BloomEffect,
SSAOEffect,
DepthOfFieldEffect,
NormalPass,
SMAAEffect
} from 'postprocessing'
export class PostProcessingManager {
private composer: EffectComposer
private bloomEffect: BloomEffect
private ssaoEffect: SSAOEffect
private depthOfFieldEffect: DepthOfFieldEffect
private normalPass: NormalPass
constructor(
renderer: THREE.WebGLRenderer,
scene: THREE.Scene,
camera: THREE.PerspectiveCamera
) {
// Create effect composer
this.composer = new EffectComposer(renderer)
// Add render pass (renders the scene)
const renderPass = new RenderPass(scene, camera)
this.composer.addPass(renderPass)
// Create normal pass for SSAO
this.normalPass = new NormalPass(scene, camera)
this.composer.addPass(this.normalPass)
// Create bloom effect - enhanced for more dramatic glow
this.bloomEffect = new BloomEffect({
intensity: 1.2,
luminanceThreshold: 0.1,
luminanceSmoothing: 0.15,
radius: 0.9,
mipmapBlur: true
})
// Create SSAO effect - enhanced for better ambient occlusion
this.ssaoEffect = new SSAOEffect(camera, this.normalPass.texture, {
intensity: 0.8,
fade: 0.01,
radius: 0.15,
samples: 32,
rings: 4,
worldDistanceThreshold: 20,
worldDistanceFalloff: 5,
worldProximityThreshold: 0.0005,
worldProximityFalloff: 0.001,
luminanceInfluence: 0.7,
bias: 0.02
})
// Create depth of field effect - tighter focus for more dramatic effect
this.depthOfFieldEffect = new DepthOfFieldEffect(camera, {
focusDistance: 0.015, // Closer focus point
focalLength: 0.12, // Shorter focal length for stronger blur
bokehScale: 4.0, // Larger bokeh for more pronounced blur
height: 480
})
// Create SMAA effect for better antialiasing
const smaaEffect = new SMAAEffect()
// Combine effects in a single pass for better performance
const effectPass = new EffectPass(
camera,
this.bloomEffect,
this.ssaoEffect,
this.depthOfFieldEffect,
smaaEffect
)
this.composer.addPass(effectPass)
}
public render(deltaTime?: number): void {
this.composer.render(deltaTime)
}
public resize(width: number, height: number): void {
this.composer.setSize(width, height)
}
// Control methods for real-time adjustments
public setBloomIntensity(intensity: number): void {
this.bloomEffect.intensity = intensity
}
public setSSAOIntensity(intensity: number): void {
this.ssaoEffect.intensity = intensity
}
public setDepthOfFieldFocus(focusDistance: number): void {
this.depthOfFieldEffect.circleOfConfusionMaterial.uniforms.focusDistance.value = focusDistance
}
// Get the composer for advanced usage
public getComposer(): EffectComposer {
return this.composer
}
// Update method for any per-frame updates
public update(_deltaTime: number): void {
// Add any custom update logic here if needed
}
// Dispose method for cleanup
public dispose(): void {
this.composer.dispose()
this.bloomEffect.dispose()
this.ssaoEffect.dispose()
this.depthOfFieldEffect.dispose()
this.normalPass.dispose()
}
}