UNPKG

three

Version:

JavaScript 3D library

239 lines (175 loc) 6.83 kB
import Node from '../core/Node.js'; import NodeMaterial from '../../materials/nodes/NodeMaterial.js'; import ChainMap from '../../renderers/common/ChainMap.js'; import { getDataFromObject } from '../core/NodeUtils.js'; import { NodeUpdateType } from '../core/constants.js'; import { property, vec4 } from '../tsl/TSLBase.js'; import { positionWorld } from '../accessors/Position.js'; import { NoBlending, VSMShadowMap } from '../../constants.js'; const _shadowMaterialLib = /*@__PURE__*/ new WeakMap(); const _shadowRenderObjectLibrary = /*@__PURE__*/ new ChainMap(); const _shadowRenderObjectKeys = []; /** * Retrieves or creates a shadow material for the given light source. * * This function checks if a shadow material already exists for the provided light. * If not, it creates a new `NodeMaterial` configured for shadow rendering and stores it * in the `shadowMaterialLib` for future use. * * @private * @function * @param {Light} light - The light source for which the shadow material is needed. * If the light is a point light, a depth node is calculated * using the linear shadow distance. * @returns {NodeMaterial} The shadow material associated with the given light. */ const _getShadowMaterial = ( light ) => { let material = _shadowMaterialLib.get( light ); if ( material === undefined ) { material = new NodeMaterial(); material.colorNode = vec4( 0, 0, 0, 1 ); material.isShadowPassMaterial = true; // Use to avoid other overrideMaterial override material.colorNode unintentionally when using material.shadowNode material.name = 'ShadowMaterial'; material.blending = NoBlending; material.fog = false; _shadowMaterialLib.set( light, material ); } return material; }; /** * Disposes the shadow material for the given light source. * * @private * @param {Light} light - The light source. */ const _disposeShadowMaterial = ( light ) => { const material = _shadowMaterialLib.get( light ); if ( material !== undefined ) { material.dispose(); _shadowMaterialLib.delete( light ); } }; /** * Creates a function to render shadow objects in a scene. * * @private * @function * @param {Renderer} renderer - The renderer. * @param {LightShadow} shadow - The light shadow object containing shadow properties. * @param {number} shadowType - The type of shadow map (e.g., BasicShadowMap). * @param {boolean} useVelocity - Whether to use velocity data for rendering. * @return {shadowRenderObjectFunction} A function that renders shadow objects. */ const _getShadowRenderObjectFunction = ( renderer, shadow, shadowType, useVelocity ) => { _shadowRenderObjectKeys[ 0 ] = renderer; _shadowRenderObjectKeys[ 1 ] = shadow; let renderObjectFunction = _shadowRenderObjectLibrary.get( _shadowRenderObjectKeys ); if ( renderObjectFunction === undefined || ( renderObjectFunction.shadowType !== shadowType || renderObjectFunction.useVelocity !== useVelocity ) ) { renderObjectFunction = ( object, scene, _camera, geometry, material, group, lightsNode, clippingContext, passId ) => { if ( object.castShadow === true || ( object.receiveShadow && shadowType === VSMShadowMap ) ) { if ( useVelocity ) { getDataFromObject( object ).useVelocity = true; } object.onBeforeShadow( renderer, object, _camera, shadow.camera, geometry, scene.overrideMaterial, group ); renderer.renderObject( object, scene, _camera, geometry, material, group, lightsNode, clippingContext, passId ); object.onAfterShadow( renderer, object, _camera, shadow.camera, geometry, scene.overrideMaterial, group ); } }; renderObjectFunction.shadowType = shadowType; renderObjectFunction.useVelocity = useVelocity; _shadowRenderObjectLibrary.set( _shadowRenderObjectKeys, renderObjectFunction ); } _shadowRenderObjectKeys[ 0 ] = null; _shadowRenderObjectKeys[ 1 ] = null; return renderObjectFunction; }; /** * Base class for all shadow nodes. * * Shadow nodes encapsulate shadow related logic and are always coupled to lighting nodes. * Lighting nodes might share the same shadow node type or use specific ones depending on * their requirements. * * @augments Node */ class ShadowBaseNode extends Node { static get type() { return 'ShadowBaseNode'; } /** * Constructs a new shadow base node. * * @param {Light} light - The shadow casting light. */ constructor( light ) { super(); /** * The shadow casting light. * * @type {Light} */ this.light = light; /** * Overwritten since shadows are updated by default per render. * * @type {string} * @default 'render' */ this.updateBeforeType = NodeUpdateType.RENDER; /** * This flag can be used for type testing. * * @type {boolean} * @readonly * @default true */ this.isShadowBaseNode = true; } /** * Retrieves or creates a shadow material for the shadow casting light source. * * This method checks if a shadow material already exists for the provided light in the internal library. * If not, it creates a new `NodeMaterial` configured for shadow rendering and stores it for future use. * * @return {NodeMaterial} The shadow material associated with the given light. */ getShadowMaterial() { return _getShadowMaterial( this.light ); } /** * Disposes the shadow material for the shadow casting light source. */ disposeShadowMaterial() { _disposeShadowMaterial( this.light ); } /** * Returns a function to render shadow objects in a scene for the given light shadow and renderer. * * @param {Renderer} renderer - The renderer. * @param {LightShadow} [shadow=this.light.shadow] - The light shadow object containing shadow properties. * @return {Function} A function that renders shadow objects. */ getShadowRenderObjectFunction( renderer, shadow = this.light.shadow ) { const shadowType = renderer.shadowMap.type; const currentMRT = renderer.getMRT(); const useVelocity = currentMRT ? currentMRT.has( 'velocity' ) : false; return _getShadowRenderObjectFunction( renderer, shadow, shadowType, useVelocity ); } /** * Setups the shadow position node which is by default the predefined TSL node object `shadowPositionWorld`. * * @param {NodeBuilder} object - A configuration object that must at least hold a material reference. */ setupShadowPosition( { context, material } ) { // Use assign inside an Fn() shadowPositionWorld.assign( material.receivedShadowPositionNode || context.shadowPositionWorld || positionWorld ); } } /** * TSL object that represents the vertex position in world space during the shadow pass. * * @tsl * @type {Node<vec3>} */ export const shadowPositionWorld = /*@__PURE__*/ property( 'vec3', 'shadowPositionWorld' ); export default ShadowBaseNode;