Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@
"webgpu_volume_cloud",
"webgpu_volume_lighting",
"webgpu_volume_lighting_rectarea",
"webgpu_volume_lighting_traa",
"webgpu_volume_perlin",
"webgpu_water",
"webgpu_xr_rollercoaster",
Expand Down
129 changes: 113 additions & 16 deletions examples/jsm/tsl/display/RetroPassNode.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { MeshBasicNodeMaterial, PassNode, UnsignedByteType, NearestFilter } from 'three/webgpu';
import { float, vec2, vec4, Fn, uv, varying, cameraProjectionMatrix, cameraViewMatrix, positionWorld, screenSize, materialColor, replaceDefaultUV } from 'three/tsl';
import { MeshBasicNodeMaterial, PassNode, UnsignedByteType, NearestFilter, CubeMapNode, MeshPhongNodeMaterial } from 'three/webgpu';
import { float, vec2, vec4, Fn, uv, varying, cameraProjectionMatrix, cameraViewMatrix, positionWorld, screenSize, materialColor, uint, texture, uniform, context, reflectVector } from 'three/tsl';

const _affineUv = varying( vec2() );
const _w = varying( float() );
Expand Down Expand Up @@ -50,7 +50,8 @@ class RetroPassNode extends PassNode {
super( PassNode.COLOR, scene, camera );

const {
affineDistortion = null
affineDistortion = null,
filterTextures = false
} = options;

this.setResolutionScale( .25 );
Expand All @@ -61,6 +62,8 @@ class RetroPassNode extends PassNode {

this.affineDistortionNode = affineDistortion;

this.filterTextures = filterTextures;

this._materialCache = new Map();

}
Expand All @@ -80,36 +83,125 @@ class RetroPassNode extends PassNode {

renderer.setRenderObjectFunction( ( object, scene, camera, geometry, material, ...params ) => {

let retroMaterial = this._materialCache.get( material );
const retroMaterialData = this._materialCache.get( material );

let retroMaterial;

if ( retroMaterialData === undefined || retroMaterialData.version !== material.version ) {

if ( retroMaterialData !== undefined ) {

retroMaterialData.material.dispose();

}

if ( material.isMeshBasicMaterial || material.isMeshBasicNodeMaterial ) {

retroMaterial = new MeshBasicNodeMaterial();

if ( retroMaterial === undefined ) {
} else {

retroMaterial = new MeshBasicNodeMaterial();
retroMaterial = new MeshPhongNodeMaterial();

}

retroMaterial.colorNode = material.colorNode || null;
retroMaterial.opacityNode = material.opacityNode || null;
retroMaterial.positionNode = material.positionNode || null;
retroMaterial.vertexNode = material.vertexNode || _clipSpaceRetro;

let colorNode = material.colorNode || materialColor;

if ( material.isMeshStandardNodeMaterial || material.isMeshStandardMaterial ) {

const envMap = material.envMap || scene.environment;

if ( envMap ) {

const reflection = new CubeMapNode( texture( envMap ) );

let metalness;

if ( material.metalnessNode ) {

metalness = material.metalnessNode;

} else {

metalness = uniform( material.metalness ).onRenderUpdate( ( { material } ) => material.metalness );

if ( material.metalnessMap ) {

const textureUniform = texture( material.metalnessMap ).onRenderUpdate( ( { material } ) => material.metalnessMap );

metalness = metalness.mul( textureUniform.b );

}

}

colorNode = metalness.mix( colorNode, reflection );

}

}

retroMaterial.colorNode = colorNode;

//

const contextData = {};

if ( this.affineDistortionNode ) {

retroMaterial.colorNode = replaceDefaultUV( () => {
contextData.getUV = ( texture ) => {

let finalUV;

if ( texture.isCubeTextureNode ) {

return this.affineDistortionNode.mix( uv(), _affineUv.div( _w ) );
finalUV = reflectVector;

}, retroMaterial.colorNode || materialColor );
} else {

finalUV = this.affineDistortionNode.mix( uv(), _affineUv.div( _w ) );

}

return finalUV;

};

}

this._materialCache.set( material, retroMaterial );
if ( this.filterTextures !== true ) {

contextData.getTextureLevel = () => uint( 0 );

}

retroMaterial.contextNode = context( contextData );

//

this._materialCache.set( material, {
material: retroMaterial,
version: material.version
} );

} else {

retroMaterial = retroMaterialData.material;

}

retroMaterial.map = material.map;
retroMaterial.color = material.color;
retroMaterial.opacity = material.opacity;
retroMaterial.transparent = material.transparent;
retroMaterial.side = material.side;
for ( const property in material ) {

if ( retroMaterial[ property ] === undefined ) continue;

retroMaterial[ property ] = material[ property ];

}

renderer.renderObject( object, scene, camera, geometry, retroMaterial, ...params );

Expand All @@ -131,7 +223,12 @@ class RetroPassNode extends PassNode {

super.dispose();

this._materialCache.forEach( material => material.dispose() );
this._materialCache.forEach( ( data ) => {

data.material.dispose();

} );

this._materialCache.clear();

}
Expand Down
Binary file modified examples/screenshots/webgpu_postprocessing_retro.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
113 changes: 99 additions & 14 deletions examples/webgpu_postprocessing_retro.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,21 @@
<script type="module">

import * as THREE from 'three/webgpu';
import { pass, mix, mul, oneMinus, positionLocal, smoothstep, texture, time, rotateUV, Fn, uv, vec2, vec3, vec4, uniform, posterize, floor, float, sin, fract, dot, step, color, normalWorld, length, atan, replaceDefaultUV } from 'three/tsl';
import { pass, mix, mul, oneMinus, positionLocal, smoothstep, texture, time, rotateUV, Fn, uv, vec2, vec3, vec4, uniform, posterize, floor, float, sin, fract, dot, step, color, normalWorld, length, atan, replaceDefaultUV, screenSize } from 'three/tsl';
import { retroPass } from 'three/addons/tsl/display/RetroPassNode.js';
import { bayerDither } from 'three/addons/tsl/math/Bayer.js';
import { scanlines, vignette, colorBleeding, barrelUV } from 'three/addons/tsl/display/CRT.js';
import { circle } from 'three/addons/tsl/display/Shape.js';

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { HDRLoader } from 'three/addons/loaders/HDRLoader.js';

import { Inspector } from 'three/addons/inspector/Inspector.js';

let camera, scene, renderer, renderPipeline, controls;
let environment;
let currentModel;

init();

Expand Down Expand Up @@ -136,17 +139,90 @@
const gltfLoader = new GLTFLoader();
const textureLoader = new THREE.TextureLoader();

// baked model
// Model

gltfLoader.load(
'./models/gltf/coffeeMug.glb',
( gltf ) => {
const models = {
'Coffee Mug': 'models/gltf/coffeeMug.glb',
'Damaged Helmet': 'models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf',
};

gltf.scene.getObjectByName( 'baked' ).material.map.anisotropy = 8;
scene.add( gltf.scene );
function loadModel( name ) {

function loadEnvironment() {

if ( environment ) return;

environment = new HDRLoader()
.setPath( 'textures/equirectangular/' )
.load( 'venice_sunset_1k.hdr', ( texture ) => {

texture.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = texture;

// re-invalidate retro pass textures

retro.dispose();

} );

}
);

if ( currentModel ) {

scene.remove( currentModel );
currentModel.traverse( ( child ) => {

if ( child.isMesh ) {

child.geometry.dispose();
child.material.dispose();

}

} );

}

gltfLoader.load( models[ name ], ( gltf ) => {

currentModel = gltf.scene;
currentModel.position.set( 0, 0, 0 );

smoke.visible = false;

if ( name === 'Damaged Helmet' ) {

loadEnvironment();

currentModel.scale.setScalar( 3 );
currentModel.position.y = 1;

} else if ( name === 'Coffee Mug' ) {

smoke.visible = true;

}

scene.add( currentModel );

} );

}

loadModel( 'Coffee Mug' );

// lighting

const ambientLight = new THREE.AmbientLight( 0x404040, 2 );
scene.add( ambientLight );

const directionalLight = new THREE.DirectionalLight( 0xffffff, 3 );
directionalLight.position.set( 5, 10, 5 );
scene.add( directionalLight );

const pointLight = new THREE.PointLight( 0xff6600, 5, 20 );
pointLight.position.set( - 3, 3, 2 );
scene.add( pointLight );

// geometry

Expand Down Expand Up @@ -235,9 +311,9 @@
const colorDepthSteps = uniform( 32 );

// CRT effect parameters (subtle for PS1 look)
const scanlineIntensity = uniform( 0.08 ); // subtle scanlines
const scanlineCount = uniform( 60 ); // match vertical resolution
const scanlineSpeed = uniform( 0.05 ); // slow scroll
const scanlineIntensity = uniform( 0.3 ); // subtle scanlines
const scanlineDensity = uniform( 1 ); // 0.1-1: normalized scanline density (1 = full screen resolution)
const scanlineSpeed = uniform( 0.0 ); // no scanline movement
const vignetteIntensity = uniform( 0.3 ); // subtle vignette
const bleeding = uniform( 0.001 ); // minimal bleeding
const curvature = uniform( 0.02 ); // subtle curve
Expand All @@ -252,13 +328,15 @@
const distortedUV = barrelUV( curvature );
const distortedDelta = circle( curvature.add( .1 ).mul( 10 ), 1 ).mul( curvature ).mul( .05 );

let retroPipeline = retroPass( scene, camera, { affineDistortion } );
const retro = retroPass( scene, camera, { affineDistortion } );

let retroPipeline = retro;
retroPipeline = replaceDefaultUV( distortedUV, retroPipeline );
retroPipeline = colorBleeding( retroPipeline, bleeding.add( distortedDelta ) );
retroPipeline = bayerDither( retroPipeline, colorDepthSteps );
retroPipeline = posterize( retroPipeline, colorDepthSteps );
retroPipeline = vignette( retroPipeline, vignetteIntensity, 0.6 );
retroPipeline = scanlines( retroPipeline, scanlineIntensity, scanlineCount, scanlineSpeed );
retroPipeline = scanlines( retroPipeline, scanlineIntensity, screenSize.y.mul( scanlineDensity ), scanlineSpeed );

renderPipeline.outputNode = retroPipeline;

Expand All @@ -278,6 +356,8 @@

const gui = renderer.inspector.createParameters( 'Settings' );

gui.add( { model: 'Coffee Mug' }, 'model', Object.keys( models ) ).name( 'Model' ).onChange( loadModel );

gui.add( { enabled: true }, 'enabled' ).onChange( v => {

renderPipeline.outputNode = v ? retroPipeline : defaultPass;
Expand All @@ -288,11 +368,16 @@
gui.add( curvature, 'value', 0, 0.2, 0.01 ).name( 'Curvature' );
gui.add( colorDepthSteps, 'value', 4, 32, 1 ).name( 'Color Depth' );
gui.add( scanlineIntensity, 'value', 0, 1, 0.01 ).name( 'Scanlines' );
gui.add( scanlineCount, 'value', 20, 480, 1 ).name( 'Scanline Count' );
gui.add( scanlineDensity, 'value', 0.02, 1, 0.01 ).name( 'Scanline Density' );
gui.add( scanlineSpeed, 'value', 0, .1, 0.01 ).name( 'Scanline Speed' );
gui.add( vignetteIntensity, 'value', 0, 1, 0.01 ).name( 'Vignette' );
gui.add( bleeding, 'value', 0, 0.005, 0.001 ).name( 'Color Bleeding' );
gui.add( affineDistortion, 'value', 0, 1 ).name( 'Affine Distortion' );
gui.add( retro, 'filterTextures' ).name( 'Filter Textures' ).onChange( () => {

retro.dispose();

} );

window.addEventListener( 'resize', onWindowResize );

Expand Down
4 changes: 2 additions & 2 deletions examples/webgpu_volume_lighting.html
Original file line number Diff line number Diff line change
Expand Up @@ -239,12 +239,12 @@
const gui = renderer.inspector.createParameters( 'Volumetric Lighting' );

const rayMarching = gui.addFolder( 'Ray Marching' );
rayMarching.add( params, 'resolution', .1, .5 ).onChange( ( resolution ) => {
rayMarching.add( params, 'resolution', .1, 1 ).onChange( ( resolution ) => {

volumetricPass.setResolutionScale( resolution );

} );
rayMarching.add( volumetricMaterial, 'steps', 2, 12 ).name( 'step count' );
rayMarching.add( volumetricMaterial, 'steps', 2, 16 ).name( 'step count' );
rayMarching.add( denoiseStrength, 'value', 0, 1 ).name( 'denoise strength' );
rayMarching.add( params, 'denoise' ).onChange( ( denoise ) => {

Expand Down
4 changes: 2 additions & 2 deletions examples/webgpu_volume_lighting_rectarea.html
Original file line number Diff line number Diff line change
Expand Up @@ -261,12 +261,12 @@
const gui = renderer.inspector.createParameters( 'Volumetric Lighting' );

const rayMarching = gui.addFolder( 'Ray Marching' );
rayMarching.add( params, 'resolution', .1, .5 ).onChange( ( resolution ) => {
rayMarching.add( params, 'resolution', .1, 1 ).onChange( ( resolution ) => {

volumetricPass.setResolutionScale( resolution );

} );
rayMarching.add( volumetricMaterial, 'steps', 2, 12 ).name( 'step count' );
rayMarching.add( volumetricMaterial, 'steps', 2, 16 ).name( 'step count' );
rayMarching.add( denoiseStrength, 'value', 0, 1 ).name( 'denoise strength' );
rayMarching.add( params, 'denoise' ).onChange( ( denoise ) => {

Expand Down
Loading
Loading