|
| 1 | +import { CUSTOM_ELEMENTS_SCHEMA, Component, Input, computed, inject } from '@angular/core'; |
| 2 | +import { NgtArgs, NgtSignalStore, injectNgtRef } from 'angular-three'; |
| 3 | +import { NGTP_EFFECT_COMPOSER_API } from 'angular-three-postprocessing'; |
| 4 | +import { BlendFunction, SSAOEffect } from 'postprocessing'; |
| 5 | + |
| 6 | +// first two args are camera and texture |
| 7 | +export type NgtpSSAOState = NonNullable<ConstructorParameters<typeof SSAOEffect>[2]>; |
| 8 | + |
| 9 | +@Component({ |
| 10 | + selector: 'ngtp-SSAO', |
| 11 | + standalone: true, |
| 12 | + template: ` <ngt-primitive *args="[effect()]" [ref]="effectRef" /> `, |
| 13 | + imports: [NgtArgs], |
| 14 | + schemas: [CUSTOM_ELEMENTS_SCHEMA], |
| 15 | +}) |
| 16 | +export class NgtpSSAO extends NgtSignalStore<NgtpSSAOState> { |
| 17 | + @Input() effectRef = injectNgtRef<SSAOEffect>(); |
| 18 | + |
| 19 | + @Input() set blendFunction(blendFunction: BlendFunction) { |
| 20 | + this.set({ blendFunction }); |
| 21 | + } |
| 22 | + |
| 23 | + @Input() set distanceScaling(distanceScaling: boolean) { |
| 24 | + this.set({ distanceScaling }); |
| 25 | + } |
| 26 | + |
| 27 | + @Input() set depthAwareUpsampling(depthAwareUpsampling: boolean) { |
| 28 | + this.set({ depthAwareUpsampling }); |
| 29 | + } |
| 30 | + |
| 31 | + @Input() set normalDepthBuffer(normalDepthBuffer: THREE.Texture) { |
| 32 | + this.set({ normalDepthBuffer }); |
| 33 | + } |
| 34 | + |
| 35 | + @Input() set samples(samples: number) { |
| 36 | + this.set({ samples }); |
| 37 | + } |
| 38 | + |
| 39 | + @Input() set rings(rings: number) { |
| 40 | + this.set({ rings }); |
| 41 | + } |
| 42 | + |
| 43 | + @Input() set worldDistanceThreshold(worldDistanceThreshold: number) { |
| 44 | + this.set({ worldDistanceThreshold }); |
| 45 | + } |
| 46 | + |
| 47 | + @Input() set worldDistanceFalloff(worldDistanceFalloff: number) { |
| 48 | + this.set({ worldDistanceFalloff }); |
| 49 | + } |
| 50 | + |
| 51 | + @Input() set worldProximityThreshold(worldProximityThreshold: number) { |
| 52 | + this.set({ worldProximityThreshold }); |
| 53 | + } |
| 54 | + |
| 55 | + @Input() set worldProximityFalloff(worldProximityFalloff: number) { |
| 56 | + this.set({ worldProximityFalloff }); |
| 57 | + } |
| 58 | + |
| 59 | + @Input() set distanceThreshold(distanceThreshold: number) { |
| 60 | + this.set({ distanceThreshold }); |
| 61 | + } |
| 62 | + |
| 63 | + @Input() set distanceFalloff(distanceFalloff: number) { |
| 64 | + this.set({ distanceFalloff }); |
| 65 | + } |
| 66 | + |
| 67 | + @Input() set rangeThreshold(rangeThreshold: number) { |
| 68 | + this.set({ rangeThreshold }); |
| 69 | + } |
| 70 | + |
| 71 | + @Input() set rangeFalloff(rangeFalloff: number) { |
| 72 | + this.set({ rangeFalloff }); |
| 73 | + } |
| 74 | + |
| 75 | + @Input() set minRadiusScale(minRadiusScale: number) { |
| 76 | + this.set({ minRadiusScale }); |
| 77 | + } |
| 78 | + |
| 79 | + @Input() set luminanceInfluence(luminanceInfluence: number) { |
| 80 | + this.set({ luminanceInfluence }); |
| 81 | + } |
| 82 | + |
| 83 | + @Input() set radius(radius: number) { |
| 84 | + this.set({ radius }); |
| 85 | + } |
| 86 | + |
| 87 | + @Input() set intensity(intensity: number) { |
| 88 | + this.set({ intensity }); |
| 89 | + } |
| 90 | + |
| 91 | + @Input() set bias(bias: number) { |
| 92 | + this.set({ bias }); |
| 93 | + } |
| 94 | + |
| 95 | + @Input() set fade(fade: number) { |
| 96 | + this.set({ fade }); |
| 97 | + } |
| 98 | + |
| 99 | + @Input() set color(color: THREE.Color) { |
| 100 | + this.set({ color }); |
| 101 | + } |
| 102 | + |
| 103 | + @Input() set resolutionScale(resolutionScale: number) { |
| 104 | + this.set({ resolutionScale }); |
| 105 | + } |
| 106 | + |
| 107 | + @Input() set resolutionX(resolutionX: number) { |
| 108 | + this.set({ resolutionX }); |
| 109 | + } |
| 110 | + |
| 111 | + @Input() set resolutionY(resolutionY: number) { |
| 112 | + this.set({ resolutionY }); |
| 113 | + } |
| 114 | + |
| 115 | + @Input() set width(width: number) { |
| 116 | + this.set({ width }); |
| 117 | + } |
| 118 | + |
| 119 | + @Input() set height(height: number) { |
| 120 | + this.set({ height }); |
| 121 | + } |
| 122 | + |
| 123 | + readonly #effectComposerApi = inject(NGTP_EFFECT_COMPOSER_API); |
| 124 | + |
| 125 | + readonly #state = this.select(); |
| 126 | + |
| 127 | + readonly effect = computed(() => { |
| 128 | + const state = this.#state(); |
| 129 | + const { camera, normalPass, downSamplingPass, resolutionScale } = this.#effectComposerApi(); |
| 130 | + |
| 131 | + if (normalPass === null && downSamplingPass === null) { |
| 132 | + console.error('Please enable the NormalPass in the NgtpEffectComposer in order to use NgtpSSAO.'); |
| 133 | + return {}; |
| 134 | + } |
| 135 | + |
| 136 | + return new SSAOEffect(camera, normalPass && !downSamplingPass ? (normalPass as any).texture : null, { |
| 137 | + ...state, |
| 138 | + // @ts-expect-error |
| 139 | + normalDepthBuffer: state.normalDepthBuffer || (downSamplingPass ? downSamplingPass.texture : null), |
| 140 | + resolutionScale: state.resolutionScale || (resolutionScale ?? 1), |
| 141 | + depthAwareUpsampling: state.depthAwareUpsampling ?? true, |
| 142 | + }); |
| 143 | + }); |
| 144 | + |
| 145 | + constructor() { |
| 146 | + super({ |
| 147 | + blendFunction: BlendFunction.MULTIPLY, |
| 148 | + samples: 30, |
| 149 | + rings: 4, |
| 150 | + distanceThreshold: 1.0, |
| 151 | + distanceFalloff: 0.0, |
| 152 | + rangeThreshold: 0.5, |
| 153 | + rangeFalloff: 0.1, |
| 154 | + luminanceInfluence: 0.9, |
| 155 | + radius: 20, |
| 156 | + bias: 0.5, |
| 157 | + intensity: 1.0, |
| 158 | + depthAwareUpsampling: true, |
| 159 | + }); |
| 160 | + } |
| 161 | +} |
0 commit comments