diff --git a/cocos/rendering/custom/executor.ts b/cocos/rendering/custom/executor.ts index 2f0f5d06214..f80718023e4 100644 --- a/cocos/rendering/custom/executor.ts +++ b/cocos/rendering/custom/executor.ts @@ -1446,6 +1446,7 @@ class DeviceRenderScene implements RecordingInterface { const renderQueueDesc = sceneCulling.renderQueueIndex.get(this.graphScene.sceneID)!; const renderQueue = sceneCulling.renderQueues[renderQueueDesc.renderQueueTarget]; const graphSceneData = this.graphScene.scene!; + if (bool(graphSceneData.flags & SceneFlags.REFLECTION_PROBE)) renderQueue.probeQueue.applyMacro(); renderQueue.recordCommands(context.commandBuffer, this._renderPass); if (bool(graphSceneData.flags & SceneFlags.REFLECTION_PROBE)) renderQueue.probeQueue.removeMacro(); if (graphSceneData.flags & SceneFlags.GEOMETRY) { diff --git a/cocos/rendering/custom/pipeline.ts b/cocos/rendering/custom/pipeline.ts index 107482e725a..070cab45b41 100644 --- a/cocos/rendering/custom/pipeline.ts +++ b/cocos/rendering/custom/pipeline.ts @@ -28,6 +28,7 @@ * ========================= !DO NOT CHANGE THE FOLLOWING SECTION MANUALLY! ========================= */ /* eslint-disable max-len */ +import type { AABB } from '../../core/geometry/aabb'; import type { Material } from '../../asset/assets'; import type { Camera } from '../../render-scene/scene/camera'; import type { DirectionalLight } from '../../render-scene/scene/directional-light'; @@ -480,6 +481,7 @@ export interface SceneBuilder extends Setter { light: Light, csmLevel?: number, optCamera?: Camera): void; + setCullingWorldBounds (aabb: AABB): void; } /** diff --git a/cocos/rendering/custom/render-graph.ts b/cocos/rendering/custom/render-graph.ts index 16b99ceb005..30dbe8f0a2f 100644 --- a/cocos/rendering/custom/render-graph.ts +++ b/cocos/rendering/custom/render-graph.ts @@ -29,6 +29,7 @@ */ /* eslint-disable max-len */ import { AdjI, AdjacencyGraph, BidirectionalGraph, ComponentGraph, ED, InEI, MutableGraph, MutableReferenceGraph, NamedGraph, OutE, OutEI, PolymorphicGraph, PropertyGraph, ReferenceGraph, UuidGraph, VertexListGraph } from './graph'; +import type { AABB } from '../../core/geometry/aabb'; import type { Material } from '../../asset/assets'; import type { Camera } from '../../render-scene/scene/camera'; import type { Buffer, Framebuffer, RenderPass, Sampler, SamplerInfo, Swapchain, Texture } from '../../gfx'; @@ -990,6 +991,7 @@ export const enum CullingFlags { CAMERA_FRUSTUM = 0x1, LIGHT_FRUSTUM = 0x2, LIGHT_BOUNDS = 0x4, + WORLD_BOUNDS = 0x8, } export class SceneData { @@ -1000,6 +1002,7 @@ export class SceneData { light: LightInfo = new LightInfo(), cullingFlags: CullingFlags = CullingFlags.CAMERA_FRUSTUM, shadingLight: Light | null = null, + worldBounds: AABB | null = null, ) { this.scene = scene; this.camera = camera; @@ -1007,6 +1010,7 @@ export class SceneData { this.flags = flags; this.cullingFlags = cullingFlags; this.shadingLight = shadingLight; + this.worldBounds = worldBounds; } reset ( scene: RenderScene | null, @@ -1014,6 +1018,7 @@ export class SceneData { flags: SceneFlags, cullingFlags: CullingFlags, shadingLight: Light | null, + worldBounds: AABB | null, ): void { this.scene = scene; this.camera = camera; @@ -1021,6 +1026,7 @@ export class SceneData { this.flags = flags; this.cullingFlags = cullingFlags; this.shadingLight = shadingLight; + this.worldBounds = worldBounds; } declare /*pointer*/ scene: RenderScene | null; declare /*pointer*/ camera: Camera | null; @@ -1028,6 +1034,7 @@ export class SceneData { declare flags: SceneFlags; declare cullingFlags: CullingFlags; declare /*refcount*/ shadingLight: Light | null; + declare worldBounds: AABB | null; } export class Dispatch { @@ -1722,9 +1729,10 @@ export class RenderGraphObjectPool { flags: SceneFlags = SceneFlags.NONE, cullingFlags: CullingFlags = CullingFlags.CAMERA_FRUSTUM, shadingLight: Light | null = null, + worldBounds: AABB | null = null, ): SceneData { const v = this.sd.add(); // SceneData - v.reset(scene, camera, flags, cullingFlags, shadingLight); + v.reset(scene, camera, flags, cullingFlags, shadingLight, worldBounds); return v; } createDispatch ( diff --git a/cocos/rendering/custom/scene-culling.ts b/cocos/rendering/custom/scene-culling.ts index 8a0ceeeaaf5..b68059b7ec1 100644 --- a/cocos/rendering/custom/scene-culling.ts +++ b/cocos/rendering/custom/scene-culling.ts @@ -4,7 +4,7 @@ import { CommandBuffer, Device, Buffer, BufferInfo, BufferViewInfo, MemoryUsageB import { BatchingSchemes, Pass, RenderScene } from '../../render-scene'; import { CSMLevel, Camera, DirectionalLight, Light, LightType, Model, PointLight, ProbeType, RangedDirectionalLight, - ReflectionProbe, SKYBOX_FLAG, ShadowType, Shadows, SphereLight, SpotLight } from '../../render-scene/scene'; + SKYBOX_FLAG, ShadowType, Shadows, SphereLight, SpotLight } from '../../render-scene/scene'; import { Layers, Node } from '../../scene-graph'; import { PipelineSceneData } from '../pipeline-scene-data'; import { hashCombineStr, getSubpassOrPassID, bool, AlignUp, SetLightUBO } from './define'; @@ -40,7 +40,8 @@ function computeCullingKey ( const light = sceneData.light.light; const lightLevel = sceneData.light.level; const culledByLight = sceneData.light.culledByLight; - const reflectProbe = sceneData.light.probe; + const reflectProbe: boolean = !!(sceneData.flags & SceneFlags.REFLECTION_PROBE); + const worldBounds: boolean = !!(sceneData.cullingFlags & CullingFlags.WORLD_BOUNDS); const shadeLight = sceneData.shadingLight; if (camera) { // camera @@ -83,8 +84,9 @@ function computeCullingKey ( hashCode = hashCombineStr(`cast${castShadows}`, hashCode); hashCode = hashCombineStr(`level${lightLevel}`, hashCode); if (reflectProbe) { - hashCode = hashCombineStr(`probe${reflectProbe.getProbeId()}`, hashCode); + hashCode = hashCombineStr(`probe${reflectProbe}`, hashCode); } + hashCode = hashCombineStr(`wb${worldBounds}`, hashCode); hashCode = hashCombineStr(`refId${refId}`, hashCode); return hashCode; } @@ -162,7 +164,7 @@ function isReflectProbeMask (model: Model): boolean { } const transWorldBounds = new AABB(); -function isFrustumVisible (model: Model, frustum: Readonly, castShadow: boolean): boolean { +function isFrustumCulled (model: Model, frustum: Readonly, castShadow: boolean): boolean { const modelWorldBounds = model.worldBounds; if (!modelWorldBounds) { return false; @@ -175,7 +177,7 @@ function isFrustumVisible (model: Model, frustum: Readonly, castShadow: return !intersect.aabbFrustum(transWorldBounds, frustum); } -function isIntersectAABB (lAABB: AABB, rAABB: AABB): boolean { +function isAABBCulled (lAABB: AABB, rAABB: AABB): boolean { return !intersect.aabbWithAABB(lAABB, rAABB); } @@ -184,12 +186,13 @@ function sceneCulling ( camera: Camera, camOrLightFrustum: Readonly, castShadow: boolean, - probe: ReflectionProbe | null, + probePass: boolean, models: Array, + worldBounds: AABB | null, ): void { const skybox = pSceneData.skybox; const skyboxModel = skybox.model; - const visibility = camera.visibility; + const visibility = probePass ? REFLECTION_PROBE_DEFAULT_MASK : camera.visibility; const camSkyboxFlag = camera.clearFlag & SKYBOX_FLAG; if (!castShadow && skybox && skybox.enabled && skyboxModel && camSkyboxFlag) { models.push(skyboxModel); @@ -203,21 +206,33 @@ function sceneCulling ( if (scene && scene.isCulledByLod(camera, model)) { continue; } - if (!probe || (probe && probe.probeType === ProbeType.CUBE)) { - if (isNodeVisible(model.node, visibility) - || isModelVisible(model, visibility)) { - const wBounds = model.worldBounds; - // frustum culling - if (wBounds && ((!probe && isFrustumVisible(model, camOrLightFrustum, castShadow)) - || (probe && isIntersectAABB(wBounds, probe.boundingBox!)))) { + if (!isNodeVisible(model.node, visibility) && !isModelVisible(model, visibility)) { + continue; + } + const wBounds = model.worldBounds; + if (!wBounds) { + continue; + } + if (probePass) { + if (!model.bakeToReflectionProbe) { + continue; + } + if (worldBounds) { + if (isAABBCulled(wBounds, worldBounds)) { continue; } - - models.push(model); + } else if (isFrustumCulled(model, camOrLightFrustum, castShadow)) { + continue; + } + } else { + if (isFrustumCulled(model, camOrLightFrustum, castShadow)) { + continue; + } + if (worldBounds && isAABBCulled(wBounds, worldBounds)) { + continue; } - } else if (isReflectProbeMask(model)) { - models.push(model); } + models.push(model); } } @@ -254,7 +269,7 @@ function addRenderObject ( ): void { const probeQueue = queue.probeQueue; if (isDrawProbe) { - probeQueue.applyMacro(model, phaseLayoutId); + probeQueue.addToProbeQueue(model, phaseLayoutId); } const subModels = model.subModels; const subModelCount = subModels.length; @@ -518,28 +533,24 @@ export class SceneCulling { const light = sceneData.light.light; const level = sceneData.light.level; const castShadow = cullingKey.castShadows; - const probe = sceneData.light.probe; - const camera = probe ? probe.camera : sceneData.camera; + const probePass = !!(sceneData.flags & SceneFlags.REFLECTION_PROBE); + const camera = sceneData.camera; assert(frustomCulledResultID < this.frustumCullingResults.length); const models = this.frustumCullingResults[frustomCulledResultID]; - if (probe) { - sceneCulling(scene, camera, camera.frustum, castShadow, probe, models); - continue; - } if (light) { switch (light.type) { case LightType.SPOT: - sceneCulling(scene, camera, (light as SpotLight).frustum, castShadow, null, models); + sceneCulling(scene, camera, (light as SpotLight).frustum, castShadow, probePass, models, sceneData.worldBounds); break; case LightType.DIRECTIONAL: { const frustum = this.getBuiltinShadowFrustum(pplSceneData, camera, light as DirectionalLight, level); - sceneCulling(scene, camera, frustum, castShadow, null, models); + sceneCulling(scene, camera, frustum, castShadow, probePass, models, sceneData.worldBounds); } break; default: } } else { - sceneCulling(scene, camera, camera.frustum, castShadow, null, models); + sceneCulling(scene, camera, camera.frustum, castShadow, probePass, models, sceneData.worldBounds); } } } diff --git a/cocos/rendering/custom/web-pipeline-types.ts b/cocos/rendering/custom/web-pipeline-types.ts index 66a357cc1e3..7bbc9be648a 100644 --- a/cocos/rendering/custom/web-pipeline-types.ts +++ b/cocos/rendering/custom/web-pipeline-types.ts @@ -225,6 +225,17 @@ export class ProbeHelperQueue { this.probeMap.length = 0; } + applyMacro (): void { + for (const subModel of this.probeMap) { + let patches: IMacroPatch[] = [ + { name: CC_USE_RGBE_OUTPUT, value: true }, + ]; + if (subModel.patches) { + patches = patches.concat(subModel.patches); + } + subModel.onMacroPatchesStateChanged(patches); + } + } removeMacro (): void { for (const subModel of this.probeMap) { if (!subModel.patches) continue; @@ -238,7 +249,7 @@ export class ProbeHelperQueue { } } } - applyMacro (model: Model, probeLayoutId: number): void { + addToProbeQueue (model: Model, probeLayoutId: number): void { const subModels = model.subModels; for (let j = 0; j < subModels.length; j++) { const subModel: SubModel = subModels[j]; @@ -258,13 +269,6 @@ export class ProbeHelperQueue { } if (passIdx < 0) { continue; } if (!bUseReflectPass) { - let patches: IMacroPatch[] = []; - patches = patches.concat(subModel.patches!); - const useRGBEPatchs: IMacroPatch[] = [ - { name: CC_USE_RGBE_OUTPUT, value: true }, - ]; - patches = patches.concat(useRGBEPatchs); - subModel.onMacroPatchesStateChanged(patches); this.probeMap.push(subModel); } } diff --git a/cocos/rendering/custom/web-pipeline.ts b/cocos/rendering/custom/web-pipeline.ts index 16c6a47a8f6..9f5f8c6e0c4 100644 --- a/cocos/rendering/custom/web-pipeline.ts +++ b/cocos/rendering/custom/web-pipeline.ts @@ -53,6 +53,7 @@ import { Director } from '../../game'; import { ReflectionProbeManager } from '../../3d'; import { legacyCC } from '../../core/global-exports'; import { WebSetter, setCameraUBOValues, setShadowUBOLightView, setShadowUBOView, setTextureUBOView } from './web-pipeline-types'; +import { AABB } from '../../core/geometry/aabb'; const _uboVec = new Vec4(); const _samplerPointInfo = new SamplerInfo( @@ -221,6 +222,10 @@ export class WebSceneBuilder extends WebSetter implements SceneBuilder { const layoutName = this._renderGraph.getLayout(passId); setShadowUBOLightView(this, this._scene.camera, light, csmLevel, layoutName); } + setCullingWorldBounds (aabb: AABB): void { + this._scene.cullingFlags |= CullingFlags.WORLD_BOUNDS; + this._scene.worldBounds = aabb; + } private _renderGraph: RenderGraph; private _scene: SceneData; } diff --git a/editor/assets/default_renderpipeline/builtin-pipeline-settings.ts b/editor/assets/default_renderpipeline/builtin-pipeline-settings.ts index 8b02a4affa5..97e6bdcfb8c 100644 --- a/editor/assets/default_renderpipeline/builtin-pipeline-settings.ts +++ b/editor/assets/default_renderpipeline/builtin-pipeline-settings.ts @@ -319,6 +319,7 @@ export class BuiltinPipelineSettings extends Component { group: { id: 'Bloom', name: 'Bloom (PostProcessing)', style: 'section' }, type: CCFloat, min: 0, + step: 0.01, }) set bloomThreshold(value: number) { this._settings.bloom.threshold = value; @@ -332,6 +333,7 @@ export class BuiltinPipelineSettings extends Component { group: { id: 'Bloom', name: 'Bloom (PostProcessing)', style: 'section' }, type: CCFloat, min: 0, + visible: false, }) set bloomIntensity(value: number) { this._settings.bloom.intensity = value; diff --git a/editor/assets/default_renderpipeline/builtin-pipeline.ts b/editor/assets/default_renderpipeline/builtin-pipeline.ts index 25e82886d65..0b8fcb78b61 100644 --- a/editor/assets/default_renderpipeline/builtin-pipeline.ts +++ b/editor/assets/default_renderpipeline/builtin-pipeline.ts @@ -154,6 +154,7 @@ class CameraConfigs { enableFXAA = false; enableFSR = false; enableHDR = false; + enablePlanarReflectionProbe = false; useFullPipeline = false; singleForwardRadiancePass = false; radianceFormat = gfx.Format.RGBA8; @@ -212,6 +213,8 @@ function setupCameraConfigs( && !!camera.scene.mainLight && camera.scene.mainLight.shadowEnabled; + cameraConfigs.enablePlanarReflectionProbe = isMainGameWindow || camera.cameraUsage === CameraUsage.SCENE_VIEW; + cameraConfigs.enableProfiler = DEBUG && isMainGameWindow; cameraConfigs.settings = camera.pipelineSettings @@ -1437,7 +1440,8 @@ if (rendering) { colorName: string, depthStencilName: string, mainLight: renderer.scene.DirectionalLight | null, - scene: renderer.RenderScene | null = null, + scene: renderer.RenderScene | null, + probe?: renderer.scene.ReflectionProbe, ): void { // set viewport const colorStoreOp = this._cameraConfigs.enableMSAA ? StoreOp.DISCARD : StoreOp.STORE; @@ -1479,11 +1483,14 @@ if (rendering) { // 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 + const builder = 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); + if (probe) { + builder.setCullingWorldBounds(probe.boundingBox); + } } private _tryAddReflectionProbePasses(ppl: rendering.BasicPipeline, id: number, @@ -1506,6 +1513,9 @@ if (rendering) { const height = Math.max(Math.floor(area.y), 1); if (probe.probeType === renderer.scene.ProbeType.PLANAR) { + if (!this._cameraConfigs.enablePlanarReflectionProbe) { + continue; + } const window: renderer.RenderWindow = probe.realtimePlanarTexture!.window!; const colorName = `PlanarProbeRT${probeID}`; const depthStencilName = `PlanarProbeDS${probeID}`; @@ -1536,7 +1546,7 @@ if (rendering) { const probePass = ppl.addRenderPass(width, height, 'default'); probePass.name = `CubeProbe${probeID}${faceIdx}`; this._buildReflectionProbePass(probePass, id, probe.camera, - colorName, depthStencilName, mainLight, scene); + colorName, depthStencilName, mainLight, scene, probe); } probe.needRender = false; } diff --git a/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h b/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h index bddd3632c1f..1d8321f4442 100644 --- a/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h +++ b/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h @@ -196,6 +196,7 @@ class NativeSceneBuilder final : public SceneBuilder, public NativeSetter { } void useLightFrustum(IntrusivePtr light, uint32_t csmLevel, const scene::Camera *optCamera) override; + void setCullingWorldBounds(const geometry::AABB &aabb) override; }; class NativeRenderSubpassBuilderImpl : public NativeSetter { diff --git a/native/cocos/renderer/pipeline/custom/NativeRenderGraph.cpp b/native/cocos/renderer/pipeline/custom/NativeRenderGraph.cpp index 9753b4d6077..23d35ce4b9f 100644 --- a/native/cocos/renderer/pipeline/custom/NativeRenderGraph.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeRenderGraph.cpp @@ -725,7 +725,8 @@ CullingFlags makeCullingFlags(const LightInfo &light) { void NativeRenderQueueBuilder::addSceneOfCamera( scene::Camera *camera, LightInfo light, SceneFlags sceneFlags) { const auto *pLight = light.light.get(); - SceneData scene(camera->getScene(), camera, sceneFlags, light, makeCullingFlags(light), light.light); + SceneData scene(camera->getScene(), camera, sceneFlags, light, + makeCullingFlags(light), light.light, geometry::AABB{}); auto sceneID = addVertex2( SceneTag{}, std::forward_as_tuple("Camera"), @@ -809,6 +810,12 @@ void NativeSceneBuilder::useLightFrustum( } } +void NativeSceneBuilder::setCullingWorldBounds(const geometry::AABB& aabb) { + auto &sceneData = get(SceneTag{}, nodeID, *renderGraph); + sceneData.cullingFlags |= CullingFlags::WORLD_BOUNDS; + sceneData.worldBounds = aabb; +} + SceneBuilder *NativeRenderQueueBuilder::addScene( const scene::Camera *camera, SceneFlags sceneFlags, scene::Light *light, scene::RenderScene *scene) { @@ -826,7 +833,8 @@ SceneBuilder *NativeRenderQueueBuilder::addScene( // Objects are projected to camera by default and are culled further if light is available. light ? CullingFlags::CAMERA_FRUSTUM | CullingFlags::LIGHT_BOUNDS : CullingFlags::CAMERA_FRUSTUM, - light), + light, + geometry::AABB{}), *renderGraph, nodeID); CC_ENSURES(sceneID != RenderGraph::null_vertex()); diff --git a/native/cocos/renderer/pipeline/custom/RenderGraphTypes.h b/native/cocos/renderer/pipeline/custom/RenderGraphTypes.h index 7f7ee8197d3..abbf63a216f 100644 --- a/native/cocos/renderer/pipeline/custom/RenderGraphTypes.h +++ b/native/cocos/renderer/pipeline/custom/RenderGraphTypes.h @@ -878,6 +878,7 @@ enum class CullingFlags : uint32_t { CAMERA_FRUSTUM = 0x1, LIGHT_FRUSTUM = 0x2, LIGHT_BOUNDS = 0x4, + WORLD_BOUNDS = 0x8, }; constexpr CullingFlags operator|(const CullingFlags lhs, const CullingFlags rhs) noexcept { @@ -910,13 +911,14 @@ constexpr bool any(CullingFlags e) noexcept { struct SceneData { SceneData() = default; - SceneData(const scene::RenderScene* sceneIn, const scene::Camera* cameraIn, SceneFlags flagsIn, LightInfo lightIn, CullingFlags cullingFlagsIn, IntrusivePtr shadingLightIn) noexcept + SceneData(const scene::RenderScene* sceneIn, const scene::Camera* cameraIn, SceneFlags flagsIn, LightInfo lightIn, CullingFlags cullingFlagsIn, IntrusivePtr shadingLightIn, geometry::AABB worldBoundsIn) noexcept : scene(sceneIn), camera(cameraIn), light(std::move(lightIn)), flags(flagsIn), cullingFlags(cullingFlagsIn), - shadingLight(std::move(shadingLightIn)) {} + shadingLight(std::move(shadingLightIn)), + worldBounds(worldBoundsIn) {} const scene::RenderScene* scene{nullptr}; const scene::Camera* camera{nullptr}; @@ -924,6 +926,7 @@ struct SceneData { SceneFlags flags{SceneFlags::NONE}; CullingFlags cullingFlags{CullingFlags::CAMERA_FRUSTUM}; IntrusivePtr shadingLight; + geometry::AABB worldBounds; }; struct Dispatch { diff --git a/native/cocos/renderer/pipeline/custom/RenderInterfaceTypes.h b/native/cocos/renderer/pipeline/custom/RenderInterfaceTypes.h index 2c69907cbd0..b48b60d0af0 100644 --- a/native/cocos/renderer/pipeline/custom/RenderInterfaceTypes.h +++ b/native/cocos/renderer/pipeline/custom/RenderInterfaceTypes.h @@ -569,6 +569,7 @@ class SceneBuilder : public Setter { * @param optCamera @en Additional scene culling camera. @zh 额外的场景裁切相机 */ virtual void useLightFrustum(IntrusivePtr light, uint32_t csmLevel, const scene::Camera *optCamera) = 0; + virtual void setCullingWorldBounds(const geometry::AABB &aabb) = 0; void useLightFrustum(IntrusivePtr light) { useLightFrustum(std::move(light), 0, nullptr); }