Skip to content

Commit

Permalink
revisions
Browse files Browse the repository at this point in the history
  • Loading branch information
sunag committed Nov 20, 2024
1 parent 2277aa0 commit 80271a9
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 27 deletions.
86 changes: 65 additions & 21 deletions src/nodes/lighting/PointShadowNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,55 @@ import { sub, div, mul } from '../math/OperatorNode.js';
import { renderGroup } from '../core/UniformGroupNode.js';
import { Vector2 } from '../../math/Vector2.js';
import { Vector4 } from '../../math/Vector4.js';
import { glslFn } from '../code/FunctionNode.js';

Check warning on line 11 in src/nodes/lighting/PointShadowNode.js

View workflow job for this annotation

GitHub Actions / Lint testing

'glslFn' is defined but never used

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused import glslFn.
import { perspectiveDepthToViewZ } from '../TSL.js';

Check warning on line 12 in src/nodes/lighting/PointShadowNode.js

View workflow job for this annotation

GitHub Actions / Lint testing

'perspectiveDepthToViewZ' is defined but never used

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused import perspectiveDepthToViewZ.

export const cubeToUV = /*@__PURE__*/ Fn( ( [ v_immutable, texelSizeY_immutable ] ) => {
// cubeToUV() maps a 3D direction vector suitable for cube texture mapping to a 2D
// vector suitable for 2D texture mapping. This code uses the following layout for the
// 2D texture:
//
// xzXZ
// y Y
//
// Y - Positive y direction
// y - Negative y direction
// X - Positive x direction
// x - Negative x direction
// Z - Positive z direction
// z - Negative z direction
//
// Source and test bed:
// https://gist.github.com/tschw/da10c43c467ce8afd0c4

export const cubeToUV = /*@__PURE__*/ Fn( ( [ pos, texelSizeY ] ) => {

const v = pos.toVar();

// Number of texels to avoid at the edge of each square

const absV = abs( v );

// Intersect unit cube

const texelSizeY = float( texelSizeY_immutable ).toVar();
const v = vec3( v_immutable ).toVar();
const absV = vec3( abs( v ) ).toVar();
const scaleToCube = float( div( 1.0, max( absV.x, max( absV.y, absV.z ) ) ) ).toVar();
const scaleToCube = div( 1.0, max( absV.x, max( absV.y, absV.z ) ) );
absV.mulAssign( scaleToCube );
v.mulAssign( scaleToCube.mul( sub( 1.0, mul( 2.0, texelSizeY ) ) ) );

// Apply scale to avoid seams

// two texels less per square (one texel will do for NEAREST)
v.mulAssign( scaleToCube.mul( texelSizeY.mul( 2 ).oneMinus() ) );

// Unwrap

// space: -1 ... 1 range for each square
//
// #X## dim := ( 4 , 2 )
// # # center := ( 1 , 1 )

const planar = vec2( v.xy ).toVar();
const almostATexel = float( mul( 1.5, texelSizeY ) ).toVar();
const almostOne = float( sub( 1.0, almostATexel ) ).toVar();

const almostATexel = texelSizeY.mul( 1.5 );
const almostOne = almostATexel.oneMinus();

If( absV.z.greaterThanEqual( almostOne ), () => {

Expand All @@ -31,17 +68,21 @@ export const cubeToUV = /*@__PURE__*/ Fn( ( [ v_immutable, texelSizeY_immutable

} ).ElseIf( absV.x.greaterThanEqual( almostOne ), () => {

const signX = float( sign( v.x ) ).toVar();
planar.x.assign( v.z.mul( signX ).add( mul( 2.0, signX ) ) );
const signX = sign( v.x );
planar.x.assign( v.z.mul( signX ).add( signX.mul( 2.0 ) ) );

} ).ElseIf( absV.y.greaterThanEqual( almostOne ), () => {

const signY = float( sign( v.y ) ).toVar();
planar.x.assign( v.x.add( mul( 2.0, signY ) ).add( 2.0 ) );
const signY = sign( v.y );
planar.x.assign( v.x.add( signY.mul( 2.0 ) ).add( 2.0 ) );
planar.y.assign( v.z.mul( signY ).sub( 2.0 ) );

} );

// Transform to UV space

// scale := 0.5 / dim
// translate := ( center + 0.5 ) / dim
return vec2( 0.125, 0.25 ).mul( planar ).add( vec2( 0.375, 0.75 ) );

} ).setLayout( {
Expand All @@ -55,6 +96,8 @@ export const cubeToUV = /*@__PURE__*/ Fn( ( [ v_immutable, texelSizeY_immutable

const BasicShadowMap = Fn( ( { depthTexture, shadowCoord, shadow } ) => {

// for point lights, the uniform @vShadowCoord is re-purposed to hold
// the vector from the light to the world-space position of the fragment.
const lightToPosition = shadowCoord.xyz.toVar();
const lightToPositionLength = lightToPosition.length();

Expand All @@ -65,23 +108,24 @@ const BasicShadowMap = Fn( ( { depthTexture, shadowCoord, shadow } ) => {

const result = float( 1.0 ).toVar();

If( lightToPositionLength.sub( cameraFarLocal ).lessThanEqual( 0.0 ).and( lightToPositionLength.sub( cameraNearLocal ) ), () => {
If( lightToPositionLength.sub( cameraFarLocal ).lessThanEqual( 0.0 ).and( lightToPositionLength.sub( cameraNearLocal ).greaterThanEqual( 0.0 ) ), () => {

const dp = lightToPositionLength.sub( cameraNearLocal ).div( cameraFarLocal.sub( cameraNearLocal ) ).toVar();
// dp = normalized distance from light to fragment position
const dp = lightToPositionLength.sub( cameraNearLocal ).div( cameraFarLocal.sub( cameraNearLocal ) ).toVar(); // need to clamp?
dp.addAssign( bias );

// bd3D = base direction 3D
const bd3D = lightToPosition.normalize();
const texelSize = vec2( 1.0 ).div( mapSize.mul( vec2( 4.0, 2.0 ) ) );

const uv = cubeToUV( bd3D, texelSize.y ).flipY();

const dpRemap = dp.add( 1 ).div( 2 ); // unchecked
const uv = cubeToUV( bd3D, texelSize.y );

result.assign( texture( depthTexture, uv ).compare( dpRemap ).select( 1, 0 ) );
// no percentage-closer filtering
result.assign( texture( depthTexture, uv.flipY() ).compare( dp ).select( 1, 0 ) );

} );

return mix( 1.0, result, 1 );
return result;

} );

Expand Down Expand Up @@ -111,9 +155,9 @@ class PointShadowNode extends ShadowNode {

}

setupShadowFilter( builder, { filterFn, depthTexture, shadowCoord, shadow } ) {
setupShadowFilter( builder, { filterFn, shadowTexture, depthTexture, shadowCoord, shadow } ) {

Check warning on line 158 in src/nodes/lighting/PointShadowNode.js

View workflow job for this annotation

GitHub Actions / Lint testing

'filterFn' is defined but never used

return BasicShadowMap( { depthTexture, shadowCoord, shadow } );
return BasicShadowMap( { shadowTexture, depthTexture, shadowCoord, shadow } );

}

Expand Down
23 changes: 17 additions & 6 deletions src/nodes/lighting/ShadowNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { uniform } from '../core/UniformNode.js';
import { float, vec2, vec3, vec4, If, int, Fn, nodeObject } from '../tsl/TSLBase.js';
import { reference } from '../accessors/ReferenceNode.js';
import { texture } from '../accessors/TextureNode.js';
import { positionWorld } from '../accessors/Position.js';
import { positionView, positionWorld } from '../accessors/Position.js';

Check warning on line 7 in src/nodes/lighting/ShadowNode.js

View workflow job for this annotation

GitHub Actions / Lint testing

'positionView' is defined but never used

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused import positionView.
import { transformedNormalWorld } from '../accessors/Normal.js';
import { mix, fract, step, max, clamp, sqrt } from '../math/MathNode.js';
import { add, sub } from '../math/OperatorNode.js';
Expand All @@ -15,7 +15,8 @@ import { Loop } from '../utils/LoopNode.js';
import { screenCoordinate } from '../display/ScreenNode.js';
import { HalfFloatType, LessCompare, RGFormat, VSMShadowMap, WebGPUCoordinateSystem } from '../../constants.js';
import { renderGroup } from '../core/UniformGroupNode.js';
import { viewZToLogarithmicDepth } from '../display/ViewportDepthNode.js';
import { depth, linearDepth, viewZToLogarithmicDepth, viewZToPerspectiveDepth } from '../display/ViewportDepthNode.js';

Check warning on line 18 in src/nodes/lighting/ShadowNode.js

View workflow job for this annotation

GitHub Actions / Lint testing

'depth' is defined but never used

Check warning on line 18 in src/nodes/lighting/ShadowNode.js

View workflow job for this annotation

GitHub Actions / Lint testing

'linearDepth' is defined but never used

Check warning on line 18 in src/nodes/lighting/ShadowNode.js

View workflow job for this annotation

GitHub Actions / Lint testing

'viewZToPerspectiveDepth' is defined but never used

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused imports depth, linearDepth, viewZToPerspectiveDepth.
import { modelViewProjection, objectPosition } from '../TSL.js';

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused import modelViewProjection.

const shadowWorldPosition = vec3().toVar( 'shadowWorldPosition' );

Expand Down Expand Up @@ -298,18 +299,28 @@ class ShadowNode extends Node {

const { renderer } = builder;

const shadow = this.shadow;
const shadowMapType = renderer.shadowMap.type;

if ( _overrideMaterial === null ) {

const nearDistance = reference( 'near', 'float', shadow.camera ).setGroup( renderGroup );
const farDistance = reference( 'far', 'float', shadow.camera ).setGroup( renderGroup );

const referencePosition = objectPosition( shadow.camera );

let dist = positionWorld.sub( referencePosition ).length();
dist = dist.sub( nearDistance ).div( farDistance.sub( nearDistance ) );
dist = dist.saturate(); // clamp to [ 0, 1 ]

_overrideMaterial = new NodeMaterial();
_overrideMaterial.fragmentNode = vec4( 0, 0, 0, 1 );
_overrideMaterial.depthNode = dist;
_overrideMaterial.isShadowNodeMaterial = true; // Use to avoid other overrideMaterial override material.fragmentNode unintentionally when using material.shadowNode
_overrideMaterial.name = 'ShadowMaterial';

}

const shadow = this.shadow;
const shadowMapType = renderer.shadowMap.type;

const depthTexture = new DepthTexture( shadow.mapSize.width, shadow.mapSize.height );
depthTexture.compareFunction = LessCompare;

Expand Down Expand Up @@ -364,7 +375,7 @@ class ShadowNode extends Node {

const shadowDepthTexture = ( shadowMapType === VSMShadowMap ) ? this.vsmShadowMapHorizontal.texture : depthTexture;

const shadowNode = this.setupShadowFilter( builder, { filterFn, depthTexture: shadowDepthTexture, shadowCoord, shadow } );
const shadowNode = this.setupShadowFilter( builder, { filterFn, shadowTexture: shadowMap.texture, depthTexture: shadowDepthTexture, shadowCoord, shadow } );

const shadowColor = texture( shadowMap.texture, shadowCoord );
const shadowOutput = mix( 1, shadowNode.rgb.mix( shadowColor, 1 ), shadowIntensity.mul( shadowColor.a ) ).toVar();
Expand Down

0 comments on commit 80271a9

Please sign in to comment.