Skip to content

Commit

Permalink
WebGPURenderer: Add PCFShadowMap support.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mugen87 committed Jul 20, 2024
1 parent 216398f commit 3e15548
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 99 deletions.
12 changes: 7 additions & 5 deletions examples/webgpu_postprocessing_pixel.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

import { uniform, pixelationPass } from 'three/tsl';
import { uniform, pixelationPass, pass } from 'three/tsl';

let camera, scene, renderer, postProcessing, crystalMesh, clock;
let gui, effectController;
Expand Down Expand Up @@ -69,7 +69,7 @@

const mesh = new THREE.Mesh( new THREE.BoxGeometry( boxSideLength, boxSideLength, boxSideLength ), boxMaterial );
mesh.castShadow = true;
//mesh.receiveShadow = true;
mesh.receiveShadow = true;
mesh.rotation.y = rotation;
mesh.position.y = boxSideLength / 2;
mesh.position.set( x, boxSideLength / 2 + .0001, z );
Expand Down Expand Up @@ -101,7 +101,7 @@
specular: 0xffffff
} )
);
//crystalMesh.receiveShadow = true;
crystalMesh.receiveShadow = true;
crystalMesh.castShadow = true;
scene.add( crystalMesh );

Expand All @@ -113,6 +113,7 @@
directionalLight.position.set( 100, 100, 100 );
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.set( 2048, 2048 );
directionalLight.shadow.bias = - 0.0001;
scene.add( directionalLight );

const spotLight = new THREE.SpotLight( 0xffc100, 10, 10, Math.PI / 16, .02, 2 );
Expand All @@ -121,11 +122,12 @@
scene.add( target );
target.position.set( 0, 0, 0 );
spotLight.castShadow = true;
spotLight.shadow.bias = - 0.001;
scene.add( spotLight );

renderer = new THREE.WebGPURenderer( { antialias: false } );
renderer = new THREE.WebGPURenderer();
renderer.shadowMap.enabled = true;
//renderer.setPixelRatio( window.devicePixelRatio );
renderer.shadowMap.type = THREE.BasicShadowMap;
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animate );
document.body.appendChild( renderer.domElement );
Expand Down
177 changes: 91 additions & 86 deletions src/nodes/lighting/AnalyticLightNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import LightingNode from './LightingNode.js';
import { NodeUpdateType } from '../core/constants.js';
import { uniform } from '../core/UniformNode.js';
import { addNodeClass } from '../core/Node.js';
import { vec3, vec4 } from '../shadernode/ShaderNode.js';
import { float, If, vec2, vec3, vec4 } from '../shadernode/ShaderNode.js';
import { reference } from '../accessors/ReferenceNode.js';
import { texture } from '../accessors/TextureNode.js';
import { positionWorld } from '../accessors/PositionNode.js';
import { normalWorld } from '../accessors/NormalNode.js';
import { mix } from '../math/MathNode.js';
//import { add } from '../math/OperatorNode.js';
import { add } from '../math/OperatorNode.js';
import { Color } from '../../math/Color.js';
import { DepthTexture } from '../../textures/DepthTexture.js';
import { NearestFilter, LessCompare, NoToneMapping, WebGPUCoordinateSystem } from '../../constants.js';
import { LessCompare, WebGPUCoordinateSystem, BasicShadowMap, PCFShadowMap, FloatType } from '../../constants.js';

Check warning on line 14 in src/nodes/lighting/AnalyticLightNode.js

View workflow job for this annotation

GitHub Actions / Lint testing

'FloatType' is defined but never used

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused import FloatType.

let overrideMaterial = null;

Expand All @@ -25,23 +25,21 @@ class AnalyticLightNode extends LightingNode {

this.light = light;

this.rtt = null;
this.shadowNode = null;
this.shadowMaskNode = null;

this.color = new Color();
this._defaultColorNode = uniform( this.color );
this._shadowColorNode = null;

this.colorNode = this._defaultColorNode;

this.shadowMap = null;
this.shadowNode = null;
this._shadowColorNode = null;

this.isAnalyticLightNode = true;

}

getCacheKey() {

return super.getCacheKey() + '-' + ( this.light.id + '-' + ( this.light.castShadow ? '1' : '0' ) );
return super.getCacheKey() + '-' + ( this.light.id + '-' + ( this.light.castShadow ? '1' : '0' ) + ( this.light.receiveShadow ? '1' : '0' ) );

}

Expand All @@ -53,7 +51,7 @@ class AnalyticLightNode extends LightingNode {

setupShadow( builder ) {

const { object } = builder;
const { object, renderer } = builder;

if ( object.receiveShadow === false ) {

Expand All @@ -75,17 +73,12 @@ class AnalyticLightNode extends LightingNode {

}

const shadow = this.light.shadow;
const rtt = builder.createRenderTarget( shadow.mapSize.width, shadow.mapSize.height );

const depthTexture = new DepthTexture();
depthTexture.minFilter = NearestFilter;
depthTexture.magFilter = NearestFilter;
depthTexture.image.width = shadow.mapSize.width;
depthTexture.image.height = shadow.mapSize.height;
depthTexture.compareFunction = LessCompare;

rtt.depthTexture = depthTexture;
const shadow = this.light.shadow;
const shadowMap = builder.createRenderTarget( shadow.mapSize.width, shadow.mapSize.height );
shadowMap.depthTexture = depthTexture;

shadow.camera.updateProjectionMatrix();

Expand All @@ -100,15 +93,9 @@ class AnalyticLightNode extends LightingNode {
let shadowCoord = uniform( shadow.matrix ).mul( position.add( normalWorld.mul( normalBias ) ) );
shadowCoord = shadowCoord.xyz.div( shadowCoord.w );

const frustumTest = shadowCoord.x.greaterThanEqual( 0 )
.and( shadowCoord.x.lessThanEqual( 1 ) )
.and( shadowCoord.y.greaterThanEqual( 0 ) )
.and( shadowCoord.y.lessThanEqual( 1 ) )
.and( shadowCoord.z.lessThanEqual( 1 ) );

let coordZ = shadowCoord.z.add( bias );

if ( builder.renderer.coordinateSystem === WebGPUCoordinateSystem ) {
if ( renderer.coordinateSystem === WebGPUCoordinateSystem ) {

coordZ = coordZ.mul( 2 ).sub( 1 ); // WebGPU: Convertion [ 0, 1 ] to [ - 1, 1 ]

Expand All @@ -120,59 +107,76 @@ class AnalyticLightNode extends LightingNode {
coordZ
);

const frustumTest = shadowCoord.x.greaterThanEqual( 0 )
.and( shadowCoord.x.lessThanEqual( 1 ) )
.and( shadowCoord.y.greaterThanEqual( 0 ) )
.and( shadowCoord.y.lessThanEqual( 1 ) )
.and( shadowCoord.z.lessThanEqual( 1 ) );

shadowNode = float( 1.0 ).toVar();

const textureCompare = ( depthTexture, shadowCoord, compare ) => texture( depthTexture, shadowCoord ).compare( compare );
//const textureCompare = ( depthTexture, shadowCoord, compare ) => compare.step( texture( depthTexture, shadowCoord ) );

// BasicShadowMap

shadowNode = textureCompare( depthTexture, shadowCoord.xy, shadowCoord.z );

// PCFShadowMap
/*
const mapSize = reference( 'mapSize', 'vec2', shadow );
const radius = reference( 'radius', 'float', shadow );
const texelSize = vec2( 1 ).div( mapSize );
const dx0 = texelSize.x.negate().mul( radius );
const dy0 = texelSize.y.negate().mul( radius );
const dx1 = texelSize.x.mul( radius );
const dy1 = texelSize.y.mul( radius );
const dx2 = dx0.mul( 2 );
const dy2 = dy0.mul( 2 );
const dx3 = dx1.mul( 2 );
const dy3 = dy1.mul( 2 );
shadowNode = add(
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx0, dy0 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( 0, dy0 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx1, dy0 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx2, dy2 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( 0, dy2 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx3, dy2 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx0, 0 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx2, 0 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy, shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx3, 0 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx1, 0 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx2, dy3 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( 0, dy3 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx3, dy3 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx0, dy1 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( 0, dy1 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx1, dy1 ) ), shadowCoord.z )
).mul( 1 / 17 );
*/
//

const shadowColor = texture( rtt.texture, shadowCoord );
const shadowMaskNode = frustumTest.mix( 1, shadowNode.mix( shadowColor.a.mix( 1, shadowColor ), 1 ) );
if ( renderer.shadowMap.type === BasicShadowMap ) {

If( frustumTest, () => {

shadowNode.assign( textureCompare( depthTexture, shadowCoord.xy, shadowCoord.z ) );

} );

} else if ( renderer.shadowMap.type === PCFShadowMap ) {

If( frustumTest, () => {

const mapSize = reference( 'mapSize', 'vec2', shadow );
const radius = reference( 'radius', 'float', shadow );

const texelSize = vec2( 1 ).div( mapSize );
const dx0 = texelSize.x.negate().mul( radius );
const dy0 = texelSize.y.negate().mul( radius );
const dx1 = texelSize.x.mul( radius );
const dy1 = texelSize.y.mul( radius );
const dx2 = dx0.div( 2 );
const dy2 = dy0.div( 2 );
const dx3 = dx1.div( 2 );
const dy3 = dy1.div( 2 );

shadowNode.assign( add(
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx0, dy0 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( 0, dy0 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx1, dy0 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx2, dy2 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( 0, dy2 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx3, dy2 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx0, 0 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx2, 0 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy, shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx3, 0 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx1, 0 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx2, dy3 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( 0, dy3 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx3, dy3 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx0, dy1 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( 0, dy1 ) ), shadowCoord.z ),
textureCompare( depthTexture, shadowCoord.xy.add( vec2( dx1, dy1 ) ), shadowCoord.z )
).mul( 1 / 17 ) );

} );

} else {

throw new Error( 'THREE.WebGPURendere: Shadow map type not supported yet.' );

this.rtt = rtt;
this.colorNode = this.colorNode.mul( mix( 1, shadowMaskNode, shadowIntensity ) );
}



this.shadowMap = shadowMap;
this.colorNode = this.colorNode.mul( mix( 1, shadowNode, shadowIntensity ) );
this._shadowColorNode = this.colorNode;

this.shadowNode = shadowNode;
this.shadowMaskNode = shadowMaskNode;

//

Expand All @@ -188,26 +192,32 @@ class AnalyticLightNode extends LightingNode {

setup( builder ) {

if ( this.light.castShadow ) this.setupShadow( builder );
else if ( this.shadowNode !== null ) this.disposeShadow();
if ( this.light.castShadow ) {

this.setupShadow( builder );

} else if ( this.shadowNode !== null ) {

this.disposeShadow();

}

}

updateShadow( frame ) {

const { rtt, light } = this;
const { shadowMap, light } = this;
const { renderer, scene, camera } = frame;

const currentOverrideMaterial = scene.overrideMaterial;

scene.overrideMaterial = overrideMaterial;

rtt.setSize( light.shadow.mapSize.width, light.shadow.mapSize.height );
shadowMap.setSize( light.shadow.mapSize.width, light.shadow.mapSize.height );

light.shadow.updateMatrices( light );
light.shadow.camera.layers.mask = camera.layers.mask;

const currentToneMapping = renderer.toneMapping;
const currentRenderTarget = renderer.getRenderTarget();
const currentRenderObjectFunction = renderer.getRenderObjectFunction();

Expand All @@ -221,28 +231,23 @@ class AnalyticLightNode extends LightingNode {

} );

renderer.setRenderTarget( rtt );
renderer.toneMapping = NoToneMapping;

renderer.setRenderTarget( shadowMap );
renderer.render( scene, light.shadow.camera );

renderer.setRenderTarget( currentRenderTarget );
renderer.setRenderObjectFunction( currentRenderObjectFunction );

renderer.toneMapping = currentToneMapping;

scene.overrideMaterial = currentOverrideMaterial;

}

disposeShadow() {

this.rtt.dispose();
this.shadowMap.dispose();
this.shadowMap = null;

this.shadowNode = null;
this.shadowMaskNode = null;
this._shadowColorNode = null;
this.rtt = null;

this.colorNode = this._defaultColorNode;

Expand Down
3 changes: 1 addition & 2 deletions src/nodes/lighting/DirectionalLightNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ class DirectionalLightNode extends AnalyticLightNode {
lightingModel.direct( {
lightDirection,
lightColor,
reflectedLight,
shadowMask: this.shadowMaskNode
reflectedLight
}, builder.stack, builder );

}
Expand Down
3 changes: 1 addition & 2 deletions src/nodes/lighting/PointLightNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ class PointLightNode extends AnalyticLightNode {
lightingModel.direct( {
lightDirection,
lightColor,
reflectedLight,
shadowMask: this.shadowMaskNode
reflectedLight
}, builder.stack, builder );

}
Expand Down
3 changes: 1 addition & 2 deletions src/nodes/lighting/SpotLightNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ class SpotLightNode extends AnalyticLightNode {
lightingModel.direct( {
lightDirection,
lightColor,
reflectedLight,
shadowMask: this.shadowMaskNode
reflectedLight
}, builder.stack, builder );

}
Expand Down
4 changes: 2 additions & 2 deletions src/renderers/common/Renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { Vector2 } from '../../math/Vector2.js';
import { Vector3 } from '../../math/Vector3.js';
import { Vector4 } from '../../math/Vector4.js';
import { RenderTarget } from '../../core/RenderTarget.js';
import { DoubleSide, BackSide, FrontSide, SRGBColorSpace, NoColorSpace, NoToneMapping, LinearFilter, LinearSRGBColorSpace, HalfFloatType, RGBAFormat } from '../../constants.js';
import { DoubleSide, BackSide, FrontSide, SRGBColorSpace, NoColorSpace, NoToneMapping, LinearFilter, LinearSRGBColorSpace, HalfFloatType, RGBAFormat, PCFShadowMap } from '../../constants.js';

const _scene = /*@__PURE__*/ new Scene();
const _drawingBufferSize = /*@__PURE__*/ new Vector2();
Expand Down Expand Up @@ -138,7 +138,7 @@ class Renderer {

this.shadowMap = {
enabled: false,
type: null
type: PCFShadowMap
};

this.xr = {
Expand Down

0 comments on commit 3e15548

Please sign in to comment.