UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

671 lines (630 loc) 28.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.shadow = exports.default = exports.VSMShadowFilter = exports.PCFSoftShadowFilter = exports.PCFShadowFilter = exports.BasicShadowFilter = void 0; var _ShadowBaseNode = _interopRequireWildcard(require("./ShadowBaseNode.js")); var _TSLBase = require("../tsl/TSLBase.js"); var _ReferenceNode = require("../accessors/ReferenceNode.js"); var _TextureNode = require("../accessors/TextureNode.js"); var _Position = require("../accessors/Position.js"); var _Normal = require("../accessors/Normal.js"); var _MathNode = require("../math/MathNode.js"); var _OperatorNode = require("../math/OperatorNode.js"); var _DepthTexture = require("../../textures/DepthTexture.js"); var _NodeMaterial = _interopRequireDefault(require("../../materials/nodes/NodeMaterial.js")); var _QuadMesh = _interopRequireDefault(require("../../renderers/common/QuadMesh.js")); var _LoopNode = require("../utils/LoopNode.js"); var _ScreenNode = require("../display/ScreenNode.js"); var _constants = require("../../constants.js"); var _UniformGroupNode = require("../core/UniformGroupNode.js"); var _ViewportDepthNode = require("../display/ViewportDepthNode.js"); var _Object3DNode = require("../accessors/Object3DNode.js"); var _Lights = require("../accessors/Lights.js"); var _RendererUtils = require("../../renderers/common/RendererUtils.js"); var _NodeUtils = require("../core/NodeUtils.js"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } const shadowMaterialLib = /*@__PURE__*/new WeakMap(); const linearDistance = /*@__PURE__*/(0, _TSLBase.Fn)(([position, cameraNear, cameraFar]) => { let dist = _Position.positionWorld.sub(position).length(); dist = dist.sub(cameraNear).div(cameraFar.sub(cameraNear)); dist = dist.saturate(); // clamp to [ 0, 1 ] return dist; }); const linearShadowDistance = light => { const camera = light.shadow.camera; const nearDistance = (0, _ReferenceNode.reference)('near', 'float', camera).setGroup(_UniformGroupNode.renderGroup); const farDistance = (0, _ReferenceNode.reference)('far', 'float', camera).setGroup(_UniformGroupNode.renderGroup); const referencePosition = (0, _Object3DNode.objectPosition)(light); return linearDistance(referencePosition, nearDistance, farDistance); }; const getShadowMaterial = light => { let material = shadowMaterialLib.get(light); if (material === undefined) { const depthNode = light.isPointLight ? linearShadowDistance(light) : null; material = new _NodeMaterial.default(); material.colorNode = (0, _TSLBase.vec4)(0, 0, 0, 1); material.depthNode = depthNode; material.isShadowPassMaterial = true; // Use to avoid other overrideMaterial override material.colorNode unintentionally when using material.shadowNode material.name = 'ShadowMaterial'; material.fog = false; shadowMaterialLib.set(light, material); } return material; }; /** * A shadow filtering function performing basic filtering. This is in fact an unfiltered version of the shadow map * with a binary `[0,1]` result. * * @method * @param {Object} inputs - The input parameter object. * @param {DepthTexture} inputs.depthTexture - A reference to the shadow map's texture data. * @param {Node<vec3>} inputs.shadowCoord - The shadow coordinates. * @return {Node<float>} The filtering result. */ const BasicShadowFilter = exports.BasicShadowFilter = /*@__PURE__*/(0, _TSLBase.Fn)(({ depthTexture, shadowCoord }) => { return (0, _TextureNode.texture)(depthTexture, shadowCoord.xy).compare(shadowCoord.z); }); /** * A shadow filtering function performing PCF filtering. * * @method * @param {Object} inputs - The input parameter object. * @param {DepthTexture} inputs.depthTexture - A reference to the shadow map's texture data. * @param {Node<vec3>} inputs.shadowCoord - The shadow coordinates. * @param {LightShadow} inputs.shadow - The light shadow. * @return {Node<float>} The filtering result. */ const PCFShadowFilter = exports.PCFShadowFilter = /*@__PURE__*/(0, _TSLBase.Fn)(({ depthTexture, shadowCoord, shadow }) => { const depthCompare = (uv, compare) => (0, _TextureNode.texture)(depthTexture, uv).compare(compare); const mapSize = (0, _ReferenceNode.reference)('mapSize', 'vec2', shadow).setGroup(_UniformGroupNode.renderGroup); const radius = (0, _ReferenceNode.reference)('radius', 'float', shadow).setGroup(_UniformGroupNode.renderGroup); const texelSize = (0, _TSLBase.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); return (0, _OperatorNode.add)(depthCompare(shadowCoord.xy.add((0, _TSLBase.vec2)(dx0, dy0)), shadowCoord.z), depthCompare(shadowCoord.xy.add((0, _TSLBase.vec2)(0, dy0)), shadowCoord.z), depthCompare(shadowCoord.xy.add((0, _TSLBase.vec2)(dx1, dy0)), shadowCoord.z), depthCompare(shadowCoord.xy.add((0, _TSLBase.vec2)(dx2, dy2)), shadowCoord.z), depthCompare(shadowCoord.xy.add((0, _TSLBase.vec2)(0, dy2)), shadowCoord.z), depthCompare(shadowCoord.xy.add((0, _TSLBase.vec2)(dx3, dy2)), shadowCoord.z), depthCompare(shadowCoord.xy.add((0, _TSLBase.vec2)(dx0, 0)), shadowCoord.z), depthCompare(shadowCoord.xy.add((0, _TSLBase.vec2)(dx2, 0)), shadowCoord.z), depthCompare(shadowCoord.xy, shadowCoord.z), depthCompare(shadowCoord.xy.add((0, _TSLBase.vec2)(dx3, 0)), shadowCoord.z), depthCompare(shadowCoord.xy.add((0, _TSLBase.vec2)(dx1, 0)), shadowCoord.z), depthCompare(shadowCoord.xy.add((0, _TSLBase.vec2)(dx2, dy3)), shadowCoord.z), depthCompare(shadowCoord.xy.add((0, _TSLBase.vec2)(0, dy3)), shadowCoord.z), depthCompare(shadowCoord.xy.add((0, _TSLBase.vec2)(dx3, dy3)), shadowCoord.z), depthCompare(shadowCoord.xy.add((0, _TSLBase.vec2)(dx0, dy1)), shadowCoord.z), depthCompare(shadowCoord.xy.add((0, _TSLBase.vec2)(0, dy1)), shadowCoord.z), depthCompare(shadowCoord.xy.add((0, _TSLBase.vec2)(dx1, dy1)), shadowCoord.z)).mul(1 / 17); }); /** * A shadow filtering function performing PCF soft filtering. * * @method * @param {Object} inputs - The input parameter object. * @param {DepthTexture} inputs.depthTexture - A reference to the shadow map's texture data. * @param {Node<vec3>} inputs.shadowCoord - The shadow coordinates. * @param {LightShadow} inputs.shadow - The light shadow. * @return {Node<float>} The filtering result. */ const PCFSoftShadowFilter = exports.PCFSoftShadowFilter = /*@__PURE__*/(0, _TSLBase.Fn)(({ depthTexture, shadowCoord, shadow }) => { const depthCompare = (uv, compare) => (0, _TextureNode.texture)(depthTexture, uv).compare(compare); const mapSize = (0, _ReferenceNode.reference)('mapSize', 'vec2', shadow).setGroup(_UniformGroupNode.renderGroup); const texelSize = (0, _TSLBase.vec2)(1).div(mapSize); const dx = texelSize.x; const dy = texelSize.y; const uv = shadowCoord.xy; const f = (0, _MathNode.fract)(uv.mul(mapSize).add(0.5)); uv.subAssign(f.mul(texelSize)); return (0, _OperatorNode.add)(depthCompare(uv, shadowCoord.z), depthCompare(uv.add((0, _TSLBase.vec2)(dx, 0)), shadowCoord.z), depthCompare(uv.add((0, _TSLBase.vec2)(0, dy)), shadowCoord.z), depthCompare(uv.add(texelSize), shadowCoord.z), (0, _MathNode.mix)(depthCompare(uv.add((0, _TSLBase.vec2)(dx.negate(), 0)), shadowCoord.z), depthCompare(uv.add((0, _TSLBase.vec2)(dx.mul(2), 0)), shadowCoord.z), f.x), (0, _MathNode.mix)(depthCompare(uv.add((0, _TSLBase.vec2)(dx.negate(), dy)), shadowCoord.z), depthCompare(uv.add((0, _TSLBase.vec2)(dx.mul(2), dy)), shadowCoord.z), f.x), (0, _MathNode.mix)(depthCompare(uv.add((0, _TSLBase.vec2)(0, dy.negate())), shadowCoord.z), depthCompare(uv.add((0, _TSLBase.vec2)(0, dy.mul(2))), shadowCoord.z), f.y), (0, _MathNode.mix)(depthCompare(uv.add((0, _TSLBase.vec2)(dx, dy.negate())), shadowCoord.z), depthCompare(uv.add((0, _TSLBase.vec2)(dx, dy.mul(2))), shadowCoord.z), f.y), (0, _MathNode.mix)((0, _MathNode.mix)(depthCompare(uv.add((0, _TSLBase.vec2)(dx.negate(), dy.negate())), shadowCoord.z), depthCompare(uv.add((0, _TSLBase.vec2)(dx.mul(2), dy.negate())), shadowCoord.z), f.x), (0, _MathNode.mix)(depthCompare(uv.add((0, _TSLBase.vec2)(dx.negate(), dy.mul(2))), shadowCoord.z), depthCompare(uv.add((0, _TSLBase.vec2)(dx.mul(2), dy.mul(2))), shadowCoord.z), f.x), f.y)).mul(1 / 9); }); /** * A shadow filtering function performing VSM filtering. * * @method * @param {Object} inputs - The input parameter object. * @param {DepthTexture} inputs.depthTexture - A reference to the shadow map's texture data. * @param {Node<vec3>} inputs.shadowCoord - The shadow coordinates. * @return {Node<float>} The filtering result. */ const VSMShadowFilter = exports.VSMShadowFilter = /*@__PURE__*/(0, _TSLBase.Fn)(({ depthTexture, shadowCoord }) => { const occlusion = (0, _TSLBase.float)(1).toVar(); const distribution = (0, _TextureNode.texture)(depthTexture).sample(shadowCoord.xy).rg; const hardShadow = (0, _MathNode.step)(shadowCoord.z, distribution.x); (0, _TSLBase.If)(hardShadow.notEqual((0, _TSLBase.float)(1.0)), () => { const distance = shadowCoord.z.sub(distribution.x); const variance = (0, _MathNode.max)(0, distribution.y.mul(distribution.y)); let softnessProbability = variance.div(variance.add(distance.mul(distance))); // Chebeyshevs inequality softnessProbability = (0, _MathNode.clamp)((0, _OperatorNode.sub)(softnessProbability, 0.3).div(0.95 - 0.3)); occlusion.assign((0, _MathNode.clamp)((0, _MathNode.max)(hardShadow, softnessProbability))); }); return occlusion; }); /** * Represents the shader code for the first VSM render pass. * * @method * @param {Object} inputs - The input parameter object. * @param {Node<float>} inputs.samples - The number of samples * @param {Node<float>} inputs.radius - The radius. * @param {Node<float>} inputs.size - The size. * @param {TextureNode} inputs.shadowPass - A reference to the render target's depth data. * @return {Node<vec2>} The VSM output. */ const VSMPassVertical = /*@__PURE__*/(0, _TSLBase.Fn)(({ samples, radius, size, shadowPass }) => { const mean = (0, _TSLBase.float)(0).toVar(); const squaredMean = (0, _TSLBase.float)(0).toVar(); const uvStride = samples.lessThanEqual((0, _TSLBase.float)(1)).select((0, _TSLBase.float)(0), (0, _TSLBase.float)(2).div(samples.sub(1))); const uvStart = samples.lessThanEqual((0, _TSLBase.float)(1)).select((0, _TSLBase.float)(0), (0, _TSLBase.float)(-1)); (0, _LoopNode.Loop)({ start: (0, _TSLBase.int)(0), end: (0, _TSLBase.int)(samples), type: 'int', condition: '<' }, ({ i }) => { const uvOffset = uvStart.add((0, _TSLBase.float)(i).mul(uvStride)); const depth = shadowPass.sample((0, _OperatorNode.add)(_ScreenNode.screenCoordinate.xy, (0, _TSLBase.vec2)(0, uvOffset).mul(radius)).div(size)).x; mean.addAssign(depth); squaredMean.addAssign(depth.mul(depth)); }); mean.divAssign(samples); squaredMean.divAssign(samples); const std_dev = (0, _MathNode.sqrt)(squaredMean.sub(mean.mul(mean))); return (0, _TSLBase.vec2)(mean, std_dev); }); /** * Represents the shader code for the second VSM render pass. * * @method * @param {Object} inputs - The input parameter object. * @param {Node<float>} inputs.samples - The number of samples * @param {Node<float>} inputs.radius - The radius. * @param {Node<float>} inputs.size - The size. * @param {TextureNode} inputs.shadowPass - The result of the first VSM render pass. * @return {Node<vec2>} The VSM output. */ const VSMPassHorizontal = /*@__PURE__*/(0, _TSLBase.Fn)(({ samples, radius, size, shadowPass }) => { const mean = (0, _TSLBase.float)(0).toVar(); const squaredMean = (0, _TSLBase.float)(0).toVar(); const uvStride = samples.lessThanEqual((0, _TSLBase.float)(1)).select((0, _TSLBase.float)(0), (0, _TSLBase.float)(2).div(samples.sub(1))); const uvStart = samples.lessThanEqual((0, _TSLBase.float)(1)).select((0, _TSLBase.float)(0), (0, _TSLBase.float)(-1)); (0, _LoopNode.Loop)({ start: (0, _TSLBase.int)(0), end: (0, _TSLBase.int)(samples), type: 'int', condition: '<' }, ({ i }) => { const uvOffset = uvStart.add((0, _TSLBase.float)(i).mul(uvStride)); const distribution = shadowPass.sample((0, _OperatorNode.add)(_ScreenNode.screenCoordinate.xy, (0, _TSLBase.vec2)(uvOffset, 0).mul(radius)).div(size)); mean.addAssign(distribution.x); squaredMean.addAssign((0, _OperatorNode.add)(distribution.y.mul(distribution.y), distribution.x.mul(distribution.x))); }); mean.divAssign(samples); squaredMean.divAssign(samples); const std_dev = (0, _MathNode.sqrt)(squaredMean.sub(mean.mul(mean))); return (0, _TSLBase.vec2)(mean, std_dev); }); const _shadowFilterLib = [BasicShadowFilter, PCFShadowFilter, PCFSoftShadowFilter, VSMShadowFilter]; // let _rendererState; const _quadMesh = /*@__PURE__*/new _QuadMesh.default(); /** * Represents the default shadow implementation for lighting nodes. * * @augments ShadowBaseNode */ class ShadowNode extends _ShadowBaseNode.default { static get type() { return 'ShadowNode'; } /** * Constructs a new shadow node. * * @param {Light} light - The shadow casting light. * @param {?LightShadow} [shadow=null] - An optional light shadow. */ constructor(light, shadow = null) { super(light); /** * The light shadow which defines the properties light's * shadow. * * @type {?LightShadow} * @default null */ this.shadow = shadow || light.shadow; /** * A reference to the shadow map which is a render target. * * @type {?RenderTarget} * @default null */ this.shadowMap = null; /** * Only relevant for VSM shadows. Render target for the * first VSM render pass. * * @type {?RenderTarget} * @default null */ this.vsmShadowMapVertical = null; /** * Only relevant for VSM shadows. Render target for the * second VSM render pass. * * @type {?RenderTarget} * @default null */ this.vsmShadowMapHorizontal = null; /** * Only relevant for VSM shadows. Node material which * is used to render the first VSM pass. * * @type {?NodeMaterial} * @default null */ this.vsmMaterialVertical = null; /** * Only relevant for VSM shadows. Node material which * is used to render the second VSM pass. * * @type {?NodeMaterial} * @default null */ this.vsmMaterialHorizontal = null; /** * A reference to the output node which defines the * final result of this shadow node. * * @type {?Node} * @private * @default null */ this._node = null; this._cameraFrameId = new WeakMap(); /** * This flag can be used for type testing. * * @type {boolean} * @readonly * @default true */ this.isShadowNode = true; } /** * Setups the shadow filtering. * * @param {NodeBuilder} builder - A reference to the current node builder. * @param {Object} inputs - A configuration object that defines the shadow filtering. * @param {Function} inputs.filterFn - This function defines the filtering type of the shadow map e.g. PCF. * @param {DepthTexture} inputs.depthTexture - A reference to the shadow map's texture data. * @param {Node<vec3>} inputs.shadowCoord - Shadow coordinates which are used to sample from the shadow map. * @param {LightShadow} inputs.shadow - The light shadow. * @return {Node<float>} The result node of the shadow filtering. */ setupShadowFilter(builder, { filterFn, depthTexture, shadowCoord, shadow }) { 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)); const shadowNode = filterFn({ depthTexture, shadowCoord, shadow }); return frustumTest.select(shadowNode, (0, _TSLBase.float)(1)); } /** * Setups the shadow coordinates. * * @param {NodeBuilder} builder - A reference to the current node builder. * @param {Node<vec3>} shadowPosition - A node representing the shadow position. * @return {Node<vec3>} The shadow coordinates. */ setupShadowCoord(builder, shadowPosition) { const { shadow } = this; const { renderer } = builder; const bias = (0, _ReferenceNode.reference)('bias', 'float', shadow).setGroup(_UniformGroupNode.renderGroup); let shadowCoord = shadowPosition; let coordZ; if (shadow.camera.isOrthographicCamera || renderer.logarithmicDepthBuffer !== true) { shadowCoord = shadowCoord.xyz.div(shadowCoord.w); coordZ = shadowCoord.z; if (renderer.coordinateSystem === _constants.WebGPUCoordinateSystem) { coordZ = coordZ.mul(2).sub(1); // WebGPU: Conversion [ 0, 1 ] to [ - 1, 1 ] } } else { const w = shadowCoord.w; shadowCoord = shadowCoord.xy.div(w); // <-- Only divide X/Y coords since we don't need Z // The normally available "cameraNear" and "cameraFar" nodes cannot be used here because they do not get // updated to use the shadow camera. So, we have to declare our own "local" ones here. // TODO: How do we get the cameraNear/cameraFar nodes to use the shadow camera so we don't have to declare local ones here? const cameraNearLocal = (0, _ReferenceNode.reference)('near', 'float', shadow.camera).setGroup(_UniformGroupNode.renderGroup); const cameraFarLocal = (0, _ReferenceNode.reference)('far', 'float', shadow.camera).setGroup(_UniformGroupNode.renderGroup); coordZ = (0, _ViewportDepthNode.viewZToLogarithmicDepth)(w.negate(), cameraNearLocal, cameraFarLocal); } shadowCoord = (0, _TSLBase.vec3)(shadowCoord.x, shadowCoord.y.oneMinus(), // follow webgpu standards coordZ.add(bias)); return shadowCoord; } /** * Returns the shadow filtering function for the given shadow type. * * @param {number} type - The shadow type. * @return {Function} The filtering function. */ getShadowFilterFn(type) { return _shadowFilterLib[type]; } /** * Setups the shadow output node. * * @param {NodeBuilder} builder - A reference to the current node builder. * @return {Node<vec3>} The shadow output node. */ setupShadow(builder) { const { renderer } = builder; const { light, shadow } = this; const shadowMapType = renderer.shadowMap.type; const depthTexture = new _DepthTexture.DepthTexture(shadow.mapSize.width, shadow.mapSize.height); depthTexture.compareFunction = _constants.LessCompare; const shadowMap = builder.createRenderTarget(shadow.mapSize.width, shadow.mapSize.height); shadowMap.depthTexture = depthTexture; shadow.camera.updateProjectionMatrix(); // VSM if (shadowMapType === _constants.VSMShadowMap) { depthTexture.compareFunction = null; // VSM does not use textureSampleCompare()/texture2DCompare() this.vsmShadowMapVertical = builder.createRenderTarget(shadow.mapSize.width, shadow.mapSize.height, { format: _constants.RGFormat, type: _constants.HalfFloatType }); this.vsmShadowMapHorizontal = builder.createRenderTarget(shadow.mapSize.width, shadow.mapSize.height, { format: _constants.RGFormat, type: _constants.HalfFloatType }); const shadowPassVertical = (0, _TextureNode.texture)(depthTexture); const shadowPassHorizontal = (0, _TextureNode.texture)(this.vsmShadowMapVertical.texture); const samples = (0, _ReferenceNode.reference)('blurSamples', 'float', shadow).setGroup(_UniformGroupNode.renderGroup); const radius = (0, _ReferenceNode.reference)('radius', 'float', shadow).setGroup(_UniformGroupNode.renderGroup); const size = (0, _ReferenceNode.reference)('mapSize', 'vec2', shadow).setGroup(_UniformGroupNode.renderGroup); let material = this.vsmMaterialVertical || (this.vsmMaterialVertical = new _NodeMaterial.default()); material.fragmentNode = VSMPassVertical({ samples, radius, size, shadowPass: shadowPassVertical }).context(builder.getSharedContext()); material.name = 'VSMVertical'; material = this.vsmMaterialHorizontal || (this.vsmMaterialHorizontal = new _NodeMaterial.default()); material.fragmentNode = VSMPassHorizontal({ samples, radius, size, shadowPass: shadowPassHorizontal }).context(builder.getSharedContext()); material.name = 'VSMHorizontal'; } // const shadowIntensity = (0, _ReferenceNode.reference)('intensity', 'float', shadow).setGroup(_UniformGroupNode.renderGroup); const normalBias = (0, _ReferenceNode.reference)('normalBias', 'float', shadow).setGroup(_UniformGroupNode.renderGroup); const shadowPosition = (0, _Lights.lightShadowMatrix)(light).mul(_ShadowBaseNode.shadowPositionWorld.add(_Normal.transformedNormalWorld.mul(normalBias))); const shadowCoord = this.setupShadowCoord(builder, shadowPosition); // const filterFn = shadow.filterNode || this.getShadowFilterFn(renderer.shadowMap.type) || null; if (filterFn === null) { throw new Error('THREE.WebGPURenderer: Shadow map type not supported yet.'); } const shadowDepthTexture = shadowMapType === _constants.VSMShadowMap ? this.vsmShadowMapHorizontal.texture : depthTexture; const shadowNode = this.setupShadowFilter(builder, { filterFn, shadowTexture: shadowMap.texture, depthTexture: shadowDepthTexture, shadowCoord, shadow }); const shadowColor = (0, _TextureNode.texture)(shadowMap.texture, shadowCoord); const shadowOutput = (0, _MathNode.mix)(1, shadowNode.rgb.mix(shadowColor, 1), shadowIntensity.mul(shadowColor.a)).toVar(); this.shadowMap = shadowMap; this.shadow.map = shadowMap; return shadowOutput; } /** * The implementation performs the setup of the output node. An output is only * produces if shadow mapping is globally enabled in the renderer. * * @param {NodeBuilder} builder - A reference to the current node builder. * @return {ShaderCallNodeInternal} The output node. */ setup(builder) { if (builder.renderer.shadowMap.enabled === false) return; return (0, _TSLBase.Fn)(() => { let node = this._node; this.setupShadowPosition(builder); if (node === null) { this._node = node = this.setupShadow(builder); } if (builder.material.shadowNode) { // @deprecated, r171 console.warn('THREE.NodeMaterial: ".shadowNode" is deprecated. Use ".castShadowNode" instead.'); } if (builder.material.receivedShadowNode) { node = builder.material.receivedShadowNode(node); } return node; })(); } /** * Renders the shadow. The logic of this function could be included * into {@link ShadowNode#updateShadow} however more specialized shadow * nodes might require a custom shadow map rendering. By having a * dedicated method, it's easier to overwrite the default behavior. * * @param {NodeFrame} frame - A reference to the current node frame. */ renderShadow(frame) { const { shadow, shadowMap, light } = this; const { renderer, scene } = frame; shadow.updateMatrices(light); shadowMap.setSize(shadow.mapSize.width, shadow.mapSize.height); renderer.render(scene, shadow.camera); } /** * Updates the shadow. * * @param {NodeFrame} frame - A reference to the current node frame. */ updateShadow(frame) { const { shadowMap, light, shadow } = this; const { renderer, scene, camera } = frame; const shadowType = renderer.shadowMap.type; const depthVersion = shadowMap.depthTexture.version; this._depthVersionCached = depthVersion; shadow.camera.layers.mask = camera.layers.mask; const currentRenderObjectFunction = renderer.getRenderObjectFunction(); const currentMRT = renderer.getMRT(); const useVelocity = currentMRT ? currentMRT.has('velocity') : false; _rendererState = (0, _RendererUtils.resetRendererAndSceneState)(renderer, scene, _rendererState); scene.overrideMaterial = getShadowMaterial(light); renderer.setRenderObjectFunction((object, scene, _camera, geometry, material, group, ...params) => { if (object.castShadow === true || object.receiveShadow && shadowType === _constants.VSMShadowMap) { if (useVelocity) { (0, _NodeUtils.getDataFromObject)(object).useVelocity = true; } object.onBeforeShadow(renderer, object, camera, shadow.camera, geometry, scene.overrideMaterial, group); renderer.renderObject(object, scene, _camera, geometry, material, group, ...params); object.onAfterShadow(renderer, object, camera, shadow.camera, geometry, scene.overrideMaterial, group); } }); renderer.setRenderTarget(shadowMap); this.renderShadow(frame); renderer.setRenderObjectFunction(currentRenderObjectFunction); // vsm blur pass if (light.isPointLight !== true && shadowType === _constants.VSMShadowMap) { this.vsmPass(renderer); } (0, _RendererUtils.restoreRendererAndSceneState)(renderer, scene, _rendererState); } /** * For VSM additional render passes are required. * * @param {Renderer} renderer - A reference to the current renderer. */ vsmPass(renderer) { const { shadow } = this; this.vsmShadowMapVertical.setSize(shadow.mapSize.width, shadow.mapSize.height); this.vsmShadowMapHorizontal.setSize(shadow.mapSize.width, shadow.mapSize.height); renderer.setRenderTarget(this.vsmShadowMapVertical); _quadMesh.material = this.vsmMaterialVertical; _quadMesh.render(renderer); renderer.setRenderTarget(this.vsmShadowMapHorizontal); _quadMesh.material = this.vsmMaterialHorizontal; _quadMesh.render(renderer); } /** * Frees the internal resources of this shadow node. */ dispose() { this.shadowMap.dispose(); this.shadowMap = null; if (this.vsmShadowMapVertical !== null) { this.vsmShadowMapVertical.dispose(); this.vsmShadowMapVertical = null; this.vsmMaterialVertical.dispose(); this.vsmMaterialVertical = null; } if (this.vsmShadowMapHorizontal !== null) { this.vsmShadowMapHorizontal.dispose(); this.vsmShadowMapHorizontal = null; this.vsmMaterialHorizontal.dispose(); this.vsmMaterialHorizontal = null; } super.dispose(); } /** * The implementation performs the update of the shadow map if necessary. * * @param {NodeFrame} frame - A reference to the current node frame. */ updateBefore(frame) { const { shadow } = this; let needsUpdate = shadow.needsUpdate || shadow.autoUpdate; if (needsUpdate) { if (this._cameraFrameId[frame.camera] === frame.frameId) { needsUpdate = false; } this._cameraFrameId[frame.camera] = frame.frameId; } if (needsUpdate) { this.updateShadow(frame); if (this.shadowMap.depthTexture.version === this._depthVersionCached) { shadow.needsUpdate = false; } } } } var _default = exports.default = ShadowNode; /** * TSL function for creating an instance of `ShadowNode`. * * @tsl * @function * @param {Light} light - The shadow casting light. * @param {?LightShadow} [shadow] - The light shadow. * @return {ShadowNode} The created shadow node. */ const shadow = (light, shadow) => (0, _TSLBase.nodeObject)(new ShadowNode(light, shadow)); exports.shadow = shadow;