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
41 changes: 40 additions & 1 deletion examples/jsm/tsl/display/GTAONode.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { reference, logarithmicDepthToViewZ, viewZToPerspectiveDepth, getNormalF
const _quadMesh = /*@__PURE__*/ new QuadMesh();
const _size = /*@__PURE__*/ new Vector2();

// From Activision GTAO paper: https://www.activision.com/cdn/research/s2016_pbs_activision_occlusion.pptx
const _temporalRotations = [ 60, 300, 180, 240, 120, 0 ];

let _rendererState;

/**
Expand Down Expand Up @@ -150,6 +153,20 @@ class GTAONode extends TempNode {
*/
this.samples = uniform( 16 );

/**
* Whether to use temporal filtering or not. Setting this property to
* `true` requires the usage of `TRAANode`. This will help to reduce noise
* although it introduces typical TAA artifacts like ghosting and temporal
* instabilities.
*
* If setting this property to `false`, a manual denoise via `DenoiseNode`
* might be required.
*
* @type {boolean}
* @default false
*/
this.useTemporalFiltering = false;

/**
* The node represents the internal noise texture used by the AO.
*
Expand Down Expand Up @@ -190,6 +207,13 @@ class GTAONode extends TempNode {
*/
this._cameraFar = reference( 'far', 'float', camera );

/**
* Temporal direction that influences the rotation angle for each slice.
*
* @type {UniformNode<float>}
*/
this._temporalDirection = uniform( 0 );

/**
* The material that is used to render the effect.
*
Expand Down Expand Up @@ -247,6 +271,20 @@ class GTAONode extends TempNode {

_rendererState = RendererUtils.resetRendererState( renderer, _rendererState );

// update temporal uniforms

if ( this.useTemporalFiltering === true ) {

const frameId = frame.frameId;

this._temporalDirection.value = _temporalRotations[ frameId % 6 ] / 360;

} else {

this._temporalDirection.value = 0;

}

//

const size = renderer.getDrawingBufferSize( _size );
Expand Down Expand Up @@ -313,6 +351,7 @@ class GTAONode extends TempNode {
const noiseResolution = textureSize( this._noiseNode, 0 );
let noiseUv = vec2( uvNode.x, uvNode.y.oneMinus() );
noiseUv = noiseUv.mul( this.resolution.div( noiseResolution ) );

const noiseTexel = sampleNoise( noiseUv );
const randomVec = noiseTexel.xyz.mul( 2.0 ).sub( 1.0 );
const tangent = vec3( randomVec.xy, 0.0 ).normalize();
Expand All @@ -328,7 +367,7 @@ class GTAONode extends TempNode {

Loop( { start: int( 0 ), end: DIRECTIONS, type: 'int', condition: '<' }, ( { i } ) => {

const angle = float( i ).div( float( DIRECTIONS ) ).mul( PI ).toVar();
const angle = float( i ).div( float( DIRECTIONS ) ).mul( PI ).add( this._temporalDirection ).toVar();
const sampleDir = vec4( cos( angle ), sin( angle ), 0., add( 0.5, mul( 0.5, noiseTexel.w ) ) );
sampleDir.xyz = normalize( kernelMatrix.mul( sampleDir.xyz ) );

Expand Down
2 changes: 2 additions & 0 deletions examples/webgpu_postprocessing_ao.html
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@

aoPass = ao( scenePassDepth, scenePassNormal, camera ).toInspector( 'AO' );
aoPass.resolutionScale = 0.5; // running AO in half resolution is often sufficient
aoPass.useTemporalFiltering = true;
blendPassAO = vec4( scenePassColor.rgb.mul( aoPass.r ), scenePassColor.a ); // the AO is stored only in the red channel

// traa
Expand Down Expand Up @@ -167,6 +168,7 @@
gui.add( params, 'radius', 0.1, 1 ).onChange( updateParameters );
gui.add( params, 'scale', 0.01, 2 ).onChange( updateParameters );
gui.add( params, 'thickness', 0.01, 2 ).onChange( updateParameters );
gui.add( aoPass, 'useTemporalFiltering' ).name( 'temporal filtering' );
gui.add( params, 'aoOnly' ).onChange( ( value ) => {

if ( value === true ) {
Expand Down