123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- 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 { reference } from '../accessors/ReferenceNode.js';
- import { texture } from '../accessors/TextureNode.js';
- import { positionWorld } from '../accessors/PositionNode.js';
- import { normalWorld } from '../accessors/NormalNode.js';
- import { WebGPUCoordinateSystem } from 'three';
- import { mix } from '../math/MathNode.js';
- import { Color, DepthTexture, NearestFilter, LessCompare, NoToneMapping } from 'three';
- let overrideMaterial = null;
- class AnalyticLightNode extends LightingNode {
- constructor( light = null ) {
- super();
- this.updateType = NodeUpdateType.FRAME;
- this.light = light;
- this.rtt = null;
- this.shadowNode = null;
- this.shadowMaskNode = null;
- this.color = new Color();
- this._defaultColorNode = uniform( this.color );
- this.colorNode = this._defaultColorNode;
- this.isAnalyticLightNode = true;
- }
- getCacheKey() {
- return super.getCacheKey() + '-' + ( this.light.id + '-' + ( this.light.castShadow ? '1' : '0' ) );
- }
- getHash() {
- return this.light.uuid;
- }
- setupShadow( builder ) {
- const { object } = builder;
- if ( object.receiveShadow === false ) return;
- let shadowNode = this.shadowNode;
- if ( shadowNode === null ) {
- if ( overrideMaterial === null ) {
- overrideMaterial = builder.createNodeMaterial();
- overrideMaterial.fragmentNode = vec4( 0, 0, 0, 1 );
- overrideMaterial.isShadowNodeMaterial = true; // Use to avoid other overrideMaterial override material.fragmentNode unintentionally when using material.shadowNode
- }
- 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;
- shadow.camera.updateProjectionMatrix();
- //
- const shadowIntensity = reference( 'intensity', 'float', shadow );
- const bias = reference( 'bias', 'float', shadow );
- const normalBias = reference( 'normalBias', 'float', shadow );
- const position = object.material.shadowPositionNode || positionWorld;
- 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 ) {
- coordZ = coordZ.mul( 2 ).sub( 1 ); // WebGPU: Convertion [ 0, 1 ] to [ - 1, 1 ]
- }
- shadowCoord = vec3(
- shadowCoord.x,
- shadowCoord.y.oneMinus(), // follow webgpu standards
- coordZ
- );
- 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 ) );
- this.rtt = rtt;
- this.colorNode = this.colorNode.mul( mix( 1, shadowMaskNode, shadowIntensity ) );
- this.shadowNode = shadowNode;
- this.shadowMaskNode = shadowMaskNode;
- //
- this.updateBeforeType = NodeUpdateType.RENDER;
- }
- }
- setup( builder ) {
- if ( this.light.castShadow ) this.setupShadow( builder );
- else if ( this.shadowNode !== null ) this.disposeShadow();
- }
- updateShadow( frame ) {
- const { rtt, light } = this;
- const { renderer, scene, camera } = frame;
- const currentOverrideMaterial = scene.overrideMaterial;
- scene.overrideMaterial = overrideMaterial;
- rtt.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();
- renderer.setRenderObjectFunction( ( object, ...params ) => {
- if ( object.castShadow === true ) {
- renderer.renderObject( object, ...params );
- }
- } );
- renderer.setRenderTarget( rtt );
- renderer.toneMapping = NoToneMapping;
- renderer.render( scene, light.shadow.camera );
- renderer.setRenderTarget( currentRenderTarget );
- renderer.setRenderObjectFunction( currentRenderObjectFunction );
- renderer.toneMapping = currentToneMapping;
- scene.overrideMaterial = currentOverrideMaterial;
- }
- disposeShadow() {
- this.rtt.dispose();
- this.shadowNode = null;
- this.shadowMaskNode = null;
- this.rtt = null;
- this.colorNode = this._defaultColorNode;
- }
- updateBefore( frame ) {
- const { light } = this;
- if ( light.castShadow ) this.updateShadow( frame );
- }
- update( /*frame*/ ) {
- const { light } = this;
- this.color.copy( light.color ).multiplyScalar( light.intensity );
- }
- }
- export default AnalyticLightNode;
- addNodeClass( 'AnalyticLightNode', AnalyticLightNode );
|