| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045 |
- /*
- Copyright (c) 2021-2024 Xiamen Yaji Software Co., Ltd.
- https://www.cocos.com/
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights to
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- of the Software, and to permit persons to whom the Software is furnished to do so,
- subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- */
- import {
- assert, cclegacy, clamp, geometry, gfx, Layers, Material, pipeline,
- PipelineEventProcessor, PipelineEventType, ReflectionProbeManager, renderer,
- rendering, sys, Vec2, Vec3, Vec4, warn,
- } from 'cc';
- import { DEBUG, EDITOR } from 'cc/env';
- import {
- BloomType,
- makePipelineSettings,
- PipelineSettings,
- } from './builtin-pipeline-types';
- const { AABB, Sphere, intersect } = geometry;
- const { ClearFlagBit, Color, Format, FormatFeatureBit, LoadOp, StoreOp, TextureType, Viewport } = gfx;
- const { scene } = renderer;
- const { CameraUsage, CSMLevel, LightType } = scene;
- function forwardNeedClearColor(camera: renderer.scene.Camera): boolean {
- return !!(camera.clearFlag & (ClearFlagBit.COLOR | (ClearFlagBit.STENCIL << 1)));
- }
- function getCsmMainLightViewport(
- light: renderer.scene.DirectionalLight,
- w: number,
- h: number,
- level: number,
- vp: gfx.Viewport,
- screenSpaceSignY: number,
- ): void {
- if (light.shadowFixedArea || light.csmLevel === CSMLevel.LEVEL_1) {
- vp.left = 0;
- vp.top = 0;
- vp.width = Math.trunc(w);
- vp.height = Math.trunc(h);
- } else {
- vp.left = Math.trunc(level % 2 * 0.5 * w);
- if (screenSpaceSignY > 0) {
- vp.top = Math.trunc((1 - Math.floor(level / 2)) * 0.5 * h);
- } else {
- vp.top = Math.trunc(Math.floor(level / 2) * 0.5 * h);
- }
- vp.width = Math.trunc(0.5 * w);
- vp.height = Math.trunc(0.5 * h);
- }
- vp.left = Math.max(0, vp.left);
- vp.top = Math.max(0, vp.top);
- vp.width = Math.max(1, vp.width);
- vp.height = Math.max(1, vp.height);
- }
- export class PipelineConfigs {
- isWeb = false;
- isWebGL1 = false;
- isWebGPU = false;
- isMobile = false;
- isHDR = false;
- useFloatOutput = false;
- toneMappingType = 0; // 0: ACES, 1: None
- shadowEnabled = false;
- shadowMapFormat = Format.R32F;
- shadowMapSize = new Vec2(1, 1);
- usePlanarShadow = false;
- screenSpaceSignY = 1;
- supportDepthSample = false;
- mobileMaxSpotLightShadowMaps = 1;
- platform = new Vec4(0, 0, 0, 0);
- }
- function setupPipelineConfigs(
- ppl: rendering.BasicPipeline,
- configs: PipelineConfigs,
- ): void {
- const sampleFeature = FormatFeatureBit.SAMPLED_TEXTURE | FormatFeatureBit.LINEAR_FILTER;
- const device = ppl.device;
- // Platform
- configs.isWeb = !sys.isNative;
- configs.isWebGL1 = device.gfxAPI === gfx.API.WEBGL;
- configs.isWebGPU = device.gfxAPI === gfx.API.WEBGPU;
- configs.isMobile = sys.isMobile;
- // Rendering
- configs.isHDR = ppl.pipelineSceneData.isHDR; // Has tone mapping
- configs.useFloatOutput = ppl.getMacroBool('CC_USE_FLOAT_OUTPUT');
- configs.toneMappingType = ppl.pipelineSceneData.postSettings.toneMappingType;
- // Shadow
- const shadowInfo = ppl.pipelineSceneData.shadows;
- configs.shadowEnabled = shadowInfo.enabled;
- configs.shadowMapFormat = pipeline.supportsR32FloatTexture(ppl.device) ? Format.R32F : Format.RGBA8;
- configs.shadowMapSize.set(shadowInfo.size);
- configs.usePlanarShadow = shadowInfo.enabled && shadowInfo.type === renderer.scene.ShadowType.Planar;
- // Device
- configs.screenSpaceSignY = ppl.device.capabilities.screenSpaceSignY;
- configs.supportDepthSample = (ppl.device.getFormatFeatures(Format.DEPTH_STENCIL) & sampleFeature) === sampleFeature;
- // Constants
- const screenSpaceSignY = device.capabilities.screenSpaceSignY;
- configs.platform.x = configs.isMobile ? 1.0 : 0.0;
- configs.platform.w = (screenSpaceSignY * 0.5 + 0.5) << 1 | (device.capabilities.clipSpaceSignY * 0.5 + 0.5);
- }
- export interface PipelineSettings2 extends PipelineSettings {
- _passes?: rendering.PipelinePassBuilder[];
- }
- const defaultSettings = makePipelineSettings();
- export class CameraConfigs {
- settings: PipelineSettings = defaultSettings;
- // Window
- isMainGameWindow = false;
- renderWindowId = 0;
- // Camera
- colorName = '';
- depthStencilName = '';
- // Pipeline
- enableFullPipeline = false;
- enableProfiler = false;
- remainingPasses = 0;
- // Shading Scale
- enableShadingScale = false;
- shadingScale = 1.0;
- nativeWidth = 1;
- nativeHeight = 1;
- width = 1; // Scaled width
- height = 1; // Scaled height
- // Radiance
- enableHDR = false;
- radianceFormat = gfx.Format.RGBA8;
- // Tone Mapping
- copyAndTonemapMaterial: Material | null = null;
- // Depth
- /** @en mutable */
- enableStoreSceneDepth = false;
- }
- const sClearColorTransparentBlack = new Color(0, 0, 0, 0);
- function sortPipelinePassBuildersByConfigOrder(passBuilders: rendering.PipelinePassBuilder[]): void {
- passBuilders.sort((a, b) => {
- return a.getConfigOrder() - b.getConfigOrder();
- });
- }
- function sortPipelinePassBuildersByRenderOrder(passBuilders: rendering.PipelinePassBuilder[]): void {
- passBuilders.sort((a, b) => {
- return a.getRenderOrder() - b.getRenderOrder();
- });
- }
- function addCopyToScreenPass(
- ppl: rendering.BasicPipeline,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: CameraConfigs,
- input: string,
- ): rendering.BasicRenderPassBuilder {
- assert(!!cameraConfigs.copyAndTonemapMaterial);
- const pass = ppl.addRenderPass(
- cameraConfigs.nativeWidth,
- cameraConfigs.nativeHeight,
- 'cc-tone-mapping');
- pass.addRenderTarget(
- cameraConfigs.colorName,
- LoadOp.CLEAR, StoreOp.STORE,
- sClearColorTransparentBlack);
- pass.addTexture(input, 'inputTexture');
- pass.addQueue(rendering.QueueHint.OPAQUE)
- .addFullscreenQuad(cameraConfigs.copyAndTonemapMaterial, 1);
- return pass;
- }
- export function getPingPongRenderTarget(prevName: string, prefix: string, id: number): string {
- if (prevName.startsWith(prefix)) {
- return `${prefix}${1 - Number(prevName.charAt(prefix.length))}_${id}`;
- } else {
- return `${prefix}0_${id}`;
- }
- }
- export interface PipelineContext {
- colorName: string;
- depthStencilName: string;
- }
- class ForwardLighting {
- // Active lights
- private readonly lights: renderer.scene.Light[] = [];
- // Active spot lights with shadows (Mutually exclusive with `lights`)
- private readonly shadowEnabledSpotLights: renderer.scene.SpotLight[] = [];
- // Internal cached resources
- private readonly _sphere = Sphere.create(0, 0, 0, 1);
- private readonly _boundingBox = new AABB();
- private readonly _rangedDirLightBoundingBox = new AABB(0.0, 0.0, 0.0, 0.5, 0.5, 0.5);
- // ----------------------------------------------------------------
- // Interface
- // ----------------------------------------------------------------
- public cullLights(scene: renderer.RenderScene, frustum: geometry.Frustum, cameraPos?: Vec3): void {
- // TODO(zhouzhenglong): Make light culling native
- this.lights.length = 0;
- this.shadowEnabledSpotLights.length = 0;
- // spot lights
- for (const light of scene.spotLights) {
- if (light.baked) {
- continue;
- }
- Sphere.set(this._sphere, light.position.x, light.position.y, light.position.z, light.range);
- if (intersect.sphereFrustum(this._sphere, frustum)) {
- if (light.shadowEnabled) {
- this.shadowEnabledSpotLights.push(light);
- } else {
- this.lights.push(light);
- }
- }
- }
- // sphere lights
- for (const light of scene.sphereLights) {
- if (light.baked) {
- continue;
- }
- Sphere.set(this._sphere, light.position.x, light.position.y, light.position.z, light.range);
- if (intersect.sphereFrustum(this._sphere, frustum)) {
- this.lights.push(light);
- }
- }
- // point lights
- for (const light of scene.pointLights) {
- if (light.baked) {
- continue;
- }
- Sphere.set(this._sphere, light.position.x, light.position.y, light.position.z, light.range);
- if (intersect.sphereFrustum(this._sphere, frustum)) {
- this.lights.push(light);
- }
- }
- // ranged dir lights
- for (const light of scene.rangedDirLights) {
- AABB.transform(this._boundingBox, this._rangedDirLightBoundingBox, light.node!.getWorldMatrix());
- if (intersect.aabbFrustum(this._boundingBox, frustum)) {
- this.lights.push(light);
- }
- }
- if (cameraPos) {
- this.shadowEnabledSpotLights.sort(
- (lhs, rhs) => Vec3.squaredDistance(cameraPos, lhs.position) - Vec3.squaredDistance(cameraPos, rhs.position),
- );
- }
- }
- private _addLightQueues(camera: renderer.scene.Camera, pass: rendering.BasicRenderPassBuilder): void {
- for (const light of this.lights) {
- const queue = pass.addQueue(rendering.QueueHint.BLEND, 'forward-add');
- switch (light.type) {
- case LightType.SPHERE:
- queue.name = 'sphere-light';
- break;
- case LightType.SPOT:
- queue.name = 'spot-light';
- break;
- case LightType.POINT:
- queue.name = 'point-light';
- break;
- case LightType.RANGED_DIRECTIONAL:
- queue.name = 'ranged-directional-light';
- break;
- default:
- queue.name = 'unknown-light';
- }
- queue.addScene(
- camera,
- rendering.SceneFlags.BLEND,
- light,
- );
- }
- }
- public addSpotlightShadowPasses(
- ppl: rendering.BasicPipeline,
- camera: renderer.scene.Camera,
- maxNumShadowMaps: number,
- ): void {
- let i = 0;
- for (const light of this.shadowEnabledSpotLights) {
- const shadowMapSize = ppl.pipelineSceneData.shadows.size;
- const shadowPass = ppl.addRenderPass(shadowMapSize.x, shadowMapSize.y, 'default');
- shadowPass.name = `SpotLightShadowPass${i}`;
- shadowPass.addRenderTarget(`SpotShadowMap${i}`, LoadOp.CLEAR, StoreOp.STORE, new Color(1, 1, 1, 1));
- shadowPass.addDepthStencil(`SpotShadowDepth${i}`, LoadOp.CLEAR, StoreOp.DISCARD);
- shadowPass.addQueue(rendering.QueueHint.NONE, 'shadow-caster')
- .addScene(camera, rendering.SceneFlags.OPAQUE | rendering.SceneFlags.MASK | rendering.SceneFlags.SHADOW_CASTER)
- .useLightFrustum(light);
- ++i;
- if (i >= maxNumShadowMaps) {
- break;
- }
- }
- }
- public addLightQueues(pass: rendering.BasicRenderPassBuilder,
- camera: renderer.scene.Camera, maxNumShadowMaps: number): void {
- this._addLightQueues(camera, pass);
- let i = 0;
- for (const light of this.shadowEnabledSpotLights) {
- // Add spot-light pass
- // Save last RenderPass to the `pass` variable
- // TODO(zhouzhenglong): Fix per queue addTexture
- pass.addTexture(`SpotShadowMap${i}`, 'cc_spotShadowMap');
- const queue = pass.addQueue(rendering.QueueHint.BLEND, 'forward-add');
- queue.addScene(camera, rendering.SceneFlags.BLEND, light);
- ++i;
- if (i >= maxNumShadowMaps) {
- break;
- }
- }
- }
- // Notice: ForwardLighting cannot handle a lot of lights.
- // If there are too many lights, the performance will be very poor.
- // If many lights are needed, please implement a forward+ or deferred rendering pipeline.
- public addLightPasses(
- colorName: string,
- depthStencilName: string,
- depthStencilStoreOp: gfx.StoreOp,
- id: number, // window id
- width: number,
- height: number,
- camera: renderer.scene.Camera,
- viewport: gfx.Viewport,
- ppl: rendering.BasicPipeline,
- pass: rendering.BasicRenderPassBuilder,
- ): rendering.BasicRenderPassBuilder {
- this._addLightQueues(camera, pass);
- let count = 0;
- const shadowMapSize = ppl.pipelineSceneData.shadows.size;
- for (const light of this.shadowEnabledSpotLights) {
- const shadowPass = ppl.addRenderPass(shadowMapSize.x, shadowMapSize.y, 'default');
- shadowPass.name = 'SpotlightShadowPass';
- // Reuse csm shadow map
- shadowPass.addRenderTarget(`ShadowMap${id}`, LoadOp.CLEAR, StoreOp.STORE, new Color(1, 1, 1, 1));
- shadowPass.addDepthStencil(`ShadowDepth${id}`, LoadOp.CLEAR, StoreOp.DISCARD);
- shadowPass.addQueue(rendering.QueueHint.NONE, 'shadow-caster')
- .addScene(camera, rendering.SceneFlags.OPAQUE | rendering.SceneFlags.MASK | rendering.SceneFlags.SHADOW_CASTER)
- .useLightFrustum(light);
- // Add spot-light pass
- // Save last RenderPass to the `pass` variable
- ++count;
- const storeOp = count === this.shadowEnabledSpotLights.length
- ? depthStencilStoreOp
- : StoreOp.STORE;
- pass = ppl.addRenderPass(width, height, 'default');
- pass.name = 'SpotlightWithShadowMap';
- pass.setViewport(viewport);
- pass.addRenderTarget(colorName, LoadOp.LOAD);
- pass.addDepthStencil(depthStencilName, LoadOp.LOAD, storeOp);
- pass.addTexture(`ShadowMap${id}`, 'cc_spotShadowMap');
- const queue = pass.addQueue(rendering.QueueHint.BLEND, 'forward-add');
- queue.addScene(
- camera,
- rendering.SceneFlags.BLEND,
- light,
- );
- }
- return pass;
- }
- public isMultipleLightPassesNeeded(): boolean {
- return this.shadowEnabledSpotLights.length > 0;
- }
- }
- export interface ForwardPassConfigs {
- enableMainLightShadowMap: boolean;
- enableMainLightPlanarShadowMap: boolean;
- enablePlanarReflectionProbe: boolean;
- enableMSAA: boolean;
- enableSingleForwardPass: boolean;
- }
- export class BuiltinForwardPassBuilder implements rendering.PipelinePassBuilder {
- static ConfigOrder = 100;
- static RenderOrder = 100;
- getConfigOrder(): number {
- return BuiltinForwardPassBuilder.ConfigOrder;
- }
- getRenderOrder(): number {
- return BuiltinForwardPassBuilder.RenderOrder;
- }
- configCamera(
- camera: Readonly<renderer.scene.Camera>,
- pipelineConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: CameraConfigs & ForwardPassConfigs): void {
- // Shadow
- cameraConfigs.enableMainLightShadowMap = pipelineConfigs.shadowEnabled
- && !pipelineConfigs.usePlanarShadow
- && !!camera.scene
- && !!camera.scene.mainLight
- && camera.scene.mainLight.shadowEnabled;
- cameraConfigs.enableMainLightPlanarShadowMap = pipelineConfigs.shadowEnabled
- && pipelineConfigs.usePlanarShadow
- && !!camera.scene
- && !!camera.scene.mainLight
- && camera.scene.mainLight.shadowEnabled;
- // Reflection Probe
- cameraConfigs.enablePlanarReflectionProbe = cameraConfigs.isMainGameWindow
- || camera.cameraUsage === CameraUsage.SCENE_VIEW
- || camera.cameraUsage === CameraUsage.GAME_VIEW;
- // MSAA
- cameraConfigs.enableMSAA = cameraConfigs.settings.msaa.enabled
- && !cameraConfigs.enableStoreSceneDepth // Cannot store MS depth, resolve depth is also not cross-platform
- && !pipelineConfigs.isWeb // TODO(zhouzhenglong): remove this constraint
- && !pipelineConfigs.isWebGL1;
- // Forward rendering (Depend on MSAA and TBR)
- cameraConfigs.enableSingleForwardPass
- = pipelineConfigs.isMobile || cameraConfigs.enableMSAA;
- ++cameraConfigs.remainingPasses;
- }
- windowResize(
- ppl: rendering.BasicPipeline,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: Readonly<CameraConfigs & ForwardPassConfigs>,
- window: renderer.RenderWindow,
- camera: renderer.scene.Camera,
- nativeWidth: number,
- nativeHeight: number): void {
- const ResourceFlags = rendering.ResourceFlags;
- const ResourceResidency = rendering.ResourceResidency;
- const id = window.renderWindowId;
- const settings = cameraConfigs.settings;
- const width = cameraConfigs.enableShadingScale
- ? Math.max(Math.floor(nativeWidth * cameraConfigs.shadingScale), 1)
- : nativeWidth;
- const height = cameraConfigs.enableShadingScale
- ? Math.max(Math.floor(nativeHeight * cameraConfigs.shadingScale), 1)
- : nativeHeight;
- // MsaaRadiance
- if (cameraConfigs.enableMSAA) {
- // Notice: We never store multisample results.
- // These samples are always resolved and discarded at the end of the render pass.
- // So the ResourceResidency should be MEMORYLESS.
- if (cameraConfigs.enableHDR) {
- ppl.addTexture(`MsaaRadiance${id}`, TextureType.TEX2D, cameraConfigs.radianceFormat, width, height, 1, 1, 1,
- settings.msaa.sampleCount, ResourceFlags.COLOR_ATTACHMENT, ResourceResidency.MEMORYLESS);
- } else {
- ppl.addTexture(`MsaaRadiance${id}`, TextureType.TEX2D, Format.RGBA8, width, height, 1, 1, 1,
- settings.msaa.sampleCount, ResourceFlags.COLOR_ATTACHMENT, ResourceResidency.MEMORYLESS);
- }
- ppl.addTexture(`MsaaDepthStencil${id}`, TextureType.TEX2D, Format.DEPTH_STENCIL, width, height, 1, 1, 1,
- settings.msaa.sampleCount, ResourceFlags.DEPTH_STENCIL_ATTACHMENT, ResourceResidency.MEMORYLESS);
- }
- // Mainlight ShadowMap
- ppl.addRenderTarget(
- `ShadowMap${id}`,
- pplConfigs.shadowMapFormat,
- pplConfigs.shadowMapSize.x,
- pplConfigs.shadowMapSize.y,
- );
- ppl.addDepthStencil(
- `ShadowDepth${id}`,
- Format.DEPTH_STENCIL,
- pplConfigs.shadowMapSize.x,
- pplConfigs.shadowMapSize.y,
- );
- // Spot-light shadow maps
- if (cameraConfigs.enableSingleForwardPass) {
- const count = pplConfigs.mobileMaxSpotLightShadowMaps;
- for (let i = 0; i !== count; ++i) {
- ppl.addRenderTarget(
- `SpotShadowMap${i}`,
- pplConfigs.shadowMapFormat,
- pplConfigs.shadowMapSize.x,
- pplConfigs.shadowMapSize.y,
- );
- ppl.addDepthStencil(
- `SpotShadowDepth${i}`,
- Format.DEPTH_STENCIL,
- pplConfigs.shadowMapSize.x,
- pplConfigs.shadowMapSize.y,
- );
- }
- }
- }
- setup(
- ppl: rendering.BasicPipeline,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: CameraConfigs & ForwardPassConfigs,
- camera: renderer.scene.Camera,
- context: PipelineContext): rendering.BasicRenderPassBuilder | undefined {
- // Add global constants
- ppl.setVec4('g_platform', pplConfigs.platform);
- const id = camera.window.renderWindowId;
- const scene = camera.scene!;
- const mainLight = scene.mainLight;
- --cameraConfigs.remainingPasses;
- assert(cameraConfigs.remainingPasses >= 0);
- // Forward Lighting (Light Culling)
- this.forwardLighting.cullLights(scene, camera.frustum);
- // Main Directional light CSM Shadow Map
- if (cameraConfigs.enableMainLightShadowMap) {
- assert(!!mainLight);
- this._addCascadedShadowMapPass(ppl, pplConfigs, id, mainLight, camera);
- }
- // Spot light shadow maps (Mobile or MSAA)
- if (cameraConfigs.enableSingleForwardPass) {
- // Currently, only support 1 spot light with shadow map on mobile platform.
- // TODO(zhouzhenglong): Relex this limitation.
- this.forwardLighting.addSpotlightShadowPasses(
- ppl, camera, pplConfigs.mobileMaxSpotLightShadowMaps);
- }
- this._tryAddReflectionProbePasses(ppl, cameraConfigs, id, mainLight, camera.scene);
- if (cameraConfigs.remainingPasses > 0 || cameraConfigs.enableShadingScale) {
- context.colorName = cameraConfigs.enableShadingScale
- ? `ScaledRadiance0_${id}`
- : `Radiance0_${id}`;
- context.depthStencilName = cameraConfigs.enableShadingScale
- ? `ScaledSceneDepth_${id}`
- : `SceneDepth_${id}`;
- } else {
- context.colorName = cameraConfigs.colorName;
- context.depthStencilName = cameraConfigs.depthStencilName;
- }
- const pass = this._addForwardRadiancePasses(
- ppl, pplConfigs, cameraConfigs, id, camera,
- cameraConfigs.width, cameraConfigs.height, mainLight,
- context.colorName, context.depthStencilName,
- !cameraConfigs.enableMSAA,
- cameraConfigs.enableStoreSceneDepth ? StoreOp.STORE : StoreOp.DISCARD);
- if (!cameraConfigs.enableStoreSceneDepth) {
- context.depthStencilName = '';
- }
- if (cameraConfigs.remainingPasses === 0 && cameraConfigs.enableShadingScale) {
- return addCopyToScreenPass(ppl, pplConfigs, cameraConfigs, context.colorName);
- } else {
- return pass;
- }
- }
- private _addCascadedShadowMapPass(
- ppl: rendering.BasicPipeline,
- pplConfigs: Readonly<PipelineConfigs>,
- id: number,
- light: renderer.scene.DirectionalLight,
- camera: renderer.scene.Camera,
- ): void {
- const QueueHint = rendering.QueueHint;
- const SceneFlags = rendering.SceneFlags;
- // ----------------------------------------------------------------
- // Dynamic states
- // ----------------------------------------------------------------
- const shadowSize = ppl.pipelineSceneData.shadows.size;
- const width = shadowSize.x;
- const height = shadowSize.y;
- const viewport = this._viewport;
- viewport.left = viewport.top = 0;
- viewport.width = width;
- viewport.height = height;
- // ----------------------------------------------------------------
- // CSM Shadow Map
- // ----------------------------------------------------------------
- const pass = ppl.addRenderPass(width, height, 'default');
- pass.name = 'CascadedShadowMap';
- pass.addRenderTarget(`ShadowMap${id}`, LoadOp.CLEAR, StoreOp.STORE, new Color(1, 1, 1, 1));
- pass.addDepthStencil(`ShadowDepth${id}`, LoadOp.CLEAR, StoreOp.DISCARD);
- const csmLevel = ppl.pipelineSceneData.csmSupported ? light.csmLevel : 1;
- // Add shadow map viewports
- for (let level = 0; level !== csmLevel; ++level) {
- getCsmMainLightViewport(light, width, height, level, this._viewport, pplConfigs.screenSpaceSignY);
- const queue = pass.addQueue(QueueHint.NONE, 'shadow-caster');
- if (!pplConfigs.isWebGPU) { // Temporary workaround for WebGPU
- queue.setViewport(this._viewport);
- }
- queue
- .addScene(camera, SceneFlags.OPAQUE | SceneFlags.MASK | SceneFlags.SHADOW_CASTER)
- .useLightFrustum(light, level);
- }
- }
- private _tryAddReflectionProbePasses(
- ppl: rendering.BasicPipeline,
- cameraConfigs: Readonly<CameraConfigs & ForwardPassConfigs>,
- id: number,
- mainLight: renderer.scene.DirectionalLight | null,
- scene: renderer.RenderScene | null,
- ): void {
- const reflectionProbeManager = cclegacy.internal.reflectionProbeManager as ReflectionProbeManager | undefined;
- if (!reflectionProbeManager) {
- return;
- }
- const ResourceResidency = rendering.ResourceResidency;
- const probes = reflectionProbeManager.getProbes();
- const maxProbeCount = 4;
- let probeID = 0;
- for (const probe of probes) {
- if (!probe.needRender) {
- continue;
- }
- const area = probe.renderArea();
- const width = Math.max(Math.floor(area.x), 1);
- const height = Math.max(Math.floor(area.y), 1);
- if (probe.probeType === renderer.scene.ProbeType.PLANAR) {
- if (!cameraConfigs.enablePlanarReflectionProbe) {
- continue;
- }
- const window: renderer.RenderWindow = probe.realtimePlanarTexture!.window!;
- const colorName = `PlanarProbeRT${probeID}`;
- const depthStencilName = `PlanarProbeDS${probeID}`;
- // ProbeResource
- ppl.addRenderWindow(colorName,
- cameraConfigs.radianceFormat, width, height, window);
- ppl.addDepthStencil(depthStencilName,
- gfx.Format.DEPTH_STENCIL, width, height, ResourceResidency.MEMORYLESS);
- // Rendering
- const probePass = ppl.addRenderPass(width, height, 'default');
- probePass.name = `PlanarReflectionProbe${probeID}`;
- this._buildReflectionProbePass(probePass, cameraConfigs, id, probe.camera,
- colorName, depthStencilName, mainLight, scene);
- } else if (EDITOR) {
- for (let faceIdx = 0; faceIdx < probe.bakedCubeTextures.length; faceIdx++) {
- probe.updateCameraDir(faceIdx);
- const window: renderer.RenderWindow = probe.bakedCubeTextures[faceIdx].window!;
- const colorName = `CubeProbeRT${probeID}${faceIdx}`;
- const depthStencilName = `CubeProbeDS${probeID}${faceIdx}`;
- // ProbeResource
- ppl.addRenderWindow(colorName,
- cameraConfigs.radianceFormat, width, height, window);
- ppl.addDepthStencil(depthStencilName,
- gfx.Format.DEPTH_STENCIL, width, height, ResourceResidency.MEMORYLESS);
- // Rendering
- const probePass = ppl.addRenderPass(width, height, 'default');
- probePass.name = `CubeProbe${probeID}${faceIdx}`;
- this._buildReflectionProbePass(probePass, cameraConfigs, id, probe.camera,
- colorName, depthStencilName, mainLight, scene);
- }
- probe.needRender = false;
- }
- ++probeID;
- if (probeID === maxProbeCount) {
- break;
- }
- }
- }
- private _buildReflectionProbePass(
- pass: rendering.BasicRenderPassBuilder,
- cameraConfigs: Readonly<CameraConfigs & ForwardPassConfigs>,
- id: number,
- camera: renderer.scene.Camera,
- colorName: string,
- depthStencilName: string,
- mainLight: renderer.scene.DirectionalLight | null,
- scene: renderer.RenderScene | null = null,
- ): void {
- const QueueHint = rendering.QueueHint;
- const SceneFlags = rendering.SceneFlags;
- // set viewport
- const colorStoreOp = cameraConfigs.enableMSAA ? StoreOp.DISCARD : StoreOp.STORE;
- // bind output render target
- if (forwardNeedClearColor(camera)) {
- this._reflectionProbeClearColor.x = camera.clearColor.x;
- this._reflectionProbeClearColor.y = camera.clearColor.y;
- this._reflectionProbeClearColor.z = camera.clearColor.z;
- const clearColor = rendering.packRGBE(this._reflectionProbeClearColor);
- this._clearColor.x = clearColor.x;
- this._clearColor.y = clearColor.y;
- this._clearColor.z = clearColor.z;
- this._clearColor.w = clearColor.w;
- pass.addRenderTarget(colorName, LoadOp.CLEAR, colorStoreOp, this._clearColor);
- } else {
- pass.addRenderTarget(colorName, LoadOp.LOAD, colorStoreOp);
- }
- // bind depth stencil buffer
- if (camera.clearFlag & ClearFlagBit.DEPTH_STENCIL) {
- pass.addDepthStencil(
- depthStencilName,
- LoadOp.CLEAR,
- StoreOp.DISCARD,
- camera.clearDepth,
- camera.clearStencil,
- camera.clearFlag & ClearFlagBit.DEPTH_STENCIL,
- );
- } else {
- pass.addDepthStencil(depthStencilName, LoadOp.LOAD, StoreOp.DISCARD);
- }
- // Set shadow map if enabled
- if (cameraConfigs.enableMainLightShadowMap) {
- pass.addTexture(`ShadowMap${id}`, 'cc_shadowMap');
- }
- // TODO(zhouzhenglong): Separate OPAQUE and MASK queue
- // add opaque and mask queue
- pass.addQueue(QueueHint.NONE, 'reflect-map') // Currently we put OPAQUE and MASK into one queue, so QueueHint is NONE
- .addScene(camera,
- SceneFlags.OPAQUE | SceneFlags.MASK | SceneFlags.REFLECTION_PROBE,
- mainLight || undefined,
- scene ? scene : undefined);
- }
- private _addForwardRadiancePasses(
- ppl: rendering.BasicPipeline,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: Readonly<CameraConfigs & ForwardPassConfigs>,
- id: number,
- camera: renderer.scene.Camera,
- width: number,
- height: number,
- mainLight: renderer.scene.DirectionalLight | null,
- colorName: string,
- depthStencilName: string,
- disableMSAA: boolean = false,
- depthStencilStoreOp: gfx.StoreOp = StoreOp.DISCARD,
- ): rendering.BasicRenderPassBuilder {
- const QueueHint = rendering.QueueHint;
- const SceneFlags = rendering.SceneFlags;
- // ----------------------------------------------------------------
- // Dynamic states
- // ----------------------------------------------------------------
- // Prepare camera clear color
- const clearColor = camera.clearColor; // Reduce C++/TS interop
- this._clearColor.x = clearColor.x;
- this._clearColor.y = clearColor.y;
- this._clearColor.z = clearColor.z;
- this._clearColor.w = clearColor.w;
- // Prepare camera viewport
- const viewport = camera.viewport; // Reduce C++/TS interop
- this._viewport.left = Math.round(viewport.x * width);
- this._viewport.top = Math.round(viewport.y * height);
- // Here we must use camera.viewport.width instead of camera.viewport.z, which
- // is undefined on native platform. The same as camera.viewport.height.
- this._viewport.width = Math.max(Math.round(viewport.width * width), 1);
- this._viewport.height = Math.max(Math.round(viewport.height * height), 1);
- // MSAA
- const enableMSAA = !disableMSAA && cameraConfigs.enableMSAA;
- assert(!enableMSAA || cameraConfigs.enableSingleForwardPass);
- // ----------------------------------------------------------------
- // Forward Lighting (Main Directional Light)
- // ----------------------------------------------------------------
- const pass = cameraConfigs.enableSingleForwardPass
- ? this._addForwardSingleRadiancePass(ppl, pplConfigs, cameraConfigs,
- id, camera, enableMSAA, width, height, mainLight,
- colorName, depthStencilName, depthStencilStoreOp)
- : this._addForwardMultipleRadiancePasses(ppl, cameraConfigs,
- id, camera, width, height, mainLight,
- colorName, depthStencilName, depthStencilStoreOp);
- // Planar Shadow
- if (cameraConfigs.enableMainLightPlanarShadowMap) {
- this._addPlanarShadowQueue(camera, mainLight, pass);
- }
- // ----------------------------------------------------------------
- // Forward Lighting (Blend)
- // ----------------------------------------------------------------
- // Add transparent queue
- const sceneFlags = SceneFlags.BLEND |
- (camera.geometryRenderer
- ? SceneFlags.GEOMETRY
- : SceneFlags.NONE);
- pass
- .addQueue(QueueHint.BLEND)
- .addScene(camera, sceneFlags, mainLight || undefined);
- return pass;
- }
- private _addForwardSingleRadiancePass(
- ppl: rendering.BasicPipeline,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: Readonly<CameraConfigs & ForwardPassConfigs>,
- id: number,
- camera: renderer.scene.Camera,
- enableMSAA: boolean,
- width: number,
- height: number,
- mainLight: renderer.scene.DirectionalLight | null,
- colorName: string,
- depthStencilName: string,
- depthStencilStoreOp: gfx.StoreOp
- ): rendering.BasicRenderPassBuilder {
- assert(cameraConfigs.enableSingleForwardPass);
- // ----------------------------------------------------------------
- // Forward Lighting (Main Directional Light)
- // ----------------------------------------------------------------
- let pass: rendering.BasicRenderPassBuilder;
- if (enableMSAA) {
- const msaaRadianceName = `MsaaRadiance${id}`;
- const msaaDepthStencilName = `MsaaDepthStencil${id}`;
- const sampleCount = cameraConfigs.settings.msaa.sampleCount;
- const msPass = ppl.addMultisampleRenderPass(width, height, sampleCount, 0, 'default');
- msPass.name = 'MsaaForwardPass';
- // MSAA always discards depth stencil
- this._buildForwardMainLightPass(msPass, cameraConfigs, id, camera,
- msaaRadianceName, msaaDepthStencilName, StoreOp.DISCARD, mainLight);
- msPass.resolveRenderTarget(msaaRadianceName, colorName);
- pass = msPass;
- } else {
- pass = ppl.addRenderPass(width, height, 'default');
- pass.name = 'ForwardPass';
- this._buildForwardMainLightPass(pass, cameraConfigs, id, camera,
- colorName, depthStencilName, depthStencilStoreOp, mainLight);
- }
- assert(pass !== undefined);
- // Forward Lighting (Additive Lights)
- this.forwardLighting.addLightQueues(
- pass,
- camera,
- pplConfigs.mobileMaxSpotLightShadowMaps,
- );
- return pass;
- }
- private _addForwardMultipleRadiancePasses(
- ppl: rendering.BasicPipeline,
- cameraConfigs: Readonly<CameraConfigs & ForwardPassConfigs>,
- id: number,
- camera: renderer.scene.Camera,
- width: number,
- height: number,
- mainLight: renderer.scene.DirectionalLight | null,
- colorName: string,
- depthStencilName: string,
- depthStencilStoreOp: gfx.StoreOp
- ): rendering.BasicRenderPassBuilder {
- assert(!cameraConfigs.enableSingleForwardPass);
- // Forward Lighting (Main Directional Light)
- let pass = ppl.addRenderPass(width, height, 'default');
- pass.name = 'ForwardPass';
- const firstStoreOp = this.forwardLighting.isMultipleLightPassesNeeded()
- ? StoreOp.STORE
- : depthStencilStoreOp;
- this._buildForwardMainLightPass(pass, cameraConfigs,
- id, camera, colorName, depthStencilName, firstStoreOp, mainLight);
- // Forward Lighting (Additive Lights)
- pass = this.forwardLighting
- .addLightPasses(colorName, depthStencilName, depthStencilStoreOp,
- id, width, height, camera, this._viewport, ppl, pass);
- return pass;
- }
- private _buildForwardMainLightPass(
- pass: rendering.BasicRenderPassBuilder,
- cameraConfigs: Readonly<CameraConfigs & ForwardPassConfigs>,
- id: number,
- camera: renderer.scene.Camera,
- colorName: string,
- depthStencilName: string,
- depthStencilStoreOp: gfx.StoreOp,
- mainLight: renderer.scene.DirectionalLight | null,
- scene: renderer.RenderScene | null = null,
- ): void {
- const QueueHint = rendering.QueueHint;
- const SceneFlags = rendering.SceneFlags;
- // set viewport
- pass.setViewport(this._viewport);
- const colorStoreOp = cameraConfigs.enableMSAA ? StoreOp.DISCARD : StoreOp.STORE;
- // bind output render target
- if (forwardNeedClearColor(camera)) {
- pass.addRenderTarget(colorName, LoadOp.CLEAR, colorStoreOp, this._clearColor);
- } else {
- pass.addRenderTarget(colorName, LoadOp.LOAD, colorStoreOp);
- }
- // bind depth stencil buffer
- if (DEBUG) {
- if (colorName === cameraConfigs.colorName &&
- depthStencilName !== cameraConfigs.depthStencilName) {
- warn('Default framebuffer cannot use custom depth stencil buffer');
- }
- }
- if (camera.clearFlag & ClearFlagBit.DEPTH_STENCIL) {
- pass.addDepthStencil(
- depthStencilName,
- LoadOp.CLEAR,
- depthStencilStoreOp,
- camera.clearDepth,
- camera.clearStencil,
- camera.clearFlag & ClearFlagBit.DEPTH_STENCIL,
- );
- } else {
- pass.addDepthStencil(depthStencilName, LoadOp.LOAD, depthStencilStoreOp);
- }
- // Set shadow map if enabled
- if (cameraConfigs.enableMainLightShadowMap) {
- pass.addTexture(`ShadowMap${id}`, 'cc_shadowMap');
- }
- // TODO(zhouzhenglong): Separate OPAQUE and MASK queue
- // add opaque and mask queue
- pass.addQueue(QueueHint.NONE) // Currently we put OPAQUE and MASK into one queue, so QueueHint is NONE
- .addScene(camera,
- SceneFlags.OPAQUE | SceneFlags.MASK,
- mainLight || undefined,
- scene ? scene : undefined);
- }
- private _addPlanarShadowQueue(
- camera: renderer.scene.Camera,
- mainLight: renderer.scene.DirectionalLight | null,
- pass: rendering.BasicRenderPassBuilder,
- ) {
- const QueueHint = rendering.QueueHint;
- const SceneFlags = rendering.SceneFlags;
- pass.addQueue(QueueHint.BLEND, 'planar-shadow')
- .addScene(
- camera,
- SceneFlags.SHADOW_CASTER | SceneFlags.PLANAR_SHADOW | SceneFlags.BLEND,
- mainLight || undefined,
- );
- }
- private readonly forwardLighting = new ForwardLighting();
- private readonly _viewport = new Viewport();
- private readonly _clearColor = new Color(0, 0, 0, 1);
- private readonly _reflectionProbeClearColor = new Vec3(0, 0, 0);
- }
- export interface BloomPassConfigs {
- enableBloom: boolean;
- }
- function downSize(size: number, scale: number): number {
- return Math.max(Math.floor(size * scale), 1);
- }
- interface RenderTextureDesc {
- name: string;
- width: number;
- height: number;
- }
- export class BuiltinBloomPassBuilder implements rendering.PipelinePassBuilder {
- getConfigOrder(): number {
- return 0;
- }
- getRenderOrder(): number {
- return 200;
- }
- configCamera(
- camera: Readonly<renderer.scene.Camera>,
- pipelineConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: CameraConfigs & BloomPassConfigs): void {
- const { bloom } = cameraConfigs.settings;
- const hasValidMaterial = (
- bloom.type === BloomType.KawaseDualFilter && !!bloom.kawaseFilterMaterial ||
- bloom.type === BloomType.MipmapFilter && !!bloom.mipmapFilterMaterial
- );
- cameraConfigs.enableBloom = bloom.enabled && hasValidMaterial;
- if (cameraConfigs.enableBloom) {
- ++cameraConfigs.remainingPasses;
- }
- }
- windowResize(
- ppl: rendering.BasicPipeline,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: CameraConfigs & BloomPassConfigs,
- window: renderer.RenderWindow): void {
- if (!cameraConfigs.enableBloom) {
- return;
- }
- const { width, height, settings: { bloom } } = cameraConfigs;
- const id = window.renderWindowId;
- const format = cameraConfigs.radianceFormat;
- if (bloom.type === BloomType.KawaseDualFilter) {
- let bloomWidth = cameraConfigs.width;
- let bloomHeight = cameraConfigs.height;
- for (let i = 0; i !== bloom.iterations + 1; ++i) {
- bloomWidth = Math.max(Math.floor(bloomWidth / 2), 1);
- bloomHeight = Math.max(Math.floor(bloomHeight / 2), 1);
- ppl.addRenderTarget(`BloomTex${id}_${i}`, format, bloomWidth, bloomHeight);
- }
- } else if (bloom.type === BloomType.MipmapFilter) {
- const iterations = bloom.iterations;
- for (let i = 0; i !== iterations + 1; ++i) {
- // DownSample
- if (i < iterations) {
- const scale = Math.pow(0.5, i + 2);
- this._bloomDownSampleTexDescs[i] = this.createTexture(
- ppl,
- `DownSampleColor${id}${i}`,
- downSize(width, scale),
- downSize(height, scale),
- format);
- }
- // UpSample
- if (i < iterations - 1) {
- const scale = Math.pow(0.5, iterations - i - 1);
- this._bloomUpSampleTexDescs[i] = this.createTexture(
- ppl,
- `UpSampleColor${id}${i}`,
- downSize(width, scale),
- downSize(height, scale),
- format);
- }
- }
- this._originalColorDesc = this.createTexture(ppl, `OriginalColor${id}`, width, height, format);
- this._prefilterTexDesc = this.createTexture(ppl, `PrefilterColor${id}`,
- downSize(width, 0.5), downSize(height, 0.5), format);
- }
- }
- private createTexture(
- ppl: rendering.BasicPipeline,
- name: string, width: number, height: number, format: number): RenderTextureDesc {
- const desc = { name, width, height };
- ppl.addRenderTarget(desc.name, format, desc.width, desc.height);
- return desc;
- }
- setup(
- ppl: rendering.BasicPipeline,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: CameraConfigs & BloomPassConfigs,
- camera: renderer.scene.Camera,
- context: PipelineContext,
- prevRenderPass?: rendering.BasicRenderPassBuilder)
- : rendering.BasicRenderPassBuilder | undefined {
- if (!cameraConfigs.enableBloom) {
- return prevRenderPass;
- }
- --cameraConfigs.remainingPasses;
- assert(cameraConfigs.remainingPasses >= 0);
- const bloom = cameraConfigs.settings.bloom;
- const id = camera.window.renderWindowId;
- switch (bloom.type) {
- case BloomType.KawaseDualFilter: {
- const material = bloom.kawaseFilterMaterial;
- assert(!!material);
- return this._addKawaseDualFilterBloomPasses(
- ppl, pplConfigs,
- cameraConfigs,
- cameraConfigs.settings,
- material,
- id,
- cameraConfigs.width,
- cameraConfigs.height,
- context.colorName);
- }
- case BloomType.MipmapFilter: {
- const material = bloom.mipmapFilterMaterial;
- assert(!!material);
- return this._addMipmapFilterBloomPasses(
- ppl, pplConfigs,
- cameraConfigs,
- cameraConfigs.settings,
- material,
- id,
- cameraConfigs.width,
- cameraConfigs.height,
- context.colorName);
- }
- default:
- return prevRenderPass;
- }
- }
- private _addKawaseDualFilterBloomPasses(
- ppl: rendering.BasicPipeline,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: CameraConfigs & Readonly<BloomPassConfigs>,
- settings: PipelineSettings,
- bloomMaterial: Material,
- id: number,
- width: number,
- height: number,
- radianceName: string,
- ): rendering.BasicRenderPassBuilder {
- const QueueHint = rendering.QueueHint;
- // Based on Kawase Dual Filter Blur. Saves bandwidth on mobile devices.
- // eslint-disable-next-line max-len
- // https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_slides.pdf
- // Size: [prefilter(1/2), downsample(1/4), downsample(1/8), downsample(1/16), ...]
- const iterations = settings.bloom.iterations;
- const sizeCount = iterations + 1;
- this._bloomWidths.length = sizeCount;
- this._bloomHeights.length = sizeCount;
- this._bloomWidths[0] = Math.max(Math.floor(width / 2), 1);
- this._bloomHeights[0] = Math.max(Math.floor(height / 2), 1);
- for (let i = 1; i !== sizeCount; ++i) {
- this._bloomWidths[i] = Math.max(Math.floor(this._bloomWidths[i - 1] / 2), 1);
- this._bloomHeights[i] = Math.max(Math.floor(this._bloomHeights[i - 1] / 2), 1);
- }
- // Bloom texture names
- this._bloomTexNames.length = sizeCount;
- for (let i = 0; i !== sizeCount; ++i) {
- this._bloomTexNames[i] = `BloomTex${id}_${i}`;
- }
- // Setup bloom parameters
- this._bloomParams.x = pplConfigs.useFloatOutput ? 1 : 0;
- this._bloomParams.y = 0; // unused
- this._bloomParams.z = settings.bloom.threshold;
- this._bloomParams.w = settings.bloom.enableAlphaMask ? 1 : 0;
- // Prefilter pass
- const prefilterPass = ppl.addRenderPass(this._bloomWidths[0], this._bloomHeights[0], 'cc-bloom-prefilter');
- prefilterPass.addRenderTarget(
- this._bloomTexNames[0],
- LoadOp.CLEAR,
- StoreOp.STORE,
- this._clearColorTransparentBlack,
- );
- prefilterPass.addTexture(radianceName, 'inputTexture');
- prefilterPass.setVec4('bloomParams', this._bloomParams);
- prefilterPass
- .addQueue(QueueHint.OPAQUE)
- .addFullscreenQuad(bloomMaterial, 0);
- // Downsample passes
- for (let i = 1; i !== sizeCount; ++i) {
- const downPass = ppl.addRenderPass(this._bloomWidths[i], this._bloomHeights[i], 'cc-bloom-downsample');
- downPass.addRenderTarget(this._bloomTexNames[i], LoadOp.CLEAR, StoreOp.STORE, this._clearColorTransparentBlack);
- downPass.addTexture(this._bloomTexNames[i - 1], 'bloomTexture');
- this._bloomTexSize.x = this._bloomWidths[i - 1];
- this._bloomTexSize.y = this._bloomHeights[i - 1];
- downPass.setVec4('bloomTexSize', this._bloomTexSize);
- downPass
- .addQueue(QueueHint.OPAQUE)
- .addFullscreenQuad(bloomMaterial, 1);
- }
- // Upsample passes
- for (let i = iterations; i-- > 0;) {
- const upPass = ppl.addRenderPass(this._bloomWidths[i], this._bloomHeights[i], 'cc-bloom-upsample');
- upPass.addRenderTarget(this._bloomTexNames[i], LoadOp.CLEAR, StoreOp.STORE, this._clearColorTransparentBlack);
- upPass.addTexture(this._bloomTexNames[i + 1], 'bloomTexture');
- this._bloomTexSize.x = this._bloomWidths[i + 1];
- this._bloomTexSize.y = this._bloomHeights[i + 1];
- upPass.setVec4('bloomTexSize', this._bloomTexSize);
- upPass
- .addQueue(QueueHint.OPAQUE)
- .addFullscreenQuad(bloomMaterial, 2);
- }
- // Combine pass
- this._bloomParams.w = settings.bloom.intensity;
- const combinePass = ppl.addRenderPass(width, height, 'cc-bloom-combine');
- combinePass.addRenderTarget(radianceName, LoadOp.LOAD, StoreOp.STORE);
- combinePass.addTexture(this._bloomTexNames[0], 'bloomTexture');
- combinePass.setVec4('bloomParams', this._bloomParams);
- combinePass
- .addQueue(QueueHint.BLEND)
- .addFullscreenQuad(bloomMaterial, 3);
- if (cameraConfigs.remainingPasses === 0) {
- return addCopyToScreenPass(ppl, pplConfigs, cameraConfigs, radianceName);
- } else {
- return combinePass;
- }
- }
- private _addPass(
- ppl: rendering.BasicPipeline,
- width: number,
- height: number,
- layout: string,
- colorName: string,
- material: Material,
- passIndex: number,
- loadOp: gfx.LoadOp = LoadOp.CLEAR,
- clearColor: gfx.Color = sClearColorTransparentBlack,
- queueHint: rendering.QueueHint = rendering.QueueHint.OPAQUE): rendering.BasicRenderPassBuilder {
- const pass = ppl.addRenderPass(width, height, layout);
- pass.addRenderTarget(colorName, loadOp, StoreOp.STORE, clearColor);
- pass.addQueue(queueHint)
- .addFullscreenQuad(material, passIndex);
- return pass;
- }
- private _addMipmapFilterBloomPasses(
- ppl: rendering.BasicPipeline,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: CameraConfigs & Readonly<BloomPassConfigs>,
- settings: PipelineSettings,
- bloomMaterial: Material,
- id: number,
- width: number,
- height: number,
- radianceName: string,
- ): rendering.BasicRenderPassBuilder {
- // Setup bloom parameters
- this._bloomParams.x = pplConfigs.useFloatOutput ? 1 : 0;
- this._bloomParams.x = 0; // unused
- this._bloomParams.z = settings.bloom.threshold;
- this._bloomParams.w = settings.bloom.intensity;
- const prefilterInfo = this._prefilterTexDesc;
- // Prefilter pass
- let currSamplePass = this._addPass(
- ppl,
- prefilterInfo.width,
- prefilterInfo.height,
- 'cc-bloom-mipmap-prefilter',
- prefilterInfo.name,
- bloomMaterial,
- 0,
- );
- currSamplePass.addTexture(radianceName, 'mainTexture');
- currSamplePass.setVec4('bloomParams', this._bloomParams);
- const downSampleInfos = this._bloomDownSampleTexDescs;
- // Downsample passes
- for (let i = 0; i < downSampleInfos.length; ++i) {
- const currInfo = downSampleInfos[i];
- const samplerSrc = i === 0 ? prefilterInfo : downSampleInfos[i - 1];
- const samplerSrcName = samplerSrc.name;
- this._bloomTexSize.x = 1 / samplerSrc.width;
- this._bloomTexSize.y = 1 / samplerSrc.height;
- currSamplePass = this._addPass(
- ppl,
- currInfo.width,
- currInfo.height,
- 'cc-bloom-mipmap-downsample',
- currInfo.name,
- bloomMaterial,
- 1,
- );
- currSamplePass.addTexture(samplerSrcName, 'mainTexture');
- currSamplePass.setVec4('bloomParams', this._bloomTexSize);
- }
- const lastIndex = downSampleInfos.length - 1;
- const upSampleInfos = this._bloomUpSampleTexDescs;
- // Upsample passes
- for (let i = 0; i < upSampleInfos.length; i++) {
- const currInfo = upSampleInfos[i];
- const sampleSrc = i === 0 ? downSampleInfos[lastIndex] : upSampleInfos[i - 1];
- const sampleSrcName = sampleSrc.name;
- this._bloomTexSize.x = 1 / sampleSrc.width;
- this._bloomTexSize.y = 1 / sampleSrc.height;
- currSamplePass = this._addPass(
- ppl,
- currInfo.width,
- currInfo.height,
- 'cc-bloom-mipmap-upsample',
- currInfo.name,
- bloomMaterial,
- 2,
- );
- currSamplePass.addTexture(sampleSrcName, 'mainTexture');
- currSamplePass.addTexture(downSampleInfos[lastIndex - 1 - i].name, 'downsampleTexture');
- currSamplePass.setVec4('bloomParams', this._bloomTexSize);
- }
- // Combine pass
- const combinePass = this._addPass(
- ppl,
- width,
- height,
- 'cc-bloom-mipmap-combine',
- radianceName,
- bloomMaterial,
- 3,
- LoadOp.LOAD,
- );
- combinePass.addTexture(upSampleInfos[upSampleInfos.length - 1].name, 'bloomTexture');
- combinePass.setVec4('bloomParams', this._bloomParams);
- if (cameraConfigs.remainingPasses === 0) {
- return addCopyToScreenPass(ppl, pplConfigs, cameraConfigs, radianceName);
- } else {
- return combinePass;
- }
- }
- // Bloom
- private readonly _clearColorTransparentBlack = new Color(0, 0, 0, 0);
- private readonly _bloomParams = new Vec4(0, 0, 0, 0);
- private readonly _bloomTexSize = new Vec4(0, 0, 0, 0);
- private readonly _bloomWidths: Array<number> = [];
- private readonly _bloomHeights: Array<number> = [];
- private readonly _bloomTexNames: Array<string> = [];
- // Mipmap Bloom
- private readonly _bloomUpSampleTexDescs: Array<RenderTextureDesc> = [];
- private readonly _bloomDownSampleTexDescs: Array<RenderTextureDesc> = [];
- private _prefilterTexDesc: RenderTextureDesc = { name: '', width: 0, height: 0 };
- private _originalColorDesc: RenderTextureDesc = { name: '', width: 0, height: 0 };
- }
- export interface ToneMappingPassConfigs {
- enableToneMapping: boolean;
- enableColorGrading: boolean;
- }
- export class BuiltinToneMappingPassBuilder implements rendering.PipelinePassBuilder {
- getConfigOrder(): number {
- return 0;
- }
- getRenderOrder(): number {
- return 300;
- }
- configCamera(
- camera: Readonly<renderer.scene.Camera>,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: CameraConfigs & ToneMappingPassConfigs): void {
- const settings = cameraConfigs.settings;
- cameraConfigs.enableColorGrading
- = settings.colorGrading.enabled
- && !!settings.colorGrading.material
- && !!settings.colorGrading.colorGradingMap;
- cameraConfigs.enableToneMapping
- = cameraConfigs.enableHDR // From Half to RGBA8
- || cameraConfigs.enableColorGrading; // Color grading
- if (cameraConfigs.enableToneMapping) {
- ++cameraConfigs.remainingPasses;
- }
- }
- windowResize(
- ppl: rendering.BasicPipeline,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: CameraConfigs & ToneMappingPassConfigs): void {
- if (cameraConfigs.enableColorGrading) {
- assert(!!cameraConfigs.settings.colorGrading.material);
- cameraConfigs.settings.colorGrading.material.setProperty(
- 'colorGradingMap',
- cameraConfigs.settings.colorGrading.colorGradingMap);
- }
- }
- setup(
- ppl: rendering.BasicPipeline,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: CameraConfigs & ToneMappingPassConfigs,
- camera: renderer.scene.Camera,
- context: PipelineContext,
- prevRenderPass?: rendering.BasicRenderPassBuilder)
- : rendering.BasicRenderPassBuilder | undefined {
- if (!cameraConfigs.enableToneMapping) {
- return prevRenderPass;
- }
- --cameraConfigs.remainingPasses;
- assert(cameraConfigs.remainingPasses >= 0);
- if (cameraConfigs.remainingPasses === 0) {
- return this._addCopyAndTonemapPass(ppl, pplConfigs, cameraConfigs,
- cameraConfigs.width, cameraConfigs.height,
- context.colorName, cameraConfigs.colorName);
- } else {
- const id = cameraConfigs.renderWindowId;
- const ldrColorPrefix = cameraConfigs.enableShadingScale
- ? `ScaledLdrColor`
- : `LdrColor`;
- const ldrColorName = getPingPongRenderTarget(context.colorName, ldrColorPrefix, id);
- const radianceName = context.colorName;
- context.colorName = ldrColorName;
- return this._addCopyAndTonemapPass(ppl, pplConfigs, cameraConfigs,
- cameraConfigs.width, cameraConfigs.height,
- radianceName, ldrColorName);
- }
- }
- private _addCopyAndTonemapPass(
- ppl: rendering.BasicPipeline,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: CameraConfigs & ToneMappingPassConfigs,
- width: number,
- height: number,
- radianceName: string,
- colorName: string,
- ): rendering.BasicRenderPassBuilder {
- let pass: rendering.BasicRenderPassBuilder;
- const settings = cameraConfigs.settings;
- if (cameraConfigs.enableColorGrading) {
- assert(!!settings.colorGrading.material);
- assert(!!settings.colorGrading.colorGradingMap);
- const lutTex = settings.colorGrading.colorGradingMap;
- this._colorGradingTexSize.x = lutTex.width;
- this._colorGradingTexSize.y = lutTex.height;
- const isSquareMap = lutTex.width === lutTex.height;
- if (isSquareMap) {
- pass = ppl.addRenderPass(width, height, 'cc-color-grading-8x8');
- } else {
- pass = ppl.addRenderPass(width, height, 'cc-color-grading-nx1');
- }
- pass.addRenderTarget(colorName, LoadOp.CLEAR, StoreOp.STORE, sClearColorTransparentBlack);
- pass.addTexture(radianceName, 'sceneColorMap');
- pass.setVec2('lutTextureSize', this._colorGradingTexSize);
- pass.setFloat('contribute', settings.colorGrading.contribute);
- pass.addQueue(rendering.QueueHint.OPAQUE)
- .addFullscreenQuad(settings.colorGrading.material, isSquareMap ? 1 : 0);
- } else {
- pass = ppl.addRenderPass(width, height, 'cc-tone-mapping');
- pass.addRenderTarget(colorName, LoadOp.CLEAR, StoreOp.STORE, sClearColorTransparentBlack);
- pass.addTexture(radianceName, 'inputTexture');
- if (settings.toneMapping.material) {
- pass.addQueue(rendering.QueueHint.OPAQUE)
- .addFullscreenQuad(settings.toneMapping.material, 0);
- } else {
- assert(!!cameraConfigs.copyAndTonemapMaterial);
- pass.addQueue(rendering.QueueHint.OPAQUE)
- .addFullscreenQuad(cameraConfigs.copyAndTonemapMaterial, 0);
- }
- }
- return pass;
- }
- private readonly _colorGradingTexSize = new Vec2(0, 0);
- }
- export interface FXAAPassConfigs {
- enableFXAA: boolean;
- }
- export class BuiltinFXAAPassBuilder implements rendering.PipelinePassBuilder {
- getConfigOrder(): number {
- return 0;
- }
- getRenderOrder(): number {
- return 400;
- }
- configCamera(
- camera: Readonly<renderer.scene.Camera>,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: CameraConfigs & FXAAPassConfigs): void {
- cameraConfigs.enableFXAA
- = cameraConfigs.settings.fxaa.enabled
- && !!cameraConfigs.settings.fxaa.material;
- if (cameraConfigs.enableFXAA) {
- ++cameraConfigs.remainingPasses;
- }
- }
- setup(
- ppl: rendering.BasicPipeline,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: CameraConfigs & FXAAPassConfigs,
- camera: renderer.scene.Camera,
- context: PipelineContext,
- prevRenderPass?: rendering.BasicRenderPassBuilder)
- : rendering.BasicRenderPassBuilder | undefined {
- if (!cameraConfigs.enableFXAA) {
- return prevRenderPass;
- }
- --cameraConfigs.remainingPasses;
- assert(cameraConfigs.remainingPasses >= 0);
- const id = cameraConfigs.renderWindowId;
- const ldrColorPrefix = cameraConfigs.enableShadingScale
- ? `ScaledLdrColor`
- : `LdrColor`;
- const ldrColorName = getPingPongRenderTarget(context.colorName, ldrColorPrefix, id);
- assert(!!cameraConfigs.settings.fxaa.material);
- if (cameraConfigs.remainingPasses === 0) {
- if (cameraConfigs.enableShadingScale) {
- this._addFxaaPass(ppl, pplConfigs,
- cameraConfigs.settings.fxaa.material,
- cameraConfigs.width,
- cameraConfigs.height,
- context.colorName,
- ldrColorName);
- return addCopyToScreenPass(ppl, pplConfigs, cameraConfigs, ldrColorName);
- } else {
- assert(cameraConfigs.width === cameraConfigs.nativeWidth);
- assert(cameraConfigs.height === cameraConfigs.nativeHeight);
- return this._addFxaaPass(ppl, pplConfigs,
- cameraConfigs.settings.fxaa.material,
- cameraConfigs.width,
- cameraConfigs.height,
- context.colorName,
- cameraConfigs.colorName);
- }
- } else {
- const inputColorName = context.colorName;
- context.colorName = ldrColorName;
- const lastPass = this._addFxaaPass(ppl, pplConfigs,
- cameraConfigs.settings.fxaa.material,
- cameraConfigs.width,
- cameraConfigs.height,
- inputColorName,
- ldrColorName);
- return lastPass;
- }
- }
- private _addFxaaPass(
- ppl: rendering.BasicPipeline,
- pplConfigs: Readonly<PipelineConfigs>,
- fxaaMaterial: Material,
- width: number,
- height: number,
- ldrColorName: string,
- colorName: string,
- ): rendering.BasicRenderPassBuilder {
- this._fxaaParams.x = width;
- this._fxaaParams.y = height;
- this._fxaaParams.z = 1 / width;
- this._fxaaParams.w = 1 / height;
- const pass = ppl.addRenderPass(width, height, 'cc-fxaa');
- pass.addRenderTarget(colorName, LoadOp.CLEAR, StoreOp.STORE, sClearColorTransparentBlack);
- pass.addTexture(ldrColorName, 'sceneColorMap');
- pass.setVec4('texSize', this._fxaaParams);
- pass.addQueue(rendering.QueueHint.OPAQUE)
- .addFullscreenQuad(fxaaMaterial, 0);
- return pass;
- }
- // FXAA
- private readonly _fxaaParams = new Vec4(0, 0, 0, 0);
- }
- export interface FSRPassConfigs {
- enableFSR: boolean;
- }
- export class BuiltinFsrPassBuilder implements rendering.PipelinePassBuilder {
- getConfigOrder(): number {
- return 0;
- }
- getRenderOrder(): number {
- return 500;
- }
- configCamera(
- camera: Readonly<renderer.scene.Camera>,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: CameraConfigs & FSRPassConfigs): void {
- // FSR (Depend on Shading scale)
- cameraConfigs.enableFSR = cameraConfigs.settings.fsr.enabled
- && !!cameraConfigs.settings.fsr.material
- && cameraConfigs.enableShadingScale
- && cameraConfigs.shadingScale < 1.0;
- if (cameraConfigs.enableFSR) {
- ++cameraConfigs.remainingPasses;
- }
- }
- setup(
- ppl: rendering.BasicPipeline,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: CameraConfigs & FSRPassConfigs,
- camera: renderer.scene.Camera,
- context: PipelineContext,
- prevRenderPass?: rendering.BasicRenderPassBuilder)
- : rendering.BasicRenderPassBuilder | undefined {
- if (!cameraConfigs.enableFSR) {
- return prevRenderPass;
- }
- --cameraConfigs.remainingPasses;
- const inputColorName = context.colorName;
- const outputColorName
- = cameraConfigs.remainingPasses === 0
- ? cameraConfigs.colorName
- : getPingPongRenderTarget(context.colorName, 'UiColor', cameraConfigs.renderWindowId);
- context.colorName = outputColorName;
- assert(!!cameraConfigs.settings.fsr.material);
- return this._addFsrPass(ppl, pplConfigs, cameraConfigs,
- cameraConfigs.settings,
- cameraConfigs.settings.fsr.material,
- cameraConfigs.renderWindowId,
- cameraConfigs.width,
- cameraConfigs.height,
- inputColorName,
- cameraConfigs.nativeWidth,
- cameraConfigs.nativeHeight,
- outputColorName);
- }
- private _addFsrPass(
- ppl: rendering.BasicPipeline,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: CameraConfigs & FSRPassConfigs,
- settings: PipelineSettings,
- fsrMaterial: Material,
- id: number,
- width: number,
- height: number,
- inputColorName: string,
- nativeWidth: number,
- nativeHeight: number,
- outputColorName: string,
- ): rendering.BasicRenderPassBuilder {
- this._fsrTexSize.x = width;
- this._fsrTexSize.y = height;
- this._fsrTexSize.z = nativeWidth;
- this._fsrTexSize.w = nativeHeight;
- this._fsrParams.x = clamp(1.0 - settings.fsr.sharpness, 0.02, 0.98);
- const uiColorPrefix = 'UiColor';
- const fsrColorName = getPingPongRenderTarget(outputColorName, uiColorPrefix, id);
- const easuPass = ppl.addRenderPass(nativeWidth, nativeHeight, 'cc-fsr-easu');
- easuPass.addRenderTarget(fsrColorName, LoadOp.CLEAR, StoreOp.STORE, sClearColorTransparentBlack);
- easuPass.addTexture(inputColorName, 'outputResultMap');
- easuPass.setVec4('fsrTexSize', this._fsrTexSize);
- easuPass
- .addQueue(rendering.QueueHint.OPAQUE)
- .addFullscreenQuad(fsrMaterial, 0);
- const rcasPass = ppl.addRenderPass(nativeWidth, nativeHeight, 'cc-fsr-rcas');
- rcasPass.addRenderTarget(outputColorName, LoadOp.CLEAR, StoreOp.STORE, sClearColorTransparentBlack);
- rcasPass.addTexture(fsrColorName, 'outputResultMap');
- rcasPass.setVec4('fsrTexSize', this._fsrTexSize);
- rcasPass.setVec4('fsrParams', this._fsrParams);
- rcasPass
- .addQueue(rendering.QueueHint.OPAQUE)
- .addFullscreenQuad(fsrMaterial, 1);
- return rcasPass;
- }
- // FSR
- private readonly _fsrParams = new Vec4(0, 0, 0, 0);
- private readonly _fsrTexSize = new Vec4(0, 0, 0, 0);
- }
- export class BuiltinUiPassBuilder implements rendering.PipelinePassBuilder {
- getConfigOrder(): number {
- return 0;
- }
- getRenderOrder(): number {
- return 1000;
- }
- setup(
- ppl: rendering.BasicPipeline,
- pplConfigs: Readonly<PipelineConfigs>,
- cameraConfigs: CameraConfigs & FSRPassConfigs,
- camera: renderer.scene.Camera,
- context: PipelineContext,
- prevRenderPass?: rendering.BasicRenderPassBuilder)
- : rendering.BasicRenderPassBuilder | undefined {
- assert(!!prevRenderPass);
- let flags = rendering.SceneFlags.UI;
- if (cameraConfigs.enableProfiler) {
- flags |= rendering.SceneFlags.PROFILER;
- prevRenderPass.showStatistics = true;
- }
- prevRenderPass
- .addQueue(rendering.QueueHint.BLEND, 'default', 'default')
- .addScene(camera, flags);
- return prevRenderPass;
- }
- }
- if (rendering) {
- const { QueueHint, SceneFlags } = rendering;
- class BuiltinPipelineBuilder implements rendering.PipelineBuilder {
- private readonly _pipelineEvent: PipelineEventProcessor = cclegacy.director.root.pipelineEvent as PipelineEventProcessor;
- private readonly _forwardPass = new BuiltinForwardPassBuilder();
- private readonly _bloomPass = new BuiltinBloomPassBuilder();
- private readonly _toneMappingPass = new BuiltinToneMappingPassBuilder();
- private readonly _fxaaPass = new BuiltinFXAAPassBuilder();
- private readonly _fsrPass = new BuiltinFsrPassBuilder();
- private readonly _uiPass = new BuiltinUiPassBuilder();
- // Internal cached resources
- private readonly _clearColor = new Color(0, 0, 0, 1);
- private readonly _viewport = new Viewport();
- private readonly _configs = new PipelineConfigs();
- private readonly _cameraConfigs = new CameraConfigs();
- // Materials
- private readonly _copyAndTonemapMaterial = new Material();
- // Internal States
- private _initialized = false; // TODO(zhouzhenglong): Make default effect asset loading earlier and remove this flag
- private _passBuilders: rendering.PipelinePassBuilder[] = [];
- private _setupPipelinePreview(
- camera: renderer.scene.Camera,
- cameraConfigs: CameraConfigs) {
- const isEditorView: boolean
- = camera.cameraUsage === CameraUsage.SCENE_VIEW
- || camera.cameraUsage === CameraUsage.PREVIEW;
- if (isEditorView) {
- const editorSettings = rendering.getEditorPipelineSettings() as PipelineSettings | null;
- if (editorSettings) {
- cameraConfigs.settings = editorSettings;
- } else {
- cameraConfigs.settings = defaultSettings;
- }
- } else {
- if (camera.pipelineSettings) {
- cameraConfigs.settings = camera.pipelineSettings as PipelineSettings;
- } else {
- cameraConfigs.settings = defaultSettings;
- }
- }
- }
- private _preparePipelinePasses(cameraConfigs: CameraConfigs): void {
- const passBuilders = this._passBuilders;
- passBuilders.length = 0;
- const settings = cameraConfigs.settings as PipelineSettings2;
- if (settings._passes) {
- for (const pass of settings._passes) {
- passBuilders.push(pass);
- }
- assert(passBuilders.length === settings._passes.length);
- }
- passBuilders.push(this._forwardPass);
- if (settings.bloom.enabled) {
- passBuilders.push(this._bloomPass);
- }
- passBuilders.push(this._toneMappingPass);
- if (settings.fxaa.enabled) {
- passBuilders.push(this._fxaaPass);
- }
- if (settings.fsr.enabled) {
- passBuilders.push(this._fsrPass);
- }
- passBuilders.push(this._uiPass);
- }
- private _setupBuiltinCameraConfigs(
- ppl: rendering.BasicPipeline,
- camera: renderer.scene.Camera,
- pipelineConfigs: PipelineConfigs,
- cameraConfigs: CameraConfigs
- ) {
- const window = camera.window;
- const isMainGameWindow: boolean = camera.cameraUsage === CameraUsage.GAME && !!window.swapchain;
- const isGameView = isMainGameWindow || camera.cameraUsage === CameraUsage.GAME_VIEW;
- // Window
- cameraConfigs.isMainGameWindow = isMainGameWindow;
- cameraConfigs.renderWindowId = window.renderWindowId;
- // Camera
- cameraConfigs.colorName = window.colorName;
- cameraConfigs.depthStencilName = window.depthStencilName;
- // Pipeline
- cameraConfigs.enableFullPipeline = (camera.visibility & (Layers.Enum.DEFAULT)) !== 0;
- cameraConfigs.enableProfiler = ppl.profiler && isGameView;
- cameraConfigs.remainingPasses = 0;
- // Shading scale
- cameraConfigs.shadingScale = cameraConfigs.settings.shadingScale;
- cameraConfigs.enableShadingScale = cameraConfigs.settings.enableShadingScale
- && cameraConfigs.shadingScale !== 1.0;
- cameraConfigs.nativeWidth = Math.max(Math.floor(window.width), 1);
- cameraConfigs.nativeHeight = Math.max(Math.floor(window.height), 1);
- cameraConfigs.width = cameraConfigs.enableShadingScale
- ? Math.max(Math.floor(cameraConfigs.nativeWidth * cameraConfigs.shadingScale), 1)
- : cameraConfigs.nativeWidth;
- cameraConfigs.height = cameraConfigs.enableShadingScale
- ? Math.max(Math.floor(cameraConfigs.nativeHeight * cameraConfigs.shadingScale), 1)
- : cameraConfigs.nativeHeight;
- // Radiance
- cameraConfigs.enableHDR = cameraConfigs.enableFullPipeline
- && pipelineConfigs.useFloatOutput;
- cameraConfigs.radianceFormat = cameraConfigs.enableHDR
- ? gfx.Format.RGBA16F : gfx.Format.RGBA8;
- // Tone Mapping
- cameraConfigs.copyAndTonemapMaterial = this._copyAndTonemapMaterial;
- // Depth
- cameraConfigs.enableStoreSceneDepth = false;
- }
- private _setupCameraConfigs(
- ppl: rendering.BasicPipeline,
- camera: renderer.scene.Camera,
- pipelineConfigs: PipelineConfigs,
- cameraConfigs: CameraConfigs
- ): void {
- this._setupPipelinePreview(camera, cameraConfigs);
- this._preparePipelinePasses(cameraConfigs);
- sortPipelinePassBuildersByConfigOrder(this._passBuilders);
- this._setupBuiltinCameraConfigs(ppl, camera, pipelineConfigs, cameraConfigs);
- for (const builder of this._passBuilders) {
- if (builder.configCamera) {
- builder.configCamera(camera, pipelineConfigs, cameraConfigs);
- }
- }
- }
- // ----------------------------------------------------------------
- // Interface
- // ----------------------------------------------------------------
- windowResize(
- ppl: rendering.BasicPipeline,
- window: renderer.RenderWindow,
- camera: renderer.scene.Camera,
- nativeWidth: number,
- nativeHeight: number,
- ): void {
- setupPipelineConfigs(ppl, this._configs);
- this._setupCameraConfigs(ppl, camera, this._configs, this._cameraConfigs);
- // Render Window (UI)
- const id = window.renderWindowId;
- ppl.addRenderWindow(this._cameraConfigs.colorName,
- Format.RGBA8, nativeWidth, nativeHeight, window,
- this._cameraConfigs.depthStencilName);
- const width = this._cameraConfigs.width;
- const height = this._cameraConfigs.height;
- if (this._cameraConfigs.enableShadingScale) {
- ppl.addDepthStencil(`ScaledSceneDepth_${id}`, Format.DEPTH_STENCIL, width, height);
- ppl.addRenderTarget(`ScaledRadiance0_${id}`, this._cameraConfigs.radianceFormat, width, height);
- ppl.addRenderTarget(`ScaledRadiance1_${id}`, this._cameraConfigs.radianceFormat, width, height);
- ppl.addRenderTarget(`ScaledLdrColor0_${id}`, Format.RGBA8, width, height);
- ppl.addRenderTarget(`ScaledLdrColor1_${id}`, Format.RGBA8, width, height);
- } else {
- ppl.addDepthStencil(`SceneDepth_${id}`, Format.DEPTH_STENCIL, width, height);
- ppl.addRenderTarget(`Radiance0_${id}`, this._cameraConfigs.radianceFormat, width, height);
- ppl.addRenderTarget(`Radiance1_${id}`, this._cameraConfigs.radianceFormat, width, height);
- ppl.addRenderTarget(`LdrColor0_${id}`, Format.RGBA8, width, height);
- ppl.addRenderTarget(`LdrColor1_${id}`, Format.RGBA8, width, height);
- }
- ppl.addRenderTarget(`UiColor0_${id}`, Format.RGBA8, nativeWidth, nativeHeight);
- ppl.addRenderTarget(`UiColor1_${id}`, Format.RGBA8, nativeWidth, nativeHeight);
- for (const builder of this._passBuilders) {
- if (builder.windowResize) {
- builder.windowResize(ppl, this._configs, this._cameraConfigs, window, camera, nativeWidth, nativeHeight);
- }
- }
- }
- setup(cameras: renderer.scene.Camera[], ppl: rendering.BasicPipeline): void {
- // TODO(zhouzhenglong): Make default effect asset loading earlier and remove _initMaterials
- if (this._initMaterials(ppl)) {
- return;
- }
- // Render cameras
- // log(`==================== One Frame ====================`);
- for (const camera of cameras) {
- // Skip invalid camera
- if (!camera.scene || !camera.window) {
- continue;
- }
- // Setup camera configs
- this._setupCameraConfigs(ppl, camera, this._configs, this._cameraConfigs);
- // log(`Setup camera: ${camera.node!.name}, window: ${camera.window.renderWindowId}, isFull: ${this._cameraConfigs.enableFullPipeline}, `
- // + `size: ${camera.window.width}x${camera.window.height}`);
- this._pipelineEvent.emit(PipelineEventType.RENDER_CAMERA_BEGIN, camera);
- // Build pipeline
- if (this._cameraConfigs.enableFullPipeline) {
- this._buildForwardPipeline(ppl, camera, camera.scene, this._passBuilders);
- } else {
- this._buildSimplePipeline(ppl, camera);
- }
- this._pipelineEvent.emit(PipelineEventType.RENDER_CAMERA_END, camera);
- }
- }
- // ----------------------------------------------------------------
- // Pipelines
- // ----------------------------------------------------------------
- private _buildSimplePipeline(
- ppl: rendering.BasicPipeline,
- camera: renderer.scene.Camera,
- ): void {
- const width = Math.max(Math.floor(camera.window.width), 1);
- const height = Math.max(Math.floor(camera.window.height), 1);
- const colorName = this._cameraConfigs.colorName;
- const depthStencilName = this._cameraConfigs.depthStencilName;
- const viewport = camera.viewport; // Reduce C++/TS interop
- this._viewport.left = Math.round(viewport.x * width);
- this._viewport.top = Math.round(viewport.y * height);
- // Here we must use camera.viewport.width instead of camera.viewport.z, which
- // is undefined on native platform. The same as camera.viewport.height.
- this._viewport.width = Math.max(Math.round(viewport.width * width), 1);
- this._viewport.height = Math.max(Math.round(viewport.height * height), 1);
- const clearColor = camera.clearColor; // Reduce C++/TS interop
- this._clearColor.x = clearColor.x;
- this._clearColor.y = clearColor.y;
- this._clearColor.z = clearColor.z;
- this._clearColor.w = clearColor.w;
- const pass = ppl.addRenderPass(width, height, 'default');
- // bind output render target
- if (forwardNeedClearColor(camera)) {
- pass.addRenderTarget(colorName, LoadOp.CLEAR, StoreOp.STORE, this._clearColor);
- } else {
- pass.addRenderTarget(colorName, LoadOp.LOAD, StoreOp.STORE);
- }
- // bind depth stencil buffer
- if (camera.clearFlag & ClearFlagBit.DEPTH_STENCIL) {
- pass.addDepthStencil(
- depthStencilName,
- LoadOp.CLEAR,
- StoreOp.DISCARD,
- camera.clearDepth,
- camera.clearStencil,
- camera.clearFlag & ClearFlagBit.DEPTH_STENCIL,
- );
- } else {
- pass.addDepthStencil(depthStencilName, LoadOp.LOAD, StoreOp.DISCARD);
- }
- pass.setViewport(this._viewport);
- // The opaque queue is used for Reflection probe preview
- pass.addQueue(QueueHint.OPAQUE)
- .addScene(camera, SceneFlags.OPAQUE);
- // The blend queue is used for UI and Gizmos
- let flags = SceneFlags.BLEND | SceneFlags.UI;
- if (this._cameraConfigs.enableProfiler) {
- flags |= SceneFlags.PROFILER;
- pass.showStatistics = true;
- }
- pass.addQueue(QueueHint.BLEND)
- .addScene(camera, flags);
- }
- private _buildForwardPipeline(
- ppl: rendering.BasicPipeline,
- camera: renderer.scene.Camera,
- scene: renderer.RenderScene,
- passBuilders: rendering.PipelinePassBuilder[],
- ): void {
- sortPipelinePassBuildersByRenderOrder(passBuilders);
- const context: PipelineContext = {
- colorName: '',
- depthStencilName: '',
- };
- let lastPass: rendering.BasicRenderPassBuilder | undefined = undefined;
- for (const builder of passBuilders) {
- if (builder.setup) {
- lastPass = builder.setup(ppl, this._configs, this._cameraConfigs,
- camera, context, lastPass);
- }
- }
- assert(this._cameraConfigs.remainingPasses === 0);
- }
- private _initMaterials(ppl: rendering.BasicPipeline): number {
- if (this._initialized) {
- return 0;
- }
- setupPipelineConfigs(ppl, this._configs);
- // When add new effect asset, please add its uuid to the dependentAssets in cc.config.json.
- this._copyAndTonemapMaterial._uuid = `builtin-pipeline-tone-mapping-material`;
- this._copyAndTonemapMaterial.initialize({ effectName: 'pipeline/post-process/tone-mapping' });
- if (this._copyAndTonemapMaterial.effectAsset) {
- this._initialized = true;
- }
- return this._initialized ? 0 : 1;
- }
- }
- rendering.setCustomPipeline('Builtin', new BuiltinPipelineBuilder());
- } // if (rendering)
|