Files
cocos-engine/cocos/rendering/post-process/post-process-builder.ts
2023-09-06 09:55:02 +08:00

252 lines
8.4 KiB
TypeScript

import { EDITOR } from 'internal:constants';
import { Camera, CameraProjection, CameraUsage } from '../../render-scene/scene';
import { PipelineBuilder, Pipeline } from '../custom/pipeline';
import { passContext } from './utils/pass-context';
import { ForwardFinalPass } from './passes/forward-final-pass';
import { buildReflectionProbePasss, getCameraUniqueID } from '../custom/define';
import { BasePass } from './passes/base-pass';
import { ForwardPass } from './passes/forward-pass';
import { TAAPass } from './passes/taa-pass';
import { FSRPass } from './passes/fsr-pass';
import { BlitScreenPass } from './passes/blit-screen-pass';
import { ShadowPass } from './passes/shadow-pass';
import { HBAOPass } from './passes/hbao-pass';
import { PostProcess } from './components/post-process';
import { director } from '../../game';
import { Camera as CameraComponent } from '../../misc';
import { BloomPass, ColorGradingPass, FloatOutputProcessPass, ForwardTransparencyPass,
ForwardTransparencySimplePass, FxaaPass, PostFinalPass, SkinPass } from './passes';
import { PipelineEventType } from '../pipeline-event';
export class PostProcessBuilder implements PipelineBuilder {
pipelines: Map<string, BasePass[]> = new Map();
constructor () {
this.init();
}
onGlobalPipelineStateChanged (): void {
const passes = this.pipelines.get('forward');
if (passes !== undefined) {
for (let i = 0; i < passes.length; i++) {
const pass = passes[i];
if (typeof pass.onGlobalPipelineStateChanged === 'function') {
pass.onGlobalPipelineStateChanged();
}
}
}
}
init (): void {
const forward = new ForwardPass();
const forwardFinal = new ForwardFinalPass();
const shadowPass = new ShadowPass();
// default pipeline
this.addPass(shadowPass, 'default');
this.addPass(forward, 'default');
this.addPass(new ForwardTransparencySimplePass(), 'default');
this.addPass(forwardFinal, 'default');
// rendering dependent data generation
this.addPass(shadowPass);
// opaque objects forward lighting
this.addPass(forward);
this.addPass(new SkinPass());
// depth-based shading
this.addPass(new HBAOPass());
// float output related deferred processing: hdr + fog
this.addPass(new FloatOutputProcessPass());
// transparency should after hdr and depth-based shading
// temporary ignore CC_USE_FLOAT_OUTPUT
this.addPass(new ForwardTransparencyPass());
// user post-processing
this.addPass(new TAAPass());
this.addPass(new FxaaPass());
this.addPass(new ColorGradingPass());
this.addPass(new BlitScreenPass());
this.addPass(new BloomPass());
// final output
this.addPass(new FSRPass()); // fsr should be final
this.addPass(new PostFinalPass());
}
getPass (passClass: typeof BasePass, pipelineName = 'forward'): BasePass | undefined {
const pp = this.pipelines.get(pipelineName);
return pp && pp.find((p): boolean => p instanceof passClass);
}
addPass (pass: BasePass, pipelineName = 'forward'): void {
let pp = this.pipelines.get(pipelineName);
if (!pp) {
pp = [];
this.pipelines.set(pipelineName, pp);
}
const oldIdx = pp.findIndex((p): boolean => p.name === pass.name);
if (oldIdx !== -1) {
pp.splice(oldIdx, 1);
}
pp.push(pass);
}
insertPass (pass: BasePass, passClass: typeof BasePass, pipelineName = 'forward'): void {
const pp = this.pipelines.get(pipelineName);
if (pp) {
const oldIdx = pp.findIndex((p): boolean => p.name === pass.name);
if (oldIdx !== -1) {
pp.splice(oldIdx, 1);
}
const idx = pp.findIndex((p): boolean => p instanceof passClass);
if (idx !== -1) {
pp.splice(idx + 1, 0, pass);
}
}
}
private initEditor (): void {
director.root!.cameraList.forEach((cam): void => {
if (cam.name === 'Editor Camera') {
cam.usePostProcess = cam.projectionType === CameraProjection.PERSPECTIVE;
}
});
}
private applyPreviewCamera (camera: Camera): void {
if (!camera.node.parent) return;
const camComp = camera.node.parent.getComponent(CameraComponent);
const oriCamera = camComp && camComp.camera;
if (oriCamera) {
camera.postProcess = oriCamera.postProcess;
camera.usePostProcess = oriCamera.usePostProcess;
}
}
private resortEditorCameras (cameras: Camera[]): Camera[] {
const newCameras: Camera[] = [];
for (let i = 0; i < cameras.length; i++) {
const c = cameras[i];
if (c.name === 'Editor Camera'
|| c.name === 'Editor UIGizmoCamera'
|| c.name === 'Scene Gizmo Camera') {
newCameras.push(c);
}
}
for (let i = 0; i < cameras.length; i++) {
const c = cameras[i];
if (newCameras.indexOf(c) === -1) {
newCameras.push(c);
}
}
return newCameras;
}
setup (cameras: Camera[], ppl: Pipeline): void {
if (EDITOR) {
this.initEditor();
cameras = this.resortEditorCameras(cameras);
}
passContext.ppl = ppl;
passContext.shadowPass = undefined;
passContext.forwardPass = undefined;
passContext.depthSlotName = '';
passContext.isFinalCamera = false;
passContext.isFinalPass = false;
let globalPP: PostProcess | undefined;
for (let i = 0; i < PostProcess.all.length; i++) {
const pp = PostProcess.all[i];
if (pp.global) {
globalPP = pp;
}
}
for (let i = 0; i < cameras.length; i++) {
const camera = cameras[i];
if (!camera.scene) {
continue;
}
ppl.update(camera);
if (i === (cameras.length - 1)) {
passContext.isFinalCamera = true;
}
if (EDITOR && camera.cameraUsage === CameraUsage.PREVIEW) {
this.applyPreviewCamera(camera);
}
buildReflectionProbePasss(camera, ppl);
passContext.postProcess = camera.postProcess || globalPP;
director.root!.pipelineEvent.emit(PipelineEventType.RENDER_CAMERA_BEGIN, camera);
this.renderCamera(camera, ppl);
}
}
getCameraPipelineName (camera: Camera): string {
let pipelineName = camera.pipeline;
if (!pipelineName && camera.usePostProcess) {
pipelineName = 'forward';
} else {
pipelineName = 'default';
}
return pipelineName;
}
getCameraPasses (camera: Camera): BasePass[] {
const pipelineName = this.getCameraPipelineName(camera);
return this.pipelines.get(pipelineName) || [];
}
renderCamera (camera: Camera, ppl: Pipeline): void {
passContext.passPathName = `${getCameraUniqueID(camera)}`;
passContext.camera = camera;
passContext.updateViewPort();
const passes = this.getCameraPasses(camera);
const taaPass = passes.find((p): boolean => p instanceof TAAPass) as TAAPass;
if (taaPass && taaPass.checkEnable(camera)) {
taaPass.applyCameraJitter(camera);
taaPass.updateSample();
}
const floatOutputPass = passes.find((p): boolean => p instanceof FloatOutputProcessPass) as FloatOutputProcessPass;
let lastPass: BasePass | undefined;
for (let i = 0; i < passes.length; i++) {
const pass = passes[i];
if (!pass.checkEnable(camera)) {
continue;
}
if (i === (passes.length - 1)) {
passContext.isFinalPass = true;
}
if (pass.name === 'BloomPass') {
// for override post-process builder
(pass as BloomPass).hdrInputName = (floatOutputPass === undefined || floatOutputPass === null)
? '' : floatOutputPass.getHDRInputName();
}
pass.lastPass = lastPass;
pass.render(camera, ppl);
lastPass = pass;
}
}
}