|
@@ -1,5 +1,5 @@
|
|
|
import { RenderTarget, Vector2, TempNode, QuadMesh, NodeMaterial, RendererUtils, MathUtils } from 'three/webgpu';
|
|
import { RenderTarget, Vector2, TempNode, QuadMesh, NodeMaterial, RendererUtils, MathUtils } from 'three/webgpu';
|
|
|
-import { clamp, normalize, reference, nodeObject, Fn, NodeUpdateType, uniform, vec4, passTexture, uv, logarithmicDepthToViewZ, viewZToPerspectiveDepth, getViewPosition, screenCoordinate, float, sub, fract, dot, vec2, rand, vec3, Loop, mul, PI, cos, sin, uint, cross, acos, sign, pow, luminance, If, max, abs, Break, sqrt, HALF_PI, PI2, div, ceil, shiftRight, uvec2, convertToTexture, bool, getNormalFromDepth } from 'three/tsl';
|
|
|
|
|
|
|
+import { clamp, normalize, reference, nodeObject, Fn, NodeUpdateType, uniform, vec4, passTexture, uv, logarithmicDepthToViewZ, viewZToPerspectiveDepth, getViewPosition, screenCoordinate, float, sub, fract, dot, vec2, rand, vec3, Loop, mul, PI, cos, sin, uint, cross, acos, sign, pow, luminance, If, max, abs, Break, sqrt, HALF_PI, div, ceil, shiftRight, convertToTexture, bool, getNormalFromDepth } from 'three/tsl';
|
|
|
|
|
|
|
|
const _quadMesh = /*@__PURE__*/ new QuadMesh();
|
|
const _quadMesh = /*@__PURE__*/ new QuadMesh();
|
|
|
const _size = /*@__PURE__*/ new Vector2();
|
|
const _size = /*@__PURE__*/ new Vector2();
|
|
@@ -377,6 +377,7 @@ class SSGINode extends TempNode {
|
|
|
|
|
|
|
|
const uvNode = uv();
|
|
const uvNode = uv();
|
|
|
const MAX_RAY = uint( 32 );
|
|
const MAX_RAY = uint( 32 );
|
|
|
|
|
+ const globalOccludedBitfield = uint( 0 );
|
|
|
|
|
|
|
|
const sampleDepth = ( uv ) => {
|
|
const sampleDepth = ( uv ) => {
|
|
|
|
|
|
|
@@ -403,6 +404,12 @@ class SSGINode extends TempNode {
|
|
|
|
|
|
|
|
return float( 0.25 ).mul( sub( position.y, position.x ).bitAnd( 3 ) );
|
|
return float( 0.25 ).mul( sub( position.y, position.x ).bitAnd( 3 ) );
|
|
|
|
|
|
|
|
|
|
+ } ).setLayout( {
|
|
|
|
|
+ name: 'spatialOffsets',
|
|
|
|
|
+ type: 'float',
|
|
|
|
|
+ inputs: [
|
|
|
|
|
+ { name: 'position', type: 'vec2' }
|
|
|
|
|
+ ]
|
|
|
} );
|
|
} );
|
|
|
|
|
|
|
|
// Interleaved gradient function from Jimenez 2014 http://goo.gl/eomGso
|
|
// Interleaved gradient function from Jimenez 2014 http://goo.gl/eomGso
|
|
@@ -411,6 +418,12 @@ class SSGINode extends TempNode {
|
|
|
|
|
|
|
|
return fract( float( 52.9829189 ).mul( fract( dot( position, vec2( 0.06711056, 0.00583715 ) ) ) ) );
|
|
return fract( float( 52.9829189 ).mul( fract( dot( position, vec2( 0.06711056, 0.00583715 ) ) ) ) );
|
|
|
|
|
|
|
|
|
|
+ } ).setLayout( {
|
|
|
|
|
+ name: 'gradientNoise',
|
|
|
|
|
+ type: 'float',
|
|
|
|
|
+ inputs: [
|
|
|
|
|
+ { name: 'position', type: 'vec2' }
|
|
|
|
|
+ ]
|
|
|
} );
|
|
} );
|
|
|
|
|
|
|
|
const GTAOFastAcos = Fn( ( [ value ] ) => {
|
|
const GTAOFastAcos = Fn( ( [ value ] ) => {
|
|
@@ -423,35 +436,31 @@ class SSGINode extends TempNode {
|
|
|
|
|
|
|
|
return vec2( x, y );
|
|
return vec2( x, y );
|
|
|
|
|
|
|
|
|
|
+ } ).setLayout( {
|
|
|
|
|
+ name: 'GTAOFastAcos',
|
|
|
|
|
+ type: 'vec2',
|
|
|
|
|
+ inputs: [
|
|
|
|
|
+ { name: 'value', type: 'vec2' }
|
|
|
|
|
+ ]
|
|
|
} );
|
|
} );
|
|
|
|
|
|
|
|
- const bitCount = Fn( ( [ value_immutable ] ) => {
|
|
|
|
|
|
|
+ const bitCount = Fn( ( [ value ] ) => {
|
|
|
|
|
|
|
|
- const value = value_immutable;
|
|
|
|
|
- value.assign( value.sub( value.shiftRight( uint( 1 ) ).bitAnd( uint( 0x55555555 ) ) ) );
|
|
|
|
|
- value.assign( value.bitAnd( uint( 0x33333333 ) ).add( value.shiftRight( uint( 2 ) ).bitAnd( uint( 0x33333333 ) ) ) );
|
|
|
|
|
|
|
+ const v = uint( value );
|
|
|
|
|
+ v.assign( v.sub( v.shiftRight( uint( 1 ) ).bitAnd( uint( 0x55555555 ) ) ) );
|
|
|
|
|
+ v.assign( v.bitAnd( uint( 0x33333333 ) ).add( v.shiftRight( uint( 2 ) ).bitAnd( uint( 0x33333333 ) ) ) );
|
|
|
|
|
|
|
|
- return value.add( value.shiftRight( uint( 4 ) ) ).bitAnd( uint( 0xF0F0F0F ) ).mul( uint( 0x1010101 ) ).shiftRight( uint( 24 ) );
|
|
|
|
|
|
|
+ return v.add( v.shiftRight( uint( 4 ) ) ).bitAnd( uint( 0xF0F0F0F ) ).mul( uint( 0x1010101 ) ).shiftRight( uint( 24 ) );
|
|
|
|
|
|
|
|
|
|
+ } ).setLayout( {
|
|
|
|
|
+ name: 'bitCount',
|
|
|
|
|
+ type: 'uint',
|
|
|
|
|
+ inputs: [
|
|
|
|
|
+ { name: 'value', type: 'uint' }
|
|
|
|
|
+ ]
|
|
|
} );
|
|
} );
|
|
|
|
|
|
|
|
- const computeOccludedBitfield = Fn( ( [ minHorizon, maxHorizon, globalOccludedBitfield ] ) => {
|
|
|
|
|
-
|
|
|
|
|
- const startHorizonInt = uint( minHorizon.mul( float( MAX_RAY ) ) ).toConst();
|
|
|
|
|
- const angleHorizonInt = uint( ceil( maxHorizon.sub( minHorizon ).mul( float( MAX_RAY ) ) ) ).toConst();
|
|
|
|
|
- const angleHorizonBitfield = angleHorizonInt.greaterThan( uint( 0 ) ).select( uint( shiftRight( uint( 0xFFFFFFFF ), uint( 32 ).sub( MAX_RAY ).add( MAX_RAY.sub( angleHorizonInt ) ) ) ), uint( 0 ) ).toConst();
|
|
|
|
|
- let currentOccludedBitfield = angleHorizonBitfield.shiftLeft( startHorizonInt );
|
|
|
|
|
- currentOccludedBitfield = currentOccludedBitfield.bitAnd( globalOccludedBitfield.bitNot() );
|
|
|
|
|
-
|
|
|
|
|
- const result = uvec2();
|
|
|
|
|
- result.x = globalOccludedBitfield.bitOr( currentOccludedBitfield );
|
|
|
|
|
- result.y = bitCount( currentOccludedBitfield );
|
|
|
|
|
-
|
|
|
|
|
- return result;
|
|
|
|
|
-
|
|
|
|
|
- } );
|
|
|
|
|
-
|
|
|
|
|
- const horizonSampling = Fn( ( [ directionIsRight, RADIUS, viewPosition, slideDirTexelSize, initialRayStep, uvNode, viewDir, viewNormal, n, globalOccludedBitfield ] ) => {
|
|
|
|
|
|
|
+ const horizonSampling = Fn( ( [ directionIsRight, RADIUS, viewPosition, slideDirTexelSize, initialRayStep, uvNode, viewDir, viewNormal, n ] ) => {
|
|
|
|
|
|
|
|
const STEP_COUNT = this.stepCount.toConst();
|
|
const STEP_COUNT = this.stepCount.toConst();
|
|
|
const EXP_FACTOR = this.expFactor.toConst();
|
|
const EXP_FACTOR = this.expFactor.toConst();
|
|
@@ -476,7 +485,6 @@ class SSGINode extends TempNode {
|
|
|
const samplingDirection = directionIsRight.equal( true ).select( 1, - 1 );
|
|
const samplingDirection = directionIsRight.equal( true ).select( 1, - 1 );
|
|
|
|
|
|
|
|
const color = vec3( 0 );
|
|
const color = vec3( 0 );
|
|
|
- const occludedBitfield = uint( globalOccludedBitfield ).toVar();
|
|
|
|
|
|
|
|
|
|
const lastSampleViewPosition = vec3( viewPosition ).toVar();
|
|
const lastSampleViewPosition = vec3( viewPosition ).toVar();
|
|
|
|
|
|
|
@@ -502,9 +510,21 @@ class SSGINode extends TempNode {
|
|
|
frontBackHorizon = clamp( div( mul( samplingDirection, frontBackHorizon.negate() ).sub( n.sub( HALF_PI ) ), PI ) ); // Port note: subtract half pi instead of adding it
|
|
frontBackHorizon = clamp( div( mul( samplingDirection, frontBackHorizon.negate() ).sub( n.sub( HALF_PI ) ), PI ) ); // Port note: subtract half pi instead of adding it
|
|
|
frontBackHorizon = directionIsRight.equal( true ).select( frontBackHorizon.yx, frontBackHorizon.xy ); // Front/Back get inverted depending on angle
|
|
frontBackHorizon = directionIsRight.equal( true ).select( frontBackHorizon.yx, frontBackHorizon.xy ); // Front/Back get inverted depending on angle
|
|
|
|
|
|
|
|
- const result = computeOccludedBitfield( frontBackHorizon.x, frontBackHorizon.y, occludedBitfield );
|
|
|
|
|
- occludedBitfield.assign( result.x );
|
|
|
|
|
- const numOccludedZones = result.y;
|
|
|
|
|
|
|
+ // inline ComputeOccludedBitfield() for easier debugging
|
|
|
|
|
+
|
|
|
|
|
+ const minHorizon = frontBackHorizon.x.toConst();
|
|
|
|
|
+ const maxHorizon = frontBackHorizon.y.toConst();
|
|
|
|
|
+
|
|
|
|
|
+ const startHorizonInt = uint( frontBackHorizon.mul( float( MAX_RAY ) ) ).toConst();
|
|
|
|
|
+ const angleHorizonInt = uint( ceil( maxHorizon.sub( minHorizon ).mul( float( MAX_RAY ) ) ) ).toConst();
|
|
|
|
|
+ const angleHorizonBitfield = angleHorizonInt.greaterThan( uint( 0 ) ).select( uint( shiftRight( uint( 0xFFFFFFFF ), uint( 32 ).sub( MAX_RAY ).add( MAX_RAY.sub( angleHorizonInt ) ) ) ), uint( 0 ) ).toConst();
|
|
|
|
|
+ let currentOccludedBitfield = angleHorizonBitfield.shiftLeft( startHorizonInt );
|
|
|
|
|
+ currentOccludedBitfield = currentOccludedBitfield.bitAnd( globalOccludedBitfield.bitNot() );
|
|
|
|
|
+
|
|
|
|
|
+ globalOccludedBitfield.assign( globalOccludedBitfield.bitOr( currentOccludedBitfield ) );
|
|
|
|
|
+ const numOccludedZones = bitCount( currentOccludedBitfield );
|
|
|
|
|
+
|
|
|
|
|
+ //
|
|
|
|
|
|
|
|
If( numOccludedZones.greaterThan( 0 ), () => { // If a ray hit the sample, that sample is visible from shading point
|
|
If( numOccludedZones.greaterThan( 0 ), () => { // If a ray hit the sample, that sample is visible from shading point
|
|
|
|
|
|
|
@@ -538,7 +558,7 @@ class SSGINode extends TempNode {
|
|
|
|
|
|
|
|
} );
|
|
} );
|
|
|
|
|
|
|
|
- return vec4( color, occludedBitfield );
|
|
|
|
|
|
|
+ return vec3( color );
|
|
|
|
|
|
|
|
} );
|
|
} );
|
|
|
|
|
|
|
@@ -568,7 +588,7 @@ class SSGINode extends TempNode {
|
|
|
|
|
|
|
|
Loop( { start: uint( 0 ), end: ROTATION_COUNT, type: 'uint', condition: '<' }, ( { i } ) => {
|
|
Loop( { start: uint( 0 ), end: ROTATION_COUNT, type: 'uint', condition: '<' }, ( { i } ) => {
|
|
|
|
|
|
|
|
- const rotationAngle = mul( float( i ).add( noiseDirection ).add( this._temporalDirection ), PI2.div( float( ROTATION_COUNT ) ) ).toConst(); // Port note: PI was changed to PI2 to fix a sampling asymmetry issue
|
|
|
|
|
|
|
+ const rotationAngle = mul( float( i ).add( noiseDirection ).add( this._temporalDirection ), PI.div( float( ROTATION_COUNT ) ) ).toConst();
|
|
|
const sliceDir = vec3( vec2( cos( rotationAngle ), sin( rotationAngle ) ), 0 ).toConst();
|
|
const sliceDir = vec3( vec2( cos( rotationAngle ), sin( rotationAngle ) ), 0 ).toConst();
|
|
|
const slideDirTexelSize = sliceDir.xy.mul( float( 1 ).div( this._resolution ) ).toConst();
|
|
const slideDirTexelSize = sliceDir.xy.mul( float( 1 ).div( this._resolution ) ).toConst();
|
|
|
|
|
|
|
@@ -580,15 +600,10 @@ class SSGINode extends TempNode {
|
|
|
const cos_n = clamp( dot( projectedNormalNormalized, viewDir ), - 1, 1 ).toConst();
|
|
const cos_n = clamp( dot( projectedNormalNormalized, viewDir ), - 1, 1 ).toConst();
|
|
|
const n = sign( dot( projectedNormal, tangent ) ).negate().mul( acos( cos_n ) ).toConst();
|
|
const n = sign( dot( projectedNormal, tangent ) ).negate().mul( acos( cos_n ) ).toConst();
|
|
|
|
|
|
|
|
- const globalOccludedBitfield = uint( 0 );
|
|
|
|
|
-
|
|
|
|
|
- const resultRight = horizonSampling( bool( true ), RADIUS, viewPosition, slideDirTexelSize, initialRayStep, uvNode, viewDir, viewNormal, n, globalOccludedBitfield );
|
|
|
|
|
- color.addAssign( resultRight.xyz );
|
|
|
|
|
- globalOccludedBitfield.assign( resultRight.a );
|
|
|
|
|
|
|
+ globalOccludedBitfield.assign( 0 );
|
|
|
|
|
|
|
|
- const resultLeft = horizonSampling( bool( false ), RADIUS, viewPosition, slideDirTexelSize, initialRayStep, uvNode, viewDir, viewNormal, n, globalOccludedBitfield );
|
|
|
|
|
- color.addAssign( resultLeft.xyz );
|
|
|
|
|
- globalOccludedBitfield.assign( resultLeft.a );
|
|
|
|
|
|
|
+ color.addAssign( horizonSampling( bool( true ), RADIUS, viewPosition, slideDirTexelSize, initialRayStep, uvNode, viewDir, viewNormal, n ) );
|
|
|
|
|
+ color.addAssign( horizonSampling( bool( false ), RADIUS, viewPosition, slideDirTexelSize, initialRayStep, uvNode, viewDir, viewNormal, n ) );
|
|
|
|
|
|
|
|
ao.addAssign( float( bitCount( globalOccludedBitfield ) ).div( float( MAX_RAY ) ) );
|
|
ao.addAssign( float( bitCount( globalOccludedBitfield ) ).div( float( MAX_RAY ) ) );
|
|
|
|
|
|