GTAOPassProxy.js

import Check from "@lijuhong1981/jscheck/src/Check.js";
import GTAONode, { ao } from "three/addons/tsl/display/GTAONode.js";
import { vec3, vec4 } from "three/tsl";
import { Camera, TextureNode } from "three/webgpu";
import PassNodeProxy from "./PassNodeProxy.js";

/**
 * GTAOPassProxy是PassNodeProxy的一个子类,用于代理GTAO(Ground Truth Ambient Occlusion)后处理特效的Pass节点。
 * @class
 * @extends PassNodeProxy<GTAONode>
*/
class GTAOPassProxy extends PassNodeProxy {
    constructor(library) {
        super(library);
        this._values = {
            resolutionScale: 1,
            radius: 0.25,
            thickness: 1,
            distanceExponent: 1,
            distanceFallOff: 1,
            scale: 1,
            samples: 8,
            useTemporalFiltering: false,
        };
    }
    /**
     * The resolution scale. By default the effect is rendered in full resolution
     * for best quality but a value of `0.5` should be sufficient for most scenes.
     *
     * @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;
    }
    /**
     * The radius of the ambient occlusion.
     * @type {number}
     * @default 0.25
    */
    set radius(value) {
        Check.typeOf.number('radius', value);
        this._values.radius = value;
        this.node && (this.node.radius.value = value);
    }
    get radius() {
        return this._values.radius;
    }
    /**
     * The thickness of the ambient occlusion.
     * @type {number}
     * @default 1
    */
    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;
    }
    /**
     * Another option to tweak the occlusion. The recommended range is `[1,2]` for attenuating the AO.
     * @type {number}
     * @default 1
    */
    set distanceExponent(value) {
        Check.typeOf.number('distanceExponent', value);
        this._values.distanceExponent = value;
        this.node && (this.node.distanceExponent.value = value);
    }
    get distanceExponent() {
        return this._values.distanceExponent;
    }
    /**
     * The distance fall off value of the ambient occlusion. A lower value leads to a larger AO effect. The value should lie in the range `[0,1]`.
     * @type {number}
     * @default 1
    */
    set distanceFallOff(value) {
        Check.typeOf.number('distanceFallOff', value);
        this._values.distanceFallOff = value;
        this.node && (this.node.distanceFallOff.value = value);
    }
    get distanceFallOff() {
        return this._values.distanceFallOff;
    }
    /**
     * The scale of the ambient occlusion.
     * @type {number}
     * @default 1
    */
    set scale(value) {
        Check.typeOf.number('scale', value);
        this._values.scale = value;
        this.node && (this.node.scale.value = value);
    }
    get scale() {
        return this._values.scale;
    }
    /**
     * How many samples are used to compute the AO. A higher value results in better quality but also in a more expensive runtime behavior.
     * @type {number}
     * @default 8
    */
    set samples(value) {
        Check.typeOf.number('samples', value);
        this._values.samples = value;
        this.node && (this.node.samples.value = value);
    }
    get samples() {
        return this._values.samples;
    }
    /**
     * Whether to use temporal filtering or not. Setting this property to
     * `true` requires the usage of `TRAANode`. This will help to reduce noise
     * although it introduces typical TAA artifacts like ghosting and temporal
     * instabilities.
     *
     * If setting this property to `false`, a manual denoise via `DenoiseNode`
     * might be required.
     * @type {boolean}
     * @default false
    */
    set useTemporalFiltering(value) {
        Check.typeOf.boolean('useTemporalFiltering', value);
        this._values.useTemporalFiltering = value;
        this.node && (this.node.useTemporalFiltering = value);
    }
    get useTemporalFiltering() {
        return this._values.useTemporalFiltering;
    }
    /**
     * 构建GTAO Pass节点。
     * @param {TextureNode} depthNode - 深度节点对象
     * @param {TextureNode} normalNode - 法线节点对象
     * @param {Camera} camera - 相机对象
     * @param {TextureNode} outputNode - 输出节点对象
     * @returns {TextureNode} 处理后的节点对象
     * @override
    */
    build(depthNode, normalNode, camera, outputNode) {
        const aoPass = this.node = ao(depthNode, normalNode, camera);
        aoPass.toInspector('GTAO');
        this.setValues(this._values);
        const aoPassOutput = aoPass.getTextureNode();
        // AO乘法混合
        return outputNode.mul(vec4(vec3(aoPassOutput.r), 1));
    }
};

export default GTAOPassProxy;
export { GTAOPassProxy };