@openhps/core
Version:
Open Hybrid Positioning System - Core component
671 lines (630 loc) • 28.8 kB
JavaScript
"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;