Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
ac60031
refactor: opt scene
GuoLei1990 Sep 5, 2025
6efca03
refactor: opt code
GuoLei1990 Sep 5, 2025
fe7fab8
refactor: opt code
GuoLei1990 Sep 5, 2025
6677cbd
refactor: opt param names
GuoLei1990 Sep 5, 2025
c0e7bd4
refactor: opt code
GuoLei1990 Sep 5, 2025
c5c58d5
fix: lint error
GuoLei1990 Sep 5, 2025
511352c
refactor: opt code
GuoLei1990 Sep 5, 2025
a23b8d9
refactor: opt code
GuoLei1990 Sep 5, 2025
8136754
refactor: opt code
GuoLei1990 Sep 5, 2025
138f7ca
refactor: opt code
GuoLei1990 Sep 5, 2025
765cd92
refactor: opt code
GuoLei1990 Sep 5, 2025
c98a3c5
refactor: fix e2e
GuoLei1990 Sep 6, 2025
fb72f95
refactor: opt code
GuoLei1990 Sep 6, 2025
4f53ba9
refactor: opt code
GuoLei1990 Sep 6, 2025
053285f
refactor: opt code
GuoLei1990 Sep 6, 2025
e434754
refactor: opt code
GuoLei1990 Sep 6, 2025
e7d3e68
refactor: opt code
GuoLei1990 Sep 6, 2025
29ab104
refactor: opt code
GuoLei1990 Sep 6, 2025
e7a9a29
refactor: opt code
GuoLei1990 Sep 6, 2025
dd80086
refactor: opt code
GuoLei1990 Sep 6, 2025
6b8c1c2
refactor: opt code
GuoLei1990 Sep 6, 2025
bec1397
refactor: opt code
GuoLei1990 Sep 6, 2025
c39d0f6
refactor: opt code
GuoLei1990 Sep 6, 2025
f133f06
refactor: opt code
GuoLei1990 Sep 6, 2025
4c53462
refactor: opt code
GuoLei1990 Sep 6, 2025
6bde92f
refatcor: opt code
GuoLei1990 Sep 7, 2025
73237d5
refatcor: opt code
GuoLei1990 Sep 7, 2025
298c5ca
refatcor: opt code
GuoLei1990 Sep 7, 2025
2c22c0e
refatcor: opt code
GuoLei1990 Sep 7, 2025
9a6cac9
refatcor: opt code
GuoLei1990 Sep 7, 2025
95dae2a
refactor: opt code
GuoLei1990 Sep 7, 2025
eff76f7
refactor: opt code
GuoLei1990 Sep 7, 2025
b08c08e
refactor: opt code
GuoLei1990 Sep 7, 2025
5a9ce8a
refactor: opt code
GuoLei1990 Sep 7, 2025
1410816
refatcor: opt code
GuoLei1990 Sep 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions e2e/case/camera-ssao.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ import {
PBRMaterial,
PrimitiveMesh,
SkyBoxMaterial,
SSAOQuality,
AmbientOcclusionQuality,
Vector3,
WebGLEngine
WebGLEngine,
WebGLMode
} from "@galacean/engine";
import { initScreenshot, updateForE2E } from "./.mockForE2E";

Logger.enable();
WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
WebGLEngine.create({ canvas: "canvas",graphicDeviceOptions: { webGLMode: WebGLMode.WebGL1 } }).then((engine) => {
engine.canvas.resizeByClientSize(2);
const scene = engine.sceneManager.activeScene;
const rootEntity = scene.createRootEntity();
Expand All @@ -32,13 +33,13 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
cameraEntity.transform.position = new Vector3(0.8, 1, 3.5);
const camera = cameraEntity.addComponent(Camera);

scene.ssao.enabled = true;
scene.ssao.radius = 0.4;
scene.ssao.intensity = 3;
scene.ssao.power = 1.0;
scene.ssao.bias = 0.0005;
scene.ssao.bilateralThreshold = 0.01;
scene.ssao.quality = SSAOQuality.High;
scene.ambientOcclusion.enabled = true;
scene.ambientOcclusion.radius = 0.4;
scene.ambientOcclusion.intensity = 3;
scene.ambientOcclusion.power = 1.0;
scene.ambientOcclusion.bias = 0.0005;
scene.ambientOcclusion.bilateralThreshold = 0.01;
scene.ambientOcclusion.quality = AmbientOcclusionQuality.High;

const lightNode = rootEntity.createChild("light_node");
lightNode.addComponent(DirectLight).color = new Color(1, 1, 1);
Expand Down Expand Up @@ -82,6 +83,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
ambientLight.specularIntensity = 1;
})
.then(() => {
// engine.run();
updateForE2E(engine);
initScreenshot(engine, camera);
});
Expand Down
2 changes: 1 addition & 1 deletion e2e/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ export const E2E_CONFIG = {
category: "Camera",
caseFileName: "camera-ssao",
threshold: 0,
diffPercentage: 0.08
diffPercentage: 0.12
}
},
Physics: {
Expand Down
37 changes: 22 additions & 15 deletions packages/core/src/RenderPipeline/BasicRenderPipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { BackgroundTextureFillMode } from "../enums/BackgroundTextureFillMode";
import { CameraClearFlags } from "../enums/CameraClearFlags";
import { DepthTextureMode } from "../enums/DepthTextureMode";
import { ReplacementFailureStrategy } from "../enums/ReplacementFailureStrategy";
import { ScalableAmbientObscurancePass } from "../lighting/ambientOcclusion/ScalableAmbientObscurancePass";
import { FinalPass } from "../postProcess";
import { Shader } from "../shader/Shader";
import { ShaderMacroCollection } from "../shader/ShaderMacroCollection";
Expand All @@ -28,8 +29,6 @@ import { DepthOnlyPass } from "./DepthOnlyPass";
import { OpaqueTexturePass } from "./OpaqueTexturePass";
import { PipelineUtils } from "./PipelineUtils";
import { ContextRendererUpdateFlag, RenderContext } from "./RenderContext";
import { SSAOPass } from "../lighting/screenSpaceLighting/ScreenSpaceAmbientOcclusionPass";
import { ScreenSpaceAmbientOcclusion } from "../lighting/screenSpaceLighting";
import { RenderElement } from "./RenderElement";
import { SubRenderElement } from "./SubRenderElement";
import { PipelineStage } from "./enums/PipelineStage";
Expand All @@ -46,7 +45,7 @@ export class BasicRenderPipeline {
private _internalColorTarget: RenderTarget = null;
private _cascadedShadowCasterPass: CascadedShadowCasterPass;
private _depthOnlyPass: DepthOnlyPass;
private _ssaoPass: SSAOPass;
private _saoPass: ScalableAmbientObscurancePass;
private _opaqueTexturePass: OpaqueTexturePass;
private _finalPass: FinalPass;
private _copyBackgroundTexture: Texture2D;
Expand All @@ -63,7 +62,7 @@ export class BasicRenderPipeline {
this._cullingResults = new CullingResults();
this._cascadedShadowCasterPass = new CascadedShadowCasterPass(camera);
this._depthOnlyPass = new DepthOnlyPass(engine);
this._ssaoPass = new SSAOPass(engine);
this._saoPass = new ScalableAmbientObscurancePass(engine);
this._opaqueTexturePass = new OpaqueTexturePass(engine);
this._finalPass = new FinalPass(engine);
}
Expand Down Expand Up @@ -93,7 +92,15 @@ export class BasicRenderPipeline {
const cullingResults = this._cullingResults;
const sunlight = scene._lightManager._sunlight;
const depthOnlyPass = this._depthOnlyPass;
const depthPassEnabled = camera.depthTextureMode === DepthTextureMode.PrePass && depthOnlyPass._supportDepthTexture;
const ambientOcclusionEnabled = scene.ambientOcclusion._isValid();
const supportDepthTexture = depthOnlyPass.supportDepthTexture;

// Ambient occlusion enable will force enable depth prepass
if (ambientOcclusionEnabled) {
camera.depthTextureMode = DepthTextureMode.PrePass;
}

const depthPassEnabled = camera.depthTextureMode === DepthTextureMode.PrePass && supportDepthTexture;
const finalClearFlags = camera.clearFlags & ~(ignoreClear ?? CameraClearFlags.None);
const msaaSamples = renderTarget ? renderTarget.antiAliasing : camera.msaaSamples;

Expand Down Expand Up @@ -196,6 +203,16 @@ export class BasicRenderPipeline {
}
}

// Scalable ambient obscurance pass
// Before opaque pass so materials can sample ambient occlusion in BRDF
if (ambientOcclusionEnabled && supportDepthTexture) {
const saoPass = this._saoPass;
saoPass.onConfig(camera, this._depthOnlyPass.renderTarget);
saoPass.onRender(context);
} else {
this._saoPass.release();
}

this._drawRenderPass(context, camera, finalClearFlags, cubeFace, mipLevel);
}

Expand Down Expand Up @@ -265,16 +282,6 @@ export class BasicRenderPipeline {
context.setRenderTarget(colorTarget, colorViewport, mipLevel, cubeFace);
}

// Screen space ambient occlusion pass
// Before opaque pass so materials can sample ambient occlusion in BRDF
if (scene.ssao.enabled) {
camera.depthTextureMode = DepthTextureMode.PrePass;
const ssaoPass = this._ssaoPass;
ssaoPass.onConfig(camera, colorTarget);
ssaoPass.onRender(context);
context.setRenderTarget(colorTarget, colorViewport, mipLevel, cubeFace);
}

const maskManager = scene._maskManager;
if (finalClearFlags & CameraClearFlags.Stencil) {
maskManager.hasStencilWritten = false;
Expand Down
15 changes: 7 additions & 8 deletions packages/core/src/RenderPipeline/DepthOnlyPass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,12 @@ import { PipelineStage } from "./enums/PipelineStage";
* Depth only pass.
*/
export class DepthOnlyPass extends PipelinePass {
readonly _supportDepthTexture: boolean;

private _renderTarget: RenderTarget;
readonly supportDepthTexture: boolean;
renderTarget: RenderTarget;

constructor(engine: Engine) {
super(engine);
this._supportDepthTexture = engine._hardwareRenderer.canIUse(GLCapabilityType.depthTexture);
this.supportDepthTexture = engine._hardwareRenderer.canIUse(GLCapabilityType.depthTexture);
}

onConfig(camera: Camera): void {
Expand All @@ -30,7 +29,7 @@ export class DepthOnlyPass extends PipelinePass {

const renderTarget = PipelineUtils.recreateRenderTargetIfNeeded(
engine,
this._renderTarget,
this.renderTarget,
width,
height,
null,
Expand All @@ -43,12 +42,12 @@ export class DepthOnlyPass extends PipelinePass {
TextureFilterMode.Point
);

this._renderTarget = renderTarget;
this.renderTarget = renderTarget;
}

override onRender(context: RenderContext, cullingResults: CullingResults): void {
const engine = this.engine;
const renderTarget = this._renderTarget;
const renderTarget = this.renderTarget;
const camera = context.camera;
const rhi = engine._hardwareRenderer;
context.setRenderTarget(renderTarget, PipelineUtils.defaultViewport, 0);
Expand All @@ -58,6 +57,6 @@ export class DepthOnlyPass extends PipelinePass {
cullingResults.opaqueQueue.render(context, PipelineStage.DepthOnly);
cullingResults.alphaTestQueue.render(context, PipelineStage.DepthOnly);

camera.shaderData.setTexture(Camera._cameraDepthTextureProperty, this._renderTarget.depthTexture);
camera.shaderData.setTexture(Camera._cameraDepthTextureProperty, this.renderTarget.depthTexture);
}
}
2 changes: 1 addition & 1 deletion packages/core/src/RenderPipeline/PipelineUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export class PipelineUtils {
): RenderTarget {
const currentColorTexture = <Texture2D>currentRenderTarget?.getColorTexture(0);
const colorTexture =
colorFormat != undefined
colorFormat != null
? PipelineUtils.recreateTextureIfNeeded(
engine,
currentColorTexture,
Expand Down
27 changes: 9 additions & 18 deletions packages/core/src/Scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { SceneManager } from "./SceneManager";
import { EngineObject, Logger } from "./base";
import { ActiveChangeFlag } from "./enums/ActiveChangeFlag";
import { FogMode } from "./enums/FogMode";
import { DirectLight, ScreenSpaceAmbientOcclusion } from "./lighting";
import { AmbientOcclusion, DirectLight } from "./lighting";
import { AmbientLight } from "./lighting/AmbientLight";
import { LightManager } from "./lighting/LightManager";
import { PhysicsScene } from "./physics/PhysicsScene";
Expand Down Expand Up @@ -53,6 +53,14 @@ export class Scene extends EngineObject {
/** Post process manager. */
readonly postProcessManager = new PostProcessManager(this);

/**
* Ambient Occlusion settings.
* @remarks
* Darkens areas where objects are close together to simulate natural light blocking,
* such as corners, crevices, and contact points between surfaces.
*/
readonly ambientOcclusion = new AmbientOcclusion(this);

/* @internal */
_lightManager: LightManager = new LightManager();
/* @internal */
Expand All @@ -79,7 +87,6 @@ export class Scene extends EngineObject {
private _isActive: boolean = true;
private _sun: DirectLight | null;
private _enableTransparentShadow = false;
private _ssao = new ScreenSpaceAmbientOcclusion();

/**
* Whether the scene is active.
Expand Down Expand Up @@ -266,22 +273,6 @@ export class Scene extends EngineObject {
}
}

/**
* Screen Space Ambient Occlusion (SSAO) settings.
* @remarks
* Darkens areas where objects are close together to simulate natural light blocking,
* such as corners, crevices, and contact points between surfaces.
*/
get ssao(): ScreenSpaceAmbientOcclusion {
return this._ssao;
}

set ssao(value: ScreenSpaceAmbientOcclusion) {
if (this._ssao !== value) {
this._ssao = value;
}
}

/**
* Create scene.
* @param engine - Engine
Expand Down
129 changes: 129 additions & 0 deletions packages/core/src/lighting/ambientOcclusion/AmbientOcclusion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { Scene } from "../../Scene";
import { ShaderMacro } from "../../shader/ShaderMacro";
import { AmbientOcclusionQuality } from "../enums/AmbientOcclusionQuality";

/**
* Ambient Occlusion settings.
* @remarks
* Adds realistic shadows to corners, crevices, and areas where objects meet.
*/
export class AmbientOcclusion {
private static _enableMacro = ShaderMacro.getByName("SCENE_ENABLE_AMBIENT_OCCLUSION");

/**
* Controls the quality of the ambient occlusion effect.
* @remarks
* If set to `AmbientOcclusionQuality.Low`, the effect will use fewer samples for faster performance.
* If set to `AmbientOcclusionQuality.Medium`, the effect will balance quality and performance.
* If set to `AmbientOcclusionQuality.High`, the effect will use more samples for better quality, but the performance will be worse.
*/
quality = AmbientOcclusionQuality.Low;

/**
* Controls the bias to prevent self-occlusion artifacts.
* Valid range: [0.0, 0.1]
* @defaultValue 0.01
*/
bias = 0.01;

private _scene: Scene;
private _enabled = false;
private _power = 1.0;
private _bilateralThreshold = 0.05;
private _radius = 0.5;
private _intensity = 1.0;

/**
* Control whether ambient occlusion is enabled or not.
*/
get enabled(): boolean {
return this._enabled;
}

set enabled(value: boolean) {
if (this._enabled !== value) {
this._enabled = value;
this._toggleAmbientOcclusionMacro();
}
}

/**
* Controls the radius of the ambient occlusion effect.
* Higher values create larger occlusion areas.
* Valid range: [0.0, 10.0]
* @defaultValue 0.5
*/
get radius(): number {
return this._radius;
}

set radius(value: number) {
if (this._radius !== value) {
this._radius = Math.max(0.0, Math.min(10.0, value));
}
}

/**
* Controls the strength of the ambient occlusion effect.
* Valid range: [0.0, Infinity)
* @defaultValue 1.0
*/
get intensity(): number {
return this._intensity;
}

set intensity(value: number) {
if (this._intensity !== value) {
this._intensity = Math.max(0.0, value);
this._toggleAmbientOcclusionMacro();
}
}

/**
* Control the contrast of the ambient occlusion effect.
* The larger the value, the grayer the effect.
* Valid range: [0.1, 5.0]
* @defaultValue 1.0
*/
get power(): number {
return this._power;
}

set power(value: number) {
this._power = Math.max(0.1, Math.min(5.0, value));
}

/**
* Control the threshold for blurred edges.
* Smaller value that retains the edge will result in sharper edges,
* while a larger value will make the edges softer.
* Valid range: (0.000001, 1.0]
* @defaultValue 0.05
*/
get bilateralThreshold(): number {
return this._bilateralThreshold;
}

set bilateralThreshold(value: number) {
this._bilateralThreshold = Math.max(1e-6, Math.min(1.0, value));
}

constructor(scene: Scene) {
this._scene = scene;
}

/**
* @internal
*/
_isValid(): boolean {
return this._enabled && this.intensity > 0;
}

private _toggleAmbientOcclusionMacro(): void {
if (this._isValid()) {
this._scene.shaderData.enableMacro(AmbientOcclusion._enableMacro);
} else {
this._scene.shaderData.disableMacro(AmbientOcclusion._enableMacro);
}
}
}
Loading