import Check from "@lijuhong1981/jscheck/src/Check.js";
import PassNodeProxy from "./PassNodeProxy.js";
import SSSNode, { sss } from 'three/addons/tsl/display/SSSNode.js';
import { DirectionalLight, Light, Node, PassNode, TextureNode } from "three/webgpu";
import { builtinShadowContext, screenUV, vec3, vec4 } from "three/tsl";
/**
* SSSPassProxy是PassNodeProxy的一个子类,用于代理SSS(Screen Space Shadows)后处理特效的Pass节点。
* @class
* @extends PassNodeProxy<SSSNode>
*/
class SSSPassProxy extends PassNodeProxy {
constructor(library) {
super(library);
this._values = {
resolutionScale: 1,
maxDistance: 0.2,
thickness: 0.01,
shadowIntensity: 1,
quality: 0.5,
useTemporalFiltering: true,
};
}
/**
* The resolution scale. Valid values are in the range
* `[0,1]`. `1` means best quality but also results in
* more computational overhead. Setting to `0.5` means
* the effect is computed in half-resolution.
*
* @type {number}
* @default 1
*/
set resolutionScale(value) {
Check.typeOf.number('resolutionScale', value);
this._values.resolutionScale = value;
this.node && (this.node.resolutionScale = value);
}
get resolutionScale() {
return this._values.resolutionScale;
}
/**
* Maximum shadow length in world units. Longer shadows result in more computational
* overhead.
*
* @type {number}
* @default 0.2
*/
set maxDistance(value) {
Check.typeOf.number('maxDistance', value);
this._values.maxDistance = value;
this.node && (this.node.maxDistance.value = value);
}
get maxDistance() {
return this._values.maxDistance;
}
/**
* Depth testing thickness.
*
* @type {number}
* @default 0.01
*/
set thickness(value) {
Check.typeOf.number('thickness', value);
this._values.thickness = value;
this.node && (this.node.thickness.value = value);
}
get thickness() {
return this._values.thickness;
}
/**
* Shadow intensity. Must be in the range `[0, 1]`.
*
* @type {number}
* @default 1
*/
set shadowIntensity(value) {
Check.typeOf.number('shadowIntensity', value);
this._values.shadowIntensity = value;
this.node && (this.node.shadowIntensity.value = value);
}
get shadowIntensity() {
return this._values.shadowIntensity;
}
/**
* This parameter controls how detailed the raymarching process works.
* The value ranges is `[0,1]` where `1` means best quality (the maximum number
* of raymarching iterations/samples) and `0` means no samples at all.
*
* A quality of `0.5` is usually sufficient for most use cases. Try to keep
* this parameter as low as possible. Larger values result in noticeable more
* overhead.
*
* @type {number}
* @default 0.5
*/
set quality(value) {
Check.typeOf.number('quality', value);
this._values.quality = value;
this.node && (this.node.quality.value = value);
}
get quality() {
return this._values.quality;
}
/**
* Whether to use temporal filtering or not. Setting this property to
* `true` requires the usage of `TRAANode`. This will help to reduce noice
* although it introduces typical TAA artifacts like ghosting and temporal
* instabilities.
*
* @type {boolean}
* @default true
*/
set useTemporalFiltering(value) {
Check.typeOf.boolean('useTemporalFiltering', value);
this._values.useTemporalFiltering = value;
this.node && (this.node.useTemporalFiltering = value);
}
get useTemporalFiltering() {
return this._values.useTemporalFiltering;
}
/**
* 构建SSS Pass节点。
* @param {TextureNode} depthNode - 深度节点对象
* @param {Camera} camera - 摄像机对象
* @param {DirectionalLight} mainLight - 主光源对象,必须是DirectionalLight类型
* @param {TextureNode} outputNode - 输出节点对象
* @returns {TextureNode} 处理后的节点对象
* @override
*/
build(depthNode, camera, mainLight, outputNode) {
const sssPass = this.node = sss(depthNode, camera, mainLight).toInspector('SSS');
this.setValues(this._values);
// const sssSample = sssPass.getTextureNode().sample(screenUV).r;
// const sssContext = builtinShadowContext(sssSample, mainLight);
// scenePass.contextNode = sssContext;
const sssPassOutput = sssPass.getTextureNode();
return outputNode.mul(vec4(vec3(sssPassOutput.r), 1));
}
};
export default SSSPassProxy;
export { SSSPassProxy };