Files
engine/tests/src/core/postProcess/PostProcess.test.ts
zhuxudong 7fae92c8d3 Refactor PostProcessParameter to strong type (#2487)
* refactor: `PostProcessParameter` to strong type
2025-01-06 16:59:31 +08:00

381 lines
11 KiB
TypeScript

import { Color } from "@galacean/engine";
import {
BloomDownScaleMode,
BloomEffect,
Camera,
Engine,
Entity,
PostProcess,
PostProcessEffect,
PostProcessEffectBoolParameter,
PostProcessEffectFloatParameter,
PostProcessPass,
RenderTarget,
Scene,
SphereColliderShape,
StaticCollider,
Texture2D,
TonemappingEffect
} from "@galacean/engine-core";
import { MathUtil } from "@galacean/engine-math";
import { PhysXPhysics } from "@galacean/engine-physics-physx";
import { WebGLEngine } from "@galacean/engine-rhi-webgl";
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
class CustomPass extends PostProcessPass {
onRender(camera: Camera, srcTexture: Texture2D, destTarget: RenderTarget): void {}
}
export class CustomEffect extends PostProcessEffect {
intensity = new PostProcessEffectFloatParameter(0, 0, 1);
}
describe("PostProcess", () => {
let engine: Engine;
let scene: Scene;
let uberPass: PostProcessPass = null;
let postEntity: Entity = null;
let camera: Camera;
beforeAll(async function () {
engine = await WebGLEngine.create({ canvas: document.createElement("canvas") });
scene = engine.sceneManager.scenes[0];
const passes = engine.postProcessPasses;
uberPass = passes[0];
const cameraEntity = scene.createRootEntity("camera");
camera = cameraEntity.addComponent(Camera);
camera.enablePostProcess = true;
});
afterAll(() => {
engine.destroy();
});
beforeEach(() => {
postEntity = scene.createRootEntity("post-process");
});
afterEach(() => {
postEntity.destroy();
});
it("Post Process isActive", () => {
const ppManager = scene.postProcessManager;
const pp = postEntity.addComponent(PostProcess);
engine.update();
// @ts-ignore
expect(ppManager._isValid()).to.false;
// Test effect
const bloomEffect = pp.addEffect(BloomEffect);
engine.update();
// @ts-ignore
expect(ppManager._isValid()).to.false;
bloomEffect.enabled = false;
engine.update();
// @ts-ignore
expect(ppManager._isValid()).to.false;
bloomEffect.enabled = true;
engine.update();
// @ts-ignore
expect(ppManager._isValid()).to.false;
bloomEffect.intensity.value = 1;
engine.update();
// @ts-ignore
expect(ppManager._isValid()).to.true;
// Test PostProcess disable
pp.enabled = false;
engine.update();
// @ts-ignore
expect(ppManager._isValid()).to.false;
pp.enabled = true;
engine.update();
// @ts-ignore
expect(ppManager._isValid()).to.true;
// Test pass isActive
uberPass.isActive = false;
engine.update();
// @ts-ignore
expect(ppManager._isValid()).to.false;
uberPass.isActive = true;
engine.update();
// @ts-ignore
expect(ppManager._isValid()).to.true;
// Test effect remove
const removedBloomEffect = pp.removeEffect(BloomEffect);
expect(removedBloomEffect).to.instanceOf(BloomEffect);
engine.update();
// @ts-ignore
expect(ppManager._isValid()).to.false;
// Test component destroy
pp.destroy();
engine.update();
// @ts-ignore
expect(ppManager._isValid()).to.false;
// Test entity destroy
{
const pp = postEntity.addComponent(PostProcess);
pp.addEffect(TonemappingEffect);
engine.update();
// @ts-ignore
expect(ppManager._isValid()).to.true;
postEntity.destroy();
engine.update();
// @ts-ignore
expect(ppManager._isValid()).to.false;
}
});
it("Post Process Effect", () => {
const pp = postEntity.addComponent(PostProcess);
expect(pp.getEffect(BloomEffect)).to.undefined;
// @ts-ignore
expect(pp._effects.length).to.equal(0);
const bloomEffect = pp.addEffect(BloomEffect);
expect(bloomEffect).to.instanceOf(BloomEffect);
const bloomEffectRepeat = pp.addEffect(BloomEffect);
expect(bloomEffectRepeat).to.undefined;
// @ts-ignore
expect(pp._effects.length).to.equal(1);
expect(pp.removeEffect(TonemappingEffect)).to.undefined;
// Test Bloom parameters
expect(bloomEffect.highQualityFiltering.value).to.false;
expect(bloomEffect.downScale.value).to.equal(BloomDownScaleMode.Half);
expect(bloomEffect.dirtTexture.value).to.null;
expect(bloomEffect.threshold.value).to.equal(0.9);
expect(bloomEffect.scatter.value).to.equal(0.7);
expect(bloomEffect.intensity.value).to.equal(0);
expect(bloomEffect.dirtIntensity.value).to.equal(0);
expect(bloomEffect.tint.value).to.include(new Color(1, 1, 1, 1));
// Test remove effect
const removedBloomEffect = pp.removeEffect(BloomEffect);
expect(removedBloomEffect).to.instanceOf(BloomEffect);
expect(removedBloomEffect).to.equal(bloomEffect);
// @ts-ignore
expect(pp._effects.length).to.equal(0);
});
it("Post Process", () => {
const ppManager = scene.postProcessManager;
// @ts-ignore
const activePostProcesses = ppManager._activePostProcesses;
expect(activePostProcesses.length).to.equal(0);
const pp1 = postEntity.addComponent(PostProcess);
const pp2 = postEntity.addComponent(PostProcess);
pp1.addEffect(BloomEffect);
expect(pp1.priority).to.equal(0);
expect(activePostProcesses.length).to.equal(2);
expect(activePostProcesses[1] === pp2).to.true;
// Test priority
pp1.priority = 10;
engine.update();
expect(activePostProcesses[1] === pp1).to.true;
pp1.enabled = false;
expect(activePostProcesses.length).to.equal(1);
pp1.enabled = true;
expect(activePostProcesses.length).to.equal(2);
});
it("Custom effect", () => {
const pp = postEntity.addComponent(PostProcess);
const customEffect = pp.addEffect(CustomEffect);
expect(customEffect).to.instanceOf(CustomEffect);
expect(customEffect.intensity.value).to.equal(0);
// Clamp
customEffect.intensity.value = 2;
expect(customEffect.intensity.value).to.equal(1);
customEffect.intensity.value = -2;
expect(customEffect.intensity.value).to.equal(0);
// isValid
expect(customEffect.isValid()).to.true;
customEffect.enabled = false;
expect(customEffect.isValid()).to.false;
});
it("Post process effect parameter", () => {
{
const p1 = new PostProcessEffectFloatParameter(1);
const p2 = new PostProcessEffectFloatParameter(2, 0, 1);
const p3 = new PostProcessEffectFloatParameter(-2, 0, 1);
const p4 = new PostProcessEffectFloatParameter(10, 0);
const p5 = new PostProcessEffectFloatParameter(-10, 0);
const p6 = new PostProcessEffectFloatParameter(0.5, 0, 1, true);
expect(p1.value).to.equal(1);
expect(p2.value).to.equal(1);
expect(p3.value).to.equal(0);
expect(p4.value).to.equal(10);
expect(p5.value).to.equal(0);
expect(p6.value).to.equal(0.5);
}
{
const p1 = new PostProcessEffectBoolParameter(false);
const p2 = new PostProcessEffectBoolParameter(true);
const p3 = new PostProcessEffectBoolParameter(true);
expect(p1.value).to.equal(false);
expect(p2.value).to.equal(true);
expect(p3.value).to.equal(true);
}
});
it("Global mode", () => {
const ppManager = scene.postProcessManager;
const pp1 = postEntity.addComponent(PostProcess);
const pp2 = postEntity.addComponent(PostProcess);
const bloom1 = pp1.addEffect(BloomEffect);
const bloom2 = pp2.addEffect(BloomEffect);
engine.update();
const bloomBlend = ppManager.getBlendEffect(BloomEffect);
expect(bloomBlend).to.instanceOf(BloomEffect);
expect(bloomBlend.intensity.value).to.equal(0);
bloom2.intensity.value = 10;
engine.update();
expect(bloomBlend.intensity.value).to.equal(10);
pp1.priority = 10;
engine.update();
expect(bloomBlend.intensity.value).to.equal(0);
pp1.enabled = false;
engine.update();
expect(bloomBlend.intensity.value).to.equal(10);
pp1.enabled = true;
engine.update();
expect(bloomBlend.intensity.value).to.equal(0);
pp2.priority = 20;
engine.update();
expect(bloomBlend.intensity.value).to.equal(10);
});
it("Local mode", async () => {
const pp1 = postEntity.addComponent(PostProcess);
expect(pp1.blendDistance).to.equal(0);
expect(pp1.isGlobal).to.equal(true);
pp1.isGlobal = false;
// Only support local PostProcess in physics enabled Scenes.
expect(pp1.isGlobal).to.equal(true);
{
const engine = await WebGLEngine.create({
canvas: document.createElement("canvas"),
physics: new PhysXPhysics()
// physics: new LitePhysics()
});
const scene = engine.sceneManager.scenes[0];
const passes = engine.postProcessPasses;
const ppManager = scene.postProcessManager;
uberPass = passes[0];
const cameraEntity = scene.createRootEntity("camera");
const camera = cameraEntity.addComponent(Camera);
camera.enablePostProcess = true;
const postEntity = scene.createRootEntity("post-process");
const pp1 = postEntity.addComponent(PostProcess);
const pp2 = postEntity.addComponent(PostProcess);
const bloom1 = pp1.addEffect(BloomEffect);
const bloom2 = pp2.addEffect(BloomEffect);
const intensity2 = 10;
pp2.priority = 10;
bloom2.intensity.value = intensity2;
engine.update();
const bloomBlend = ppManager.getBlendEffect(BloomEffect);
expect(bloomBlend.intensity.value).to.equal(10);
// Local mode
const radius = 5;
pp2.isGlobal = false;
pp2.blendDistance = radius;
const collider = postEntity.addComponent(StaticCollider);
const physicsBox = new SphereColliderShape();
physicsBox.radius = radius;
collider.addShape(physicsBox);
// Inside
cameraEntity.transform.position.set(radius / 2, 0, 0);
engine.update();
expect(bloomBlend.intensity.value).to.equal(intensity2);
// Edge
cameraEntity.transform.position.set(radius, 0, 0);
engine.update();
expect(bloomBlend.intensity.value).to.equal(intensity2);
// Outer half in blend distance
cameraEntity.transform.position.set(radius + pp2.blendDistance / 2, 0, 0);
engine.update();
expect(bloomBlend.intensity.value).to.equal(intensity2 / 2);
// Outside over blend distance
cameraEntity.transform.position.set(radius + pp2.blendDistance, 0, 0);
engine.update();
expect(bloomBlend.intensity.value).to.equal(0);
// Blend with local and global
const intensity1 = 1;
bloom1.intensity.value = intensity1;
// Inside
cameraEntity.transform.position.set(radius / 2, 0, 0);
engine.update();
expect(bloomBlend.intensity.value).to.equal(MathUtil.lerp(intensity1, intensity2, 1));
// Edge
cameraEntity.transform.position.set(radius, 0, 0);
engine.update();
expect(bloomBlend.intensity.value).to.equal(MathUtil.lerp(intensity1, intensity2, 1));
// Outer half in blend distance
cameraEntity.transform.position.set(radius + pp2.blendDistance / 2, 0, 0);
engine.update();
expect(bloomBlend.intensity.value).to.equal(MathUtil.lerp(intensity1, intensity2, 0.5));
// Outside over blend distance
cameraEntity.transform.position.set(radius + pp2.blendDistance, 0, 0);
engine.update();
expect(bloomBlend.intensity.value).to.equal(MathUtil.lerp(intensity1, intensity2, 0));
engine.destroy();
}
});
});