@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.
193 lines (159 loc) • 8.03 kB
text/typescript
import type { N8AOPostPass } from "n8ao";
import { Color, DepthFormat, DepthStencilFormat, DepthTexture, PerspectiveCamera, UnsignedInt248Type, UnsignedIntType, WebGLRenderTarget } from "three";
import { MODULES } from "../../../engine/engine_modules.js";
import { serializable } from "../../../engine/engine_serialization.js";
import { validate } from "../../../engine/engine_util_decorator.js";
import { getParam } from "../../../engine/engine_utils.js";
import { type EffectProviderResult, PostProcessingEffect } from "../PostProcessingEffect.js";
import { VolumeParameter } from "../VolumeParameter.js";
import { registerCustomEffectType } from "../VolumeProfile.js";
const debugN8AO = getParam("debugN8AO");
// https://github.com/N8python/n8ao
/**See (N8AO documentation)[https://github.com/N8python/n8ao] */
export enum ScreenSpaceAmbientOcclusionN8QualityMode {
Performance = 0,
Low = 1,
Medium = 2,
High = 3,
Ultra = 4,
}
/** Screen Space Ambient Occlusion (SSAO) effect.
* @category Effects
* @group Components
* @link [N8AO documentation](https://github.com/N8python/n8ao)
*/
export class ScreenSpaceAmbientOcclusionN8 extends PostProcessingEffect {
get typeName() {
return "ScreenSpaceAmbientOcclusionN8";
}
get pass(): N8AOPostPass {
return this._ssao;
}
gammaCorrection: boolean = true;
/** The most important parameter for your ambient occlusion effect.
* Controls the radius/size of the ambient occlusion in world units.
* Should be set to how far you want the occlusion to extend from a given object.
* Set it too low, and AO becomes an edge detector.
* Too high, and the AO becomes "soft" and might not highlight the details you want.
* The radius should be one or two magnitudes less than scene scale:
* if your scene is 10 units across, the radius should be between 0.1 and 1. If its 100, 1 to 10.
* @default 1
*/
aoRadius: VolumeParameter = new VolumeParameter(1);
/** The second most important parameter for your ambient occlusion effect.
* Controls how fast the ambient occlusion fades away with distance in proportion to its radius.
* Defaults to 1, and behind-the-scenes, is a calculated as a ratio of your radius (0.2 * distanceFalloff is the size used for attenuation).
* Decreasing it reduces "haloing" artifacts and improves the accuracy of your occlusion,
* but making it too small makes the ambient occlusion disappear entirely.
* @default 1
*/
falloff: VolumeParameter = new VolumeParameter(1);
/** A purely artistic control for the intensity of the AO - runs the ao through the function pow(ao, intensity),
* which has the effect of darkening areas with more ambient occlusion.
* Useful to make the effect more pronounced.
* An intensity of 2 generally produces soft ambient occlusion that isn't too noticeable,
* whereas one of 5 produces heavily prominent ambient occlusion.
* @default 1
*/
intensity: VolumeParameter = new VolumeParameter(1);
/** The color of the ambient occlusion. By default, it is black, but it can be changed to any color
* to offer a crude approximation of global illumination.
* Recommended in scenes where bounced light has a uniform "color",
* for instance a scene that is predominantly lit by a blue sky.
* The color is expected to be in the sRGB color space, and is automatically converted to linear space for you.
* Keep the color pretty dark for sensible results.
* @default new Color(0, 0, 0)
*/
color: VolumeParameter = new VolumeParameter(new Color(0, 0, 0));
/** If you want the AO to calculate the radius based on screen space, you can do so by setting configuration.screenSpaceRadius to true.
* This is useful for scenes where the camera is moving across different scales a lot,
* or for scenes where the camera is very close to the objects.
* @default false
*/
screenspaceRadius: boolean = false;
/**
* The quality of the ambient occlusion effect.
* @default ScreenSpaceAmbientOcclusionN8QualityMode.Medium
*/
quality: ScreenSpaceAmbientOcclusionN8QualityMode = ScreenSpaceAmbientOcclusionN8QualityMode.Medium;
private _ssao?: N8AOPostPass;
onValidate(): void {
if (this._ssao) {
this._ssao.setQualityMode(ScreenSpaceAmbientOcclusionN8QualityMode[this.quality]);
this._ssao.configuration.gammaCorrection = this.gammaCorrection;
this._ssao.configuration.screenSpaceRadius = this.screenspaceRadius;
}
}
onCreateEffect(): EffectProviderResult {
const cam = this.context.mainCamera! as PerspectiveCamera;
const width = this.context.domWidth;
const height = this.context.domHeight;
const ssao = this._ssao = new MODULES.POSTPROCESSING_AO.MODULE.N8AOPostPass(
this.context.scene,
cam,
width, height
) as N8AOPostPass;
ssao.name = "SSAO_N8";
const mode = ScreenSpaceAmbientOcclusionN8QualityMode[this.quality];
ssao.setQualityMode(mode);
ssao.configuration.transparencyAware = false;
// ssao.needsSwap = false;
const renderTarget = new WebGLRenderTarget(width, height);
ssao.configuration.beautyRenderTarget = renderTarget;
ssao.configuration.autoRenderBeauty = false;
// // If you just want a depth buffer
// renderTarget.depthTexture = new DepthTexture(width, height, UnsignedIntType);
// renderTarget.depthTexture.format = DepthFormat;
// // If you want a depth buffer and a stencil buffer
// renderTarget.depthTexture = new DepthTexture(width, height, UnsignedInt248Type);
// renderTarget.depthTexture.format = DepthStencilFormat;
ssao.configuration.gammaCorrection = this.gammaCorrection;
ssao.configuration.screenSpaceRadius = this.screenspaceRadius;
if (debugN8AO) {
// ssao.debug = debugN8AO;
ssao.enableDebugMode();
console.log(ssao);
setInterval(() => {
console.log("SSAO", ssao.lastTime);
}, 1000);
setInterval(() => {
// ssao.enabled = !ssao.enabled;
console.log("SSAO", ssao.enabled, { ssao, autoRenderBeauty: ssao.configuration.autoRenderBeauty });
}, 4000)
}
this.intensity.onValueChanged = newValue => {
ssao.configuration.intensity = newValue;
}
this.falloff.onValueChanged = newValue => {
ssao.configuration.distanceFalloff = newValue;
}
this.aoRadius.onValueChanged = newValue => {
ssao.configuration.aoRadius = newValue;
}
this.color.onValueChanged = newValue => {
if (!ssao.color) ssao.color = new Color();
ssao.configuration.color.copy(newValue);
}
// const normalPass = new MODULES.POSTPROCESSING.MODULE.NormalPass(this.context.scene, cam);
// const depthDownsamplingPass = new MODULES.POSTPROCESSING.MODULE.DepthDownsamplingPass({
// normalBuffer: normalPass.texture,
// resolutionScale: .1
// });
// const arr = new Array();
// arr.push(normalPass);
// arr.push(depthDownsamplingPass);
// arr.push(ssao);
return ssao;
}
}
registerCustomEffectType("ScreenSpaceAmbientOcclusionN8", ScreenSpaceAmbientOcclusionN8);