1
0

ShadowNode.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838
  1. import ShadowBaseNode, { shadowPositionWorld } from './ShadowBaseNode.js';
  2. import { float, vec2, vec3, int, Fn } from '../tsl/TSLBase.js';
  3. import { reference } from '../accessors/ReferenceNode.js';
  4. import { texture, textureLoad } from '../accessors/TextureNode.js';
  5. import { cubeTexture } from '../accessors/CubeTextureNode.js';
  6. import { normalWorld } from '../accessors/Normal.js';
  7. import { mix, sqrt } from '../math/MathNode.js';
  8. import { add } from '../math/OperatorNode.js';
  9. import { DepthTexture } from '../../textures/DepthTexture.js';
  10. import NodeMaterial from '../../materials/nodes/NodeMaterial.js';
  11. import QuadMesh from '../../renderers/common/QuadMesh.js';
  12. import { Loop } from '../utils/LoopNode.js';
  13. import { screenCoordinate } from '../display/ScreenNode.js';
  14. import { HalfFloatType, LessCompare, RGFormat, VSMShadowMap, WebGPUCoordinateSystem } from '../../constants.js';
  15. import { renderGroup } from '../core/UniformGroupNode.js';
  16. import { viewZToLogarithmicDepth } from '../display/ViewportDepthNode.js';
  17. import { lightShadowMatrix } from '../accessors/Lights.js';
  18. import { resetRendererAndSceneState, restoreRendererAndSceneState } from '../../renderers/common/RendererUtils.js';
  19. import { getDataFromObject } from '../core/NodeUtils.js';
  20. import { getShadowMaterial, disposeShadowMaterial, BasicShadowFilter, PCFShadowFilter, PCFSoftShadowFilter, VSMShadowFilter } from './ShadowFilterNode.js';
  21. import ChainMap from '../../renderers/common/ChainMap.js';
  22. import { textureSize } from '../accessors/TextureSizeNode.js';
  23. import { uv } from '../accessors/UV.js';
  24. //
  25. const _shadowRenderObjectLibrary = /*@__PURE__*/ new ChainMap();
  26. const _shadowRenderObjectKeys = [];
  27. /**
  28. * Creates a function to render shadow objects in a scene.
  29. *
  30. * @tsl
  31. * @function
  32. * @param {Renderer} renderer - The renderer.
  33. * @param {LightShadow} shadow - The light shadow object containing shadow properties.
  34. * @param {number} shadowType - The type of shadow map (e.g., BasicShadowMap).
  35. * @param {boolean} useVelocity - Whether to use velocity data for rendering.
  36. * @return {shadowRenderObjectFunction} A function that renders shadow objects.
  37. */
  38. export const getShadowRenderObjectFunction = ( renderer, shadow, shadowType, useVelocity ) => {
  39. _shadowRenderObjectKeys[ 0 ] = renderer;
  40. _shadowRenderObjectKeys[ 1 ] = shadow;
  41. let renderObjectFunction = _shadowRenderObjectLibrary.get( _shadowRenderObjectKeys );
  42. if ( renderObjectFunction === undefined || ( renderObjectFunction.shadowType !== shadowType || renderObjectFunction.useVelocity !== useVelocity ) ) {
  43. renderObjectFunction = ( object, scene, _camera, geometry, material, group, ...params ) => {
  44. if ( object.castShadow === true || ( object.receiveShadow && shadowType === VSMShadowMap ) ) {
  45. if ( useVelocity ) {
  46. getDataFromObject( object ).useVelocity = true;
  47. }
  48. object.onBeforeShadow( renderer, object, _camera, shadow.camera, geometry, scene.overrideMaterial, group );
  49. renderer.renderObject( object, scene, _camera, geometry, material, group, ...params );
  50. object.onAfterShadow( renderer, object, _camera, shadow.camera, geometry, scene.overrideMaterial, group );
  51. }
  52. };
  53. renderObjectFunction.shadowType = shadowType;
  54. renderObjectFunction.useVelocity = useVelocity;
  55. _shadowRenderObjectLibrary.set( _shadowRenderObjectKeys, renderObjectFunction );
  56. }
  57. _shadowRenderObjectKeys[ 0 ] = null;
  58. _shadowRenderObjectKeys[ 1 ] = null;
  59. return renderObjectFunction;
  60. };
  61. /**
  62. * Represents the shader code for the first VSM render pass.
  63. *
  64. * @private
  65. * @method
  66. * @param {Object} inputs - The input parameter object.
  67. * @param {Node<float>} inputs.samples - The number of samples
  68. * @param {Node<float>} inputs.radius - The radius.
  69. * @param {Node<float>} inputs.size - The size.
  70. * @param {TextureNode} inputs.shadowPass - A reference to the render target's depth data.
  71. * @return {Node<vec2>} The VSM output.
  72. */
  73. const VSMPassVertical = /*@__PURE__*/ Fn( ( { samples, radius, size, shadowPass, depthLayer } ) => {
  74. const mean = float( 0 ).toVar( 'meanVertical' );
  75. const squaredMean = float( 0 ).toVar( 'squareMeanVertical' );
  76. const uvStride = samples.lessThanEqual( float( 1 ) ).select( float( 0 ), float( 2 ).div( samples.sub( 1 ) ) );
  77. const uvStart = samples.lessThanEqual( float( 1 ) ).select( float( 0 ), float( - 1 ) );
  78. Loop( { start: int( 0 ), end: int( samples ), type: 'int', condition: '<' }, ( { i } ) => {
  79. const uvOffset = uvStart.add( float( i ).mul( uvStride ) );
  80. let depth = shadowPass.sample( add( screenCoordinate.xy, vec2( 0, uvOffset ).mul( radius ) ).div( size ) );
  81. if ( shadowPass.value.isArrayTexture ) {
  82. depth = depth.depth( depthLayer );
  83. }
  84. depth = depth.x;
  85. mean.addAssign( depth );
  86. squaredMean.addAssign( depth.mul( depth ) );
  87. } );
  88. mean.divAssign( samples );
  89. squaredMean.divAssign( samples );
  90. const std_dev = sqrt( squaredMean.sub( mean.mul( mean ) ).max( 0 ) );
  91. return vec2( mean, std_dev );
  92. } );
  93. /**
  94. * Represents the shader code for the second VSM render pass.
  95. *
  96. * @private
  97. * @method
  98. * @param {Object} inputs - The input parameter object.
  99. * @param {Node<float>} inputs.samples - The number of samples
  100. * @param {Node<float>} inputs.radius - The radius.
  101. * @param {Node<float>} inputs.size - The size.
  102. * @param {TextureNode} inputs.shadowPass - The result of the first VSM render pass.
  103. * @return {Node<vec2>} The VSM output.
  104. */
  105. const VSMPassHorizontal = /*@__PURE__*/ Fn( ( { samples, radius, size, shadowPass, depthLayer } ) => {
  106. const mean = float( 0 ).toVar( 'meanHorizontal' );
  107. const squaredMean = float( 0 ).toVar( 'squareMeanHorizontal' );
  108. const uvStride = samples.lessThanEqual( float( 1 ) ).select( float( 0 ), float( 2 ).div( samples.sub( 1 ) ) );
  109. const uvStart = samples.lessThanEqual( float( 1 ) ).select( float( 0 ), float( - 1 ) );
  110. Loop( { start: int( 0 ), end: int( samples ), type: 'int', condition: '<' }, ( { i } ) => {
  111. const uvOffset = uvStart.add( float( i ).mul( uvStride ) );
  112. let distribution = shadowPass.sample( add( screenCoordinate.xy, vec2( uvOffset, 0 ).mul( radius ) ).div( size ) );
  113. if ( shadowPass.value.isArrayTexture ) {
  114. distribution = distribution.depth( depthLayer );
  115. }
  116. mean.addAssign( distribution.x );
  117. squaredMean.addAssign( add( distribution.y.mul( distribution.y ), distribution.x.mul( distribution.x ) ) );
  118. } );
  119. mean.divAssign( samples );
  120. squaredMean.divAssign( samples );
  121. const std_dev = sqrt( squaredMean.sub( mean.mul( mean ) ).max( 0 ) );
  122. return vec2( mean, std_dev );
  123. } );
  124. const _shadowFilterLib = [ BasicShadowFilter, PCFShadowFilter, PCFSoftShadowFilter, VSMShadowFilter ];
  125. //
  126. let _rendererState;
  127. const _quadMesh = /*@__PURE__*/ new QuadMesh();
  128. /**
  129. * Represents the default shadow implementation for lighting nodes.
  130. *
  131. * @augments ShadowBaseNode
  132. */
  133. class ShadowNode extends ShadowBaseNode {
  134. static get type() {
  135. return 'ShadowNode';
  136. }
  137. /**
  138. * Constructs a new shadow node.
  139. *
  140. * @param {Light} light - The shadow casting light.
  141. * @param {?LightShadow} [shadow=null] - An optional light shadow.
  142. */
  143. constructor( light, shadow = null ) {
  144. super( light );
  145. /**
  146. * The light shadow which defines the properties light's
  147. * shadow.
  148. *
  149. * @type {?LightShadow}
  150. * @default null
  151. */
  152. this.shadow = shadow || light.shadow;
  153. /**
  154. * A reference to the shadow map which is a render target.
  155. *
  156. * @type {?RenderTarget}
  157. * @default null
  158. */
  159. this.shadowMap = null;
  160. /**
  161. * Only relevant for VSM shadows. Render target for the
  162. * first VSM render pass.
  163. *
  164. * @type {?RenderTarget}
  165. * @default null
  166. */
  167. this.vsmShadowMapVertical = null;
  168. /**
  169. * Only relevant for VSM shadows. Render target for the
  170. * second VSM render pass.
  171. *
  172. * @type {?RenderTarget}
  173. * @default null
  174. */
  175. this.vsmShadowMapHorizontal = null;
  176. /**
  177. * Only relevant for VSM shadows. Node material which
  178. * is used to render the first VSM pass.
  179. *
  180. * @type {?NodeMaterial}
  181. * @default null
  182. */
  183. this.vsmMaterialVertical = null;
  184. /**
  185. * Only relevant for VSM shadows. Node material which
  186. * is used to render the second VSM pass.
  187. *
  188. * @type {?NodeMaterial}
  189. * @default null
  190. */
  191. this.vsmMaterialHorizontal = null;
  192. /**
  193. * A reference to the output node which defines the
  194. * final result of this shadow node.
  195. *
  196. * @type {?Node}
  197. * @private
  198. * @default null
  199. */
  200. this._node = null;
  201. /**
  202. * The current shadow map type of this shadow node.
  203. *
  204. * @type {?number}
  205. * @private
  206. * @default null
  207. */
  208. this._currentShadowType = null;
  209. /**
  210. * A Weak Map holding the current frame ID per camera. Used
  211. * to control the update of shadow maps.
  212. *
  213. * @type {WeakMap<Camera,number>}
  214. * @private
  215. */
  216. this._cameraFrameId = new WeakMap();
  217. /**
  218. * This flag can be used for type testing.
  219. *
  220. * @type {boolean}
  221. * @readonly
  222. * @default true
  223. */
  224. this.isShadowNode = true;
  225. /**
  226. * This index can be used when overriding setupRenderTarget with a RenderTarget Array to specify the depth layer.
  227. *
  228. * @type {number}
  229. * @readonly
  230. * @default true
  231. */
  232. this.depthLayer = 0;
  233. }
  234. /**
  235. * Setups the shadow filtering.
  236. *
  237. * @param {NodeBuilder} builder - A reference to the current node builder.
  238. * @param {Object} inputs - A configuration object that defines the shadow filtering.
  239. * @param {Function} inputs.filterFn - This function defines the filtering type of the shadow map e.g. PCF.
  240. * @param {DepthTexture} inputs.depthTexture - A reference to the shadow map's texture data.
  241. * @param {Node<vec3>} inputs.shadowCoord - Shadow coordinates which are used to sample from the shadow map.
  242. * @param {LightShadow} inputs.shadow - The light shadow.
  243. * @return {Node<float>} The result node of the shadow filtering.
  244. */
  245. setupShadowFilter( builder, { filterFn, depthTexture, shadowCoord, shadow, depthLayer } ) {
  246. const frustumTest = shadowCoord.x.greaterThanEqual( 0 )
  247. .and( shadowCoord.x.lessThanEqual( 1 ) )
  248. .and( shadowCoord.y.greaterThanEqual( 0 ) )
  249. .and( shadowCoord.y.lessThanEqual( 1 ) )
  250. .and( shadowCoord.z.lessThanEqual( 1 ) );
  251. const shadowNode = filterFn( { depthTexture, shadowCoord, shadow, depthLayer } );
  252. return frustumTest.select( shadowNode, float( 1 ) );
  253. }
  254. /**
  255. * Setups the shadow coordinates.
  256. *
  257. * @param {NodeBuilder} builder - A reference to the current node builder.
  258. * @param {Node<vec3>} shadowPosition - A node representing the shadow position.
  259. * @return {Node<vec3>} The shadow coordinates.
  260. */
  261. setupShadowCoord( builder, shadowPosition ) {
  262. const { shadow } = this;
  263. const { renderer } = builder;
  264. const bias = reference( 'bias', 'float', shadow ).setGroup( renderGroup );
  265. let shadowCoord = shadowPosition;
  266. let coordZ;
  267. if ( shadow.camera.isOrthographicCamera || renderer.logarithmicDepthBuffer !== true ) {
  268. shadowCoord = shadowCoord.xyz.div( shadowCoord.w );
  269. coordZ = shadowCoord.z;
  270. if ( renderer.coordinateSystem === WebGPUCoordinateSystem ) {
  271. coordZ = coordZ.mul( 2 ).sub( 1 ); // WebGPU: Conversion [ 0, 1 ] to [ - 1, 1 ]
  272. }
  273. } else {
  274. const w = shadowCoord.w;
  275. shadowCoord = shadowCoord.xy.div( w ); // <-- Only divide X/Y coords since we don't need Z
  276. // The normally available "cameraNear" and "cameraFar" nodes cannot be used here because they do not get
  277. // updated to use the shadow camera. So, we have to declare our own "local" ones here.
  278. // TODO: How do we get the cameraNear/cameraFar nodes to use the shadow camera so we don't have to declare local ones here?
  279. const cameraNearLocal = reference( 'near', 'float', shadow.camera ).setGroup( renderGroup );
  280. const cameraFarLocal = reference( 'far', 'float', shadow.camera ).setGroup( renderGroup );
  281. coordZ = viewZToLogarithmicDepth( w.negate(), cameraNearLocal, cameraFarLocal );
  282. }
  283. shadowCoord = vec3(
  284. shadowCoord.x,
  285. shadowCoord.y.oneMinus(), // follow webgpu standards
  286. coordZ.add( bias )
  287. );
  288. return shadowCoord;
  289. }
  290. /**
  291. * Returns the shadow filtering function for the given shadow type.
  292. *
  293. * @param {number} type - The shadow type.
  294. * @return {Function} The filtering function.
  295. */
  296. getShadowFilterFn( type ) {
  297. return _shadowFilterLib[ type ];
  298. }
  299. setupRenderTarget( shadow, builder ) {
  300. const depthTexture = new DepthTexture( shadow.mapSize.width, shadow.mapSize.height );
  301. depthTexture.name = 'ShadowDepthTexture';
  302. depthTexture.compareFunction = LessCompare;
  303. const shadowMap = builder.createRenderTarget( shadow.mapSize.width, shadow.mapSize.height );
  304. shadowMap.texture.name = 'ShadowMap';
  305. shadowMap.texture.type = shadow.mapType;
  306. shadowMap.depthTexture = depthTexture;
  307. return { shadowMap, depthTexture };
  308. }
  309. /**
  310. * Setups the shadow output node.
  311. *
  312. * @param {NodeBuilder} builder - A reference to the current node builder.
  313. * @return {Node<vec3>} The shadow output node.
  314. */
  315. setupShadow( builder ) {
  316. const { renderer, camera } = builder;
  317. const { light, shadow } = this;
  318. const shadowMapType = renderer.shadowMap.type;
  319. const { depthTexture, shadowMap } = this.setupRenderTarget( shadow, builder );
  320. shadow.camera.coordinateSystem = camera.coordinateSystem;
  321. shadow.camera.updateProjectionMatrix();
  322. // VSM
  323. if ( shadowMapType === VSMShadowMap && shadow.isPointLightShadow !== true ) {
  324. depthTexture.compareFunction = null; // VSM does not use textureSampleCompare()/texture2DCompare()
  325. if ( shadowMap.depth > 1 ) {
  326. if ( ! shadowMap._vsmShadowMapVertical ) {
  327. shadowMap._vsmShadowMapVertical = builder.createRenderTarget( shadow.mapSize.width, shadow.mapSize.height, { format: RGFormat, type: HalfFloatType, depth: shadowMap.depth, depthBuffer: false } );
  328. shadowMap._vsmShadowMapVertical.texture.name = 'VSMVertical';
  329. }
  330. this.vsmShadowMapVertical = shadowMap._vsmShadowMapVertical;
  331. if ( ! shadowMap._vsmShadowMapHorizontal ) {
  332. shadowMap._vsmShadowMapHorizontal = builder.createRenderTarget( shadow.mapSize.width, shadow.mapSize.height, { format: RGFormat, type: HalfFloatType, depth: shadowMap.depth, depthBuffer: false } );
  333. shadowMap._vsmShadowMapHorizontal.texture.name = 'VSMHorizontal';
  334. }
  335. this.vsmShadowMapHorizontal = shadowMap._vsmShadowMapHorizontal;
  336. } else {
  337. this.vsmShadowMapVertical = builder.createRenderTarget( shadow.mapSize.width, shadow.mapSize.height, { format: RGFormat, type: HalfFloatType, depthBuffer: false } );
  338. this.vsmShadowMapHorizontal = builder.createRenderTarget( shadow.mapSize.width, shadow.mapSize.height, { format: RGFormat, type: HalfFloatType, depthBuffer: false } );
  339. }
  340. let shadowPassVertical = texture( depthTexture );
  341. if ( depthTexture.isArrayTexture ) {
  342. shadowPassVertical = shadowPassVertical.depth( this.depthLayer );
  343. }
  344. let shadowPassHorizontal = texture( this.vsmShadowMapVertical.texture );
  345. if ( depthTexture.isArrayTexture ) {
  346. shadowPassHorizontal = shadowPassHorizontal.depth( this.depthLayer );
  347. }
  348. const samples = reference( 'blurSamples', 'float', shadow ).setGroup( renderGroup );
  349. const radius = reference( 'radius', 'float', shadow ).setGroup( renderGroup );
  350. const size = reference( 'mapSize', 'vec2', shadow ).setGroup( renderGroup );
  351. let material = this.vsmMaterialVertical || ( this.vsmMaterialVertical = new NodeMaterial() );
  352. material.fragmentNode = VSMPassVertical( { samples, radius, size, shadowPass: shadowPassVertical, depthLayer: this.depthLayer } ).context( builder.getSharedContext() );
  353. material.name = 'VSMVertical';
  354. material = this.vsmMaterialHorizontal || ( this.vsmMaterialHorizontal = new NodeMaterial() );
  355. material.fragmentNode = VSMPassHorizontal( { samples, radius, size, shadowPass: shadowPassHorizontal, depthLayer: this.depthLayer } ).context( builder.getSharedContext() );
  356. material.name = 'VSMHorizontal';
  357. }
  358. //
  359. const shadowIntensity = reference( 'intensity', 'float', shadow ).setGroup( renderGroup );
  360. const normalBias = reference( 'normalBias', 'float', shadow ).setGroup( renderGroup );
  361. const shadowPosition = lightShadowMatrix( light ).mul( shadowPositionWorld.add( normalWorld.mul( normalBias ) ) );
  362. const shadowCoord = this.setupShadowCoord( builder, shadowPosition );
  363. //
  364. const filterFn = shadow.filterNode || this.getShadowFilterFn( renderer.shadowMap.type ) || null;
  365. if ( filterFn === null ) {
  366. throw new Error( 'THREE.WebGPURenderer: Shadow map type not supported yet.' );
  367. }
  368. const shadowDepthTexture = ( shadowMapType === VSMShadowMap && shadow.isPointLightShadow !== true ) ? this.vsmShadowMapHorizontal.texture : depthTexture;
  369. const shadowNode = this.setupShadowFilter( builder, { filterFn, shadowTexture: shadowMap.texture, depthTexture: shadowDepthTexture, shadowCoord, shadow, depthLayer: this.depthLayer } );
  370. let shadowColor;
  371. if ( shadowMap.texture.isCubeTexture ) {
  372. // For cube shadow maps (point lights), use cubeTexture with vec3 coordinates
  373. shadowColor = cubeTexture( shadowMap.texture, shadowCoord.xyz );
  374. } else {
  375. shadowColor = texture( shadowMap.texture, shadowCoord );
  376. if ( depthTexture.isArrayTexture ) {
  377. shadowColor = shadowColor.depth( this.depthLayer );
  378. }
  379. }
  380. const shadowOutput = mix( 1, shadowNode.rgb.mix( shadowColor, 1 ), shadowIntensity.mul( shadowColor.a ) ).toVar();
  381. this.shadowMap = shadowMap;
  382. this.shadow.map = shadowMap;
  383. // Shadow Output + Inspector
  384. const inspectName = `${ this.light.type } Shadow [ ${ this.light.name || 'ID: ' + this.light.id } ]`;
  385. return shadowOutput.toInspector( `${ inspectName } / Color`, () => {
  386. if ( this.shadowMap.texture.isCubeTexture ) {
  387. return cubeTexture( this.shadowMap.texture );
  388. }
  389. return texture( this.shadowMap.texture );
  390. } ).toInspector( `${ inspectName } / Depth`, () => {
  391. // TODO: Use linear depth
  392. if ( this.shadowMap.texture.isCubeTexture ) {
  393. return cubeTexture( this.shadowMap.texture ).r.oneMinus();
  394. }
  395. return textureLoad( this.shadowMap.depthTexture, uv().mul( textureSize( texture( this.shadowMap.depthTexture ) ) ) ).r.oneMinus();
  396. } );
  397. }
  398. /**
  399. * The implementation performs the setup of the output node. An output is only
  400. * produces if shadow mapping is globally enabled in the renderer.
  401. *
  402. * @param {NodeBuilder} builder - A reference to the current node builder.
  403. * @return {ShaderCallNodeInternal} The output node.
  404. */
  405. setup( builder ) {
  406. if ( builder.renderer.shadowMap.enabled === false ) return;
  407. return Fn( () => {
  408. const currentShadowType = builder.renderer.shadowMap.type;
  409. if ( this._currentShadowType !== currentShadowType ) {
  410. this._reset();
  411. this._node = null;
  412. }
  413. let node = this._node;
  414. this.setupShadowPosition( builder );
  415. if ( node === null ) {
  416. this._node = node = this.setupShadow( builder );
  417. this._currentShadowType = currentShadowType;
  418. }
  419. if ( builder.material.receivedShadowNode ) {
  420. node = builder.material.receivedShadowNode( node );
  421. }
  422. return node;
  423. } )();
  424. }
  425. /**
  426. * Renders the shadow. The logic of this function could be included
  427. * into {@link ShadowNode#updateShadow} however more specialized shadow
  428. * nodes might require a custom shadow map rendering. By having a
  429. * dedicated method, it's easier to overwrite the default behavior.
  430. *
  431. * @param {NodeFrame} frame - A reference to the current node frame.
  432. */
  433. renderShadow( frame ) {
  434. const { shadow, shadowMap, light } = this;
  435. const { renderer, scene } = frame;
  436. shadow.updateMatrices( light );
  437. shadowMap.setSize( shadow.mapSize.width, shadow.mapSize.height, shadowMap.depth );
  438. const currentSceneName = scene.name;
  439. scene.name = `Shadow Map [ ${ light.name || 'ID: ' + light.id } ]`;
  440. renderer.render( scene, shadow.camera );
  441. scene.name = currentSceneName;
  442. }
  443. /**
  444. * Updates the shadow.
  445. *
  446. * @param {NodeFrame} frame - A reference to the current node frame.
  447. */
  448. updateShadow( frame ) {
  449. const { shadowMap, light, shadow } = this;
  450. const { renderer, scene, camera } = frame;
  451. const shadowType = renderer.shadowMap.type;
  452. const depthVersion = shadowMap.depthTexture.version;
  453. this._depthVersionCached = depthVersion;
  454. const _shadowCameraLayer = shadow.camera.layers.mask;
  455. if ( ( shadow.camera.layers.mask & 0xFFFFFFFE ) === 0 ) {
  456. shadow.camera.layers.mask = camera.layers.mask;
  457. }
  458. const currentRenderObjectFunction = renderer.getRenderObjectFunction();
  459. const currentMRT = renderer.getMRT();
  460. const useVelocity = currentMRT ? currentMRT.has( 'velocity' ) : false;
  461. _rendererState = resetRendererAndSceneState( renderer, scene, _rendererState );
  462. scene.overrideMaterial = getShadowMaterial( light );
  463. renderer.setRenderObjectFunction( getShadowRenderObjectFunction( renderer, shadow, shadowType, useVelocity ) );
  464. renderer.setClearColor( 0x000000, 0 );
  465. renderer.setRenderTarget( shadowMap );
  466. this.renderShadow( frame );
  467. renderer.setRenderObjectFunction( currentRenderObjectFunction );
  468. // vsm blur pass
  469. if ( shadowType === VSMShadowMap && shadow.isPointLightShadow !== true ) {
  470. this.vsmPass( renderer );
  471. }
  472. shadow.camera.layers.mask = _shadowCameraLayer;
  473. restoreRendererAndSceneState( renderer, scene, _rendererState );
  474. }
  475. /**
  476. * For VSM additional render passes are required.
  477. *
  478. * @param {Renderer} renderer - A reference to the current renderer.
  479. */
  480. vsmPass( renderer ) {
  481. const { shadow } = this;
  482. const depth = this.shadowMap.depth;
  483. this.vsmShadowMapVertical.setSize( shadow.mapSize.width, shadow.mapSize.height, depth );
  484. this.vsmShadowMapHorizontal.setSize( shadow.mapSize.width, shadow.mapSize.height, depth );
  485. renderer.setRenderTarget( this.vsmShadowMapVertical );
  486. _quadMesh.material = this.vsmMaterialVertical;
  487. _quadMesh.render( renderer );
  488. renderer.setRenderTarget( this.vsmShadowMapHorizontal );
  489. _quadMesh.material = this.vsmMaterialHorizontal;
  490. _quadMesh.render( renderer );
  491. }
  492. /**
  493. * Frees the internal resources of this shadow node.
  494. */
  495. dispose() {
  496. this._reset();
  497. super.dispose();
  498. }
  499. /**
  500. * Resets the resouce state of this shadow node.
  501. *
  502. * @private
  503. */
  504. _reset() {
  505. this._currentShadowType = null;
  506. disposeShadowMaterial( this.light );
  507. if ( this.shadowMap ) {
  508. this.shadowMap.dispose();
  509. this.shadowMap = null;
  510. }
  511. if ( this.vsmShadowMapVertical !== null ) {
  512. this.vsmShadowMapVertical.dispose();
  513. this.vsmShadowMapVertical = null;
  514. this.vsmMaterialVertical.dispose();
  515. this.vsmMaterialVertical = null;
  516. }
  517. if ( this.vsmShadowMapHorizontal !== null ) {
  518. this.vsmShadowMapHorizontal.dispose();
  519. this.vsmShadowMapHorizontal = null;
  520. this.vsmMaterialHorizontal.dispose();
  521. this.vsmMaterialHorizontal = null;
  522. }
  523. }
  524. /**
  525. * The implementation performs the update of the shadow map if necessary.
  526. *
  527. * @param {NodeFrame} frame - A reference to the current node frame.
  528. */
  529. updateBefore( frame ) {
  530. const { shadow } = this;
  531. let needsUpdate = shadow.needsUpdate || shadow.autoUpdate;
  532. if ( needsUpdate ) {
  533. if ( this._cameraFrameId[ frame.camera ] === frame.frameId ) {
  534. needsUpdate = false;
  535. }
  536. this._cameraFrameId[ frame.camera ] = frame.frameId;
  537. }
  538. if ( needsUpdate ) {
  539. this.updateShadow( frame );
  540. if ( this.shadowMap.depthTexture.version === this._depthVersionCached ) {
  541. shadow.needsUpdate = false;
  542. }
  543. }
  544. }
  545. }
  546. export default ShadowNode;
  547. /**
  548. * Shadow Render Object Function.
  549. *
  550. * @function shadowRenderObjectFunction
  551. * @param {Object3D} object - The 3D object to render.
  552. * @param {Scene} scene - The scene containing the object.
  553. * @param {Camera} _camera - The camera used for rendering.
  554. * @param {BufferGeometry} geometry - The geometry of the object.
  555. * @param {Material} material - The material of the object.
  556. * @param {Group} group - The group the object belongs to.
  557. * @param {...any} params - Additional parameters for rendering.
  558. */
  559. /**
  560. * TSL function for creating an instance of `ShadowNode`.
  561. *
  562. * @tsl
  563. * @function
  564. * @param {Light} light - The shadow casting light.
  565. * @param {?LightShadow} [shadow] - The light shadow.
  566. * @return {ShadowNode} The created shadow node.
  567. */
  568. export const shadow = ( light, shadow ) => new ShadowNode( light, shadow );
粤ICP备19079148号