From c78709dbc7a07b3ae857e10ebe21e72c1e183f2d Mon Sep 17 00:00:00 2001 From: xubing <58895777@qq.com> Date: Tue, 4 Aug 2026 17:07:56 +0800 Subject: [PATCH 01/11] add transparent object support for reflection probe --- cc.config.json | 8 +- .../reflection-probe-component.ts | 24 ++++ .../scene/reflection-probe.jsb.ts | 86 ++++++++++++- cocos/render-scene/scene/reflection-probe.ts | 116 +++++++++++++++++- cocos/rendering/custom/scene-culling.ts | 4 + cocos/rendering/custom/web-pipeline-types.ts | 17 ++- cocos/rendering/forward/forward-pipeline.ts | 8 ++ .../reflection-probe/reflection-probe-flow.ts | 10 +- .../reflection-probe-stage.ts | 104 ++++++++++++++-- .../render-reflection-probe-queue.ts | 51 ++++++-- .../chunks/legacy/output-standard.chunk | 4 +- .../misc/silhouette-edge-fs.chunk | 4 +- .../main-functions/misc/sky-fs.chunk | 4 +- .../render-to-reflectmap/fs.chunk | 8 +- .../render-to-scene/pipeline/forward-fs.chunk | 4 +- .../builtin-pipeline.ts | 88 +++++++++++-- .../pipeline/probe-rgbe-convert.effect | 45 +++++++ .../pipeline/probe-rgbe-convert.effect.meta | 11 ++ editor/assets/effects/pipeline/skybox.effect | 4 +- 19 files changed, 544 insertions(+), 56 deletions(-) create mode 100644 editor/assets/effects/pipeline/probe-rgbe-convert.effect create mode 100644 editor/assets/effects/pipeline/probe-rgbe-convert.effect.meta diff --git a/cc.config.json b/cc.config.json index 9970fe42df..8bd398773a 100644 --- a/cc.config.json +++ b/cc.config.json @@ -257,12 +257,13 @@ "ff9b0199-ce04-4cfe-86cc-6c719f08d6e4", "de1c2107-70c8-4021-8459-6399f24d01c6", "cbf30902-517f-40dc-af90-a550bac27cf1", - "5c601d96-e4c7-4698-991b-7ee674b11079" + "5c601d96-e4c7-4698-991b-7ee674b11079", + "a3f78b2e-6c91-4d5a-b7f3-8e2d4a1c0b96" ] }, "custom-pipeline-post-process": { "modules": ["custom-pipeline-post-process"], - "dependentAssets":[ + "dependentAssets":[ "15049ccd-4dd7-451e-a8ae-af66735c929e", "521c5f6e-1a26-42e2-8108-4400c912d9bf", "4c3ce6de-e6d1-47f7-aa36-36b9b58f72d3", @@ -277,7 +278,8 @@ "45e7c0c8-2699-4912-b45f-d42bb8384189", "84ac6f69-3086-455a-86a4-561da8ee710b", "5c601d96-e4c7-4698-991b-7ee674b11079", - "bf0a6d94-58f0-4ad2-bdf2-c4a31c7dd856" + "bf0a6d94-58f0-4ad2-bdf2-c4a31c7dd856", + "a3f78b2e-6c91-4d5a-b7f3-8e2d4a1c0b96" ], "dependentModules": ["custom-pipeline"] }, diff --git a/cocos/3d/reflection-probe/reflection-probe-component.ts b/cocos/3d/reflection-probe/reflection-probe-component.ts index 5c5bb878c3..e1af1a53d2 100644 --- a/cocos/3d/reflection-probe/reflection-probe-component.ts +++ b/cocos/3d/reflection-probe/reflection-probe-component.ts @@ -98,6 +98,9 @@ export class ReflectionProbe extends Component { @serializable private _fastBake = false; + @serializable + private _supportTransparency = false; + protected _probe: scene.ReflectionProbe | null = null; protected _previewSphere: Node | null = null; @@ -266,6 +269,26 @@ export class ReflectionProbe extends Component { this._fastBake = val; } + /** + * @en Whether to support transparent objects in reflection probe rendering. + * When enabled, uses RGBA16F intermediate RT for correct alpha blending. + * @zh 是否支持半透明物体参与反射探针渲染。 + * 开启后使用 RGBA16F 中间 RT 以支持正确的 alpha 混合。 + */ + @visible(function (this: ReflectionProbe) { return this.probeType === ProbeType.CUBE; }) + @type(CCBoolean) + @tooltip('i18n:reflection_probe.supportTransparency') + get supportTransparency (): boolean { + return this._supportTransparency; + } + + set supportTransparency (val) { + this._supportTransparency = val; + if (this._probe) { + this._probe.supportTransparency = val; + } + } + set cubemap (val: TextureCube | null) { this._cubemap = val; this.probe.cubemap = val; @@ -441,6 +464,7 @@ export class ReflectionProbe extends Component { this._probe.probeType = this._probeType; this._probe.size = this._size; this._probe.cubemap = this._cubemap!; + this._probe.supportTransparency = this._supportTransparency; } } } diff --git a/cocos/render-scene/scene/reflection-probe.jsb.ts b/cocos/render-scene/scene/reflection-probe.jsb.ts index 27824d8cad..a2eb34c4a8 100644 --- a/cocos/render-scene/scene/reflection-probe.jsb.ts +++ b/cocos/render-scene/scene/reflection-probe.jsb.ts @@ -21,12 +21,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { ClearFlagBit } from '../../gfx'; +import { ClearFlagBit, Texture, Framebuffer, RenderPass, + ColorAttachment, DepthStencilAttachment, RenderPassInfo, + TextureInfo, TextureType, TextureUsageBit, Format, LoadOp, StoreOp, + FramebufferInfo } from '../../gfx'; import { SKYBOX_FLAG } from './camera'; import type { ReflectionProbe as JsbReflectionProbe } from './reflection-probe'; +import { cclegacy } from '../../core'; declare const jsb: any; - + export enum ProbeClearFlag { SKYBOX = SKYBOX_FLAG | ClearFlagBit.DEPTH_STENCIL, SOLID_COLOR = ClearFlagBit.ALL, @@ -42,4 +46,82 @@ export type ReflectionProbe = JsbReflectionProbe; const reflectionProbeProto: any = jsb.ReflectionProbe.prototype; reflectionProbeProto._ctor = function (id:number) { this._probeId = id; + this._supportTransparency = false; + this.intermediateTextures = []; + this.intermediateFramebuffers = []; + this._intermediateRenderPass = null; + this._intermediateDepthStencilTex = null; +}; + +Object.defineProperty(reflectionProbeProto, 'supportTransparency', { + get (this: any): boolean { + return this._supportTransparency; + }, + set (this: any, value: boolean) { + this._supportTransparency = value; + }, +}); + +reflectionProbeProto.useFloatIntermediateRT = function (this: any): boolean { + return this._supportTransparency; +}; + +reflectionProbeProto.initIntermediateTextures = function (this: any, width: number, height: number, count: number): void { + reflectionProbeProto._destroyIntermediateTextures.call(this); + + const root = cclegacy.director.root; + const device = root.device; + + const colorAttachment = new ColorAttachment(); + colorAttachment.format = Format.RGBA16F; + colorAttachment.loadOp = LoadOp.CLEAR; + colorAttachment.storeOp = StoreOp.STORE; + + const depthStencilAttachment = new DepthStencilAttachment(); + depthStencilAttachment.format = Format.DEPTH_STENCIL; + depthStencilAttachment.depthLoadOp = LoadOp.CLEAR; + depthStencilAttachment.depthStoreOp = StoreOp.STORE; + depthStencilAttachment.stencilLoadOp = LoadOp.CLEAR; + depthStencilAttachment.stencilStoreOp = StoreOp.STORE; + + const renderPassInfo = new RenderPassInfo([colorAttachment], depthStencilAttachment); + this._intermediateRenderPass = device.createRenderPass(renderPassInfo) as RenderPass; + + this._intermediateDepthStencilTex = device.createTexture(new TextureInfo( + TextureType.TEX2D, TextureUsageBit.DEPTH_STENCIL_ATTACHMENT, Format.DEPTH_STENCIL, width, height, + )) as Texture; + + for (let i = 0; i < count; i++) { + const colorTex: Texture = device.createTexture(new TextureInfo( + TextureType.TEX2D, TextureUsageBit.COLOR_ATTACHMENT | TextureUsageBit.SAMPLED, Format.RGBA16F, width, height, + )) as Texture; + this.intermediateTextures.push(colorTex); + + const fb: Framebuffer = device.createFramebuffer(new FramebufferInfo( + this._intermediateRenderPass, [colorTex], this._intermediateDepthStencilTex, + )); + this.intermediateFramebuffers.push(fb); + } +}; + +reflectionProbeProto._destroyIntermediateTextures = function (this: any): void { + for (let i = 0; i < this.intermediateFramebuffers.length; i++) { + this.intermediateFramebuffers[i].destroy(); + } + this.intermediateFramebuffers.length = 0; + + for (let i = 0; i < this.intermediateTextures.length; i++) { + this.intermediateTextures[i].destroy(); + } + this.intermediateTextures.length = 0; + + if (this._intermediateDepthStencilTex) { + this._intermediateDepthStencilTex.destroy(); + this._intermediateDepthStencilTex = null; + } + + if (this._intermediateRenderPass) { + this._intermediateRenderPass.destroy(); + this._intermediateRenderPass = null; + } }; diff --git a/cocos/render-scene/scene/reflection-probe.ts b/cocos/render-scene/scene/reflection-probe.ts index b09a9dd0a4..afd79705c7 100644 --- a/cocos/render-scene/scene/reflection-probe.ts +++ b/cocos/render-scene/scene/reflection-probe.ts @@ -26,7 +26,14 @@ import { Camera, CameraAperture, CameraFOVAxis, CameraISO, CameraProjection, Cam import { Node } from '../../scene-graph/node'; import { Color, Quat, Rect, toRadian, Vec2, Vec3, geometry, cclegacy, Vec4, Size, v3, quat } from '../../core'; import { CAMERA_DEFAULT_MASK } from '../../rendering/define'; -import { ClearFlagBit, Framebuffer } from '../../gfx'; +import { + ClearFlagBit, Framebuffer, + ColorAttachment, DepthStencilAttachment, + Format, LoadOp, StoreOp, + RenderPassInfo, RenderPass, + Texture, TextureInfo, TextureType, TextureUsageBit, + FramebufferInfo, +} from '../../gfx'; import { TextureCube } from '../../asset/assets/texture-cube'; import { RenderTexture } from '../../asset/assets/render-texture'; @@ -123,6 +130,13 @@ export class ReflectionProbe { protected _previewPlane: Node | null = null; + private _supportTransparency = false; + private _intermediateRenderPass: RenderPass | null = null; + private _intermediateDepthStencil: Texture | null = null; + + public intermediateTextures: Texture[] = []; + public intermediateFramebuffers: Framebuffer[] = []; + /** * @en Set probe type,cube or planar. * @zh 设置探针类型,cube或者planar @@ -275,6 +289,24 @@ export class ReflectionProbe { return this._previewPlane!; } + set supportTransparency (value: boolean) { + this._supportTransparency = value; + } + get supportTransparency (): boolean { + return this._supportTransparency; + } + + /** + * @en Whether to use RGBA16F intermediate render target for transparency support. + * Callers should also check device capability (supportsRGBA16HalfFloatTexture) before using the float path. + * @zh 是否使用 RGBA16F 中间渲染目标以支持半透明渲染。 + * 当前等价于 supportTransparency,未来可能加入设备能力等额外判断条件。 + * 调用方在实际使用浮点路径前,还需自行检查设备是否支持 RGBA16F。 + */ + public useFloatIntermediateRT (): boolean { + return this._supportTransparency; + } + constructor (id: number) { this._probeId = id; } @@ -295,6 +327,9 @@ export class ReflectionProbe { this.bakedCubeTextures.push(renderTexture); } } + if (this._supportTransparency && this.intermediateTextures.length === 0) { + this.initIntermediateTextures(this._resolution, this._resolution, 6); + } } public captureCubemap (): void { @@ -314,6 +349,9 @@ export class ReflectionProbe { const canvasSize = cclegacy.view.getDesignResolutionSize() as Size; this.realtimePlanarTexture = this._createTargetTexture(canvasSize.width, canvasSize.height); cclegacy.internal.reflectionProbeManager.updatePlanarMap(this, this.realtimePlanarTexture.getGFXTexture()); + if (this._supportTransparency) { + this.initIntermediateTextures(canvasSize.width, canvasSize.height, 1); + } } this._syncCameraParams(sourceCamera); this._transformReflectionCamera(sourceCamera); @@ -366,6 +404,8 @@ export class ReflectionProbe { this.realtimePlanarTexture.destroy(); this.realtimePlanarTexture = null; } + + this._destroyIntermediateTextures(); } // eslint-disable-next-line @typescript-eslint/no-empty-function public enable (): void { @@ -472,6 +512,80 @@ export class ReflectionProbe { this.camera.update(true); } + /** + * @en Create RGBA16F intermediate textures and framebuffers using GFX API directly. + * @zh 直接使用 GFX API 创建 RGBA16F 中间纹理和 Framebuffer。 + */ + public initIntermediateTextures (width: number, height: number, count: number): void { + this._destroyIntermediateTextures(); + + const root = cclegacy.director.root; + const device = root.device; + + const colorAttachment = new ColorAttachment(); + colorAttachment.format = Format.RGBA16F; + colorAttachment.loadOp = LoadOp.CLEAR; + colorAttachment.storeOp = StoreOp.STORE; + + const depthStencilAttachment = new DepthStencilAttachment(); + depthStencilAttachment.format = Format.DEPTH_STENCIL; + depthStencilAttachment.depthLoadOp = LoadOp.CLEAR; + depthStencilAttachment.depthStoreOp = StoreOp.STORE; + depthStencilAttachment.stencilLoadOp = LoadOp.CLEAR; + depthStencilAttachment.stencilStoreOp = StoreOp.STORE; + + const renderPassInfo = new RenderPassInfo([colorAttachment], depthStencilAttachment); + this._intermediateRenderPass = device.createRenderPass(renderPassInfo) as RenderPass; + + this._intermediateDepthStencil = device.createTexture(new TextureInfo( + TextureType.TEX2D, + TextureUsageBit.DEPTH_STENCIL_ATTACHMENT, + Format.DEPTH_STENCIL, + width, + height, + )) as Texture; + + for (let i = 0; i < count; i++) { + const colorTex: Texture = device.createTexture(new TextureInfo( + TextureType.TEX2D, + TextureUsageBit.COLOR_ATTACHMENT | TextureUsageBit.SAMPLED, + Format.RGBA16F, + width, + height, + )) as Texture; + this.intermediateTextures.push(colorTex); + + const fb: Framebuffer = device.createFramebuffer(new FramebufferInfo( + this._intermediateRenderPass, + [colorTex], + this._intermediateDepthStencil, + )); + this.intermediateFramebuffers.push(fb); + } + } + + private _destroyIntermediateTextures (): void { + for (let i = 0; i < this.intermediateFramebuffers.length; i++) { + this.intermediateFramebuffers[i].destroy(); + } + this.intermediateFramebuffers.length = 0; + + for (let i = 0; i < this.intermediateTextures.length; i++) { + this.intermediateTextures[i].destroy(); + } + this.intermediateTextures.length = 0; + + if (this._intermediateDepthStencil) { + this._intermediateDepthStencil.destroy(); + this._intermediateDepthStencil = null; + } + + if (this._intermediateRenderPass) { + this._intermediateRenderPass.destroy(); + this._intermediateRenderPass = null; + } + } + private _createTargetTexture (width: number, height: number): RenderTexture { const rt = new RenderTexture(); rt.reset({ width, height }); diff --git a/cocos/rendering/custom/scene-culling.ts b/cocos/rendering/custom/scene-culling.ts index d44e927b7a..1ccb1d4eb8 100644 --- a/cocos/rendering/custom/scene-culling.ts +++ b/cocos/rendering/custom/scene-culling.ts @@ -477,6 +477,10 @@ export class SceneCulling { // Get or create render queue const renderQueueID = this.getOrCreateRenderQueue(renderQueueKey, sceneData.flags, sceneData.camera); + if (sceneData.light.probe && sceneData.light.probe.useFloatIntermediateRT()) { + this.renderQueues[renderQueueID].probeQueue.useFloatOutput = true; + } + // add render queue query const renderQueueQuery = this.cullingPools.renderQueueQueryRecycle.add(); renderQueueQuery.update(frustumCulledResultID, lightBoundsCullingID, renderQueueID); diff --git a/cocos/rendering/custom/web-pipeline-types.ts b/cocos/rendering/custom/web-pipeline-types.ts index d52bd8489c..13519e943a 100644 --- a/cocos/rendering/custom/web-pipeline-types.ts +++ b/cocos/rendering/custom/web-pipeline-types.ts @@ -203,6 +203,7 @@ export class DrawInstance { export const instancePool = new RecyclePool(() => new DrawInstance(), 8); const CC_USE_RGBE_OUTPUT = 'CC_USE_RGBE_OUTPUT'; +const CC_USE_FLOAT_PROBE_OUTPUT = 'CC_USE_FLOAT_PROBE_OUTPUT'; function getLayoutId (passLayout: string, phaseLayout: string): number { const r = cclegacy.rendering; // eslint-disable-next-line @typescript-eslint/no-unsafe-return @@ -221,15 +222,18 @@ function getPassIndexFromLayout (subModel: SubModel, phaseLayoutId: number): num export class ProbeHelperQueue { probeMap: Array = new Array(); defaultId: number = getLayoutId('default', 'default'); + useFloatOutput = false; clear (): void { this.probeMap.length = 0; + this.useFloatOutput = false; } applyMacro (): void { + const macroName = this.useFloatOutput ? CC_USE_FLOAT_PROBE_OUTPUT : CC_USE_RGBE_OUTPUT; for (const subModel of this.probeMap) { let patches: IMacroPatch[] = [ - { name: CC_USE_RGBE_OUTPUT, value: true }, + { name: macroName, value: true }, ]; if (subModel.patches) { patches = patches.concat(subModel.patches); @@ -241,7 +245,7 @@ export class ProbeHelperQueue { for (const subModel of this.probeMap) { if (!subModel.patches) continue; const patches = subModel.patches.filter( - (patch) => patch.name !== CC_USE_RGBE_OUTPUT, + (patch) => patch.name !== CC_USE_RGBE_OUTPUT && patch.name !== CC_USE_FLOAT_PROBE_OUTPUT, ); if (patches.length === 0) { subModel.onMacroPatchesStateChanged(null); @@ -255,10 +259,11 @@ export class ProbeHelperQueue { for (let j = 0; j < subModels.length; j++) { const subModel: SubModel = subModels[j]; - //Filter transparent objects - const isTransparent = subModel.passes[0].blendState.targets[0].blend; - if (isTransparent) { - continue; + if (!this.useFloatOutput) { + const isTransparent = subModel.passes[0].blendState.targets[0].blend; + if (isTransparent) { + continue; + } } let passIdx = getPassIndexFromLayout(subModel, probeLayoutId); diff --git a/cocos/rendering/forward/forward-pipeline.ts b/cocos/rendering/forward/forward-pipeline.ts index 92ffc17bda..d7716666c9 100644 --- a/cocos/rendering/forward/forward-pipeline.ts +++ b/cocos/rendering/forward/forward-pipeline.ts @@ -145,6 +145,14 @@ export class ForwardPipeline extends RenderPipeline { descriptorSet.update(); + const inputAssemblerData = this._createQuadInputAssembler(); + if (!inputAssemblerData.quadIB || !inputAssemblerData.quadVB || !inputAssemblerData.quadIA) { + return false; + } + this._quadVBOffscreen = inputAssemblerData.quadVB; + this._quadIB = inputAssemblerData.quadIB; + this._quadIAOffscreen = inputAssemblerData.quadIA; + return true; } diff --git a/cocos/rendering/reflection-probe/reflection-probe-flow.ts b/cocos/rendering/reflection-probe/reflection-probe-flow.ts index 3ceabf5a83..a663bb97a9 100644 --- a/cocos/rendering/reflection-probe/reflection-probe-flow.ts +++ b/cocos/rendering/reflection-probe/reflection-probe-flow.ts @@ -76,20 +76,24 @@ export class ReflectionProbeFlow extends RenderFlow { super.destroy(); } private _renderStage (camera: Camera, probe: ReflectionProbe): void { + const useFloatRT = probe.useFloatIntermediateRT() && probe.intermediateFramebuffers.length > 0; for (let i = 0; i < this._stages.length; i++) { const probeStage = this._stages[i] as ReflectionProbeStage; if (probe.probeType === ProbeType.PLANAR) { cclegacy.internal.reflectionProbeManager.updatePlanarMap(probe, null); - probeStage.setUsageInfo(probe, probe.realtimePlanarTexture!.window!.framebuffer); + const outputFb = probe.realtimePlanarTexture!.window!.framebuffer; + const fb = useFloatRT ? probe.intermediateFramebuffers[0] : outputFb; + probeStage.setUsageInfo(probe, fb, useFloatRT ? outputFb : null); probeStage.render(camera); cclegacy.internal.reflectionProbeManager.updatePlanarMap(probe, probe.realtimePlanarTexture!.getGFXTexture()); } else { for (let faceIdx = 0; faceIdx < 6; faceIdx++) { const renderTexture = probe.bakedCubeTextures[faceIdx]; if (!renderTexture) return; - //update camera dirction probe.updateCameraDir(faceIdx); - probeStage.setUsageInfo(probe, renderTexture.window!.framebuffer); + const outputFb = renderTexture.window!.framebuffer; + const fb = useFloatRT ? probe.intermediateFramebuffers[faceIdx] : outputFb; + probeStage.setUsageInfo(probe, fb, useFloatRT ? outputFb : null); probeStage.render(camera); } probe.needRender = false; diff --git a/cocos/rendering/reflection-probe/reflection-probe-stage.ts b/cocos/rendering/reflection-probe/reflection-probe-stage.ts index e75430dc17..3fea9a180c 100644 --- a/cocos/rendering/reflection-probe/reflection-probe-stage.ts +++ b/cocos/rendering/reflection-probe/reflection-probe-stage.ts @@ -23,7 +23,7 @@ */ import { ccclass } from 'cc.decorator'; -import { Color, Rect, Framebuffer, ClearFlagBit } from '../../gfx'; +import { Color, Rect, Framebuffer, ClearFlagBit, Device, CommandBuffer } from '../../gfx'; import { IRenderStageInfo, RenderStage } from '../render-stage'; import { ForwardStagePriority } from '../enum'; import { ForwardPipeline } from '../forward/forward-pipeline'; @@ -33,6 +33,8 @@ import { Camera, ReflectionProbe } from '../../render-scene/scene'; import { RenderReflectionProbeQueue } from '../render-reflection-probe-queue'; import { Vec3 } from '../../core'; import { packRGBE } from '../../core/math/color'; +import { Material } from '../../asset/assets/material'; +import { PipelineStateManager } from '../pipeline-state-manager'; const colors: Color[] = [new Color(1, 1, 1, 1)]; @@ -53,10 +55,12 @@ export class ReflectionProbeStage extends RenderStage { }; private _frameBuffer: Framebuffer | null = null; + private _outputFrameBuffer: Framebuffer | null = null; private _renderArea = new Rect(); private _probe: ReflectionProbe | null = null; private _probeRenderQueue!: RenderReflectionProbeQueue; private _rgbeColor = new Vec3(); + private _convertMaterial: Material | null = null; constructor () { super(); @@ -68,9 +72,10 @@ export class ReflectionProbeStage extends RenderStage { * @param probe * @param frameBuffer */ - public setUsageInfo (probe: ReflectionProbe, frameBuffer: Framebuffer): void { + public setUsageInfo (probe: ReflectionProbe, frameBuffer: Framebuffer, outputFrameBuffer: Framebuffer | null = null): void { this._probe = probe; this._frameBuffer = frameBuffer; + this._outputFrameBuffer = outputFrameBuffer; } public destroy (): void { @@ -119,14 +124,21 @@ export class ReflectionProbeStage extends RenderStage { const renderPass = this._frameBuffer!.renderPass; if (this._probe!.camera.clearFlag & ClearFlagBit.COLOR) { - this._rgbeColor.x = this._probe!.camera.clearColor.x; - this._rgbeColor.y = this._probe!.camera.clearColor.y; - this._rgbeColor.z = this._probe!.camera.clearColor.z; - const rgbe = packRGBE(this._rgbeColor); - colors[0].x = rgbe.x; - colors[0].y = rgbe.y; - colors[0].z = rgbe.z; - colors[0].w = rgbe.w; + if (this._probe!.useFloatIntermediateRT()) { + colors[0].x = this._probe!.camera.clearColor.x; + colors[0].y = this._probe!.camera.clearColor.y; + colors[0].z = this._probe!.camera.clearColor.z; + colors[0].w = this._probe!.camera.clearColor.w; + } else { + this._rgbeColor.x = this._probe!.camera.clearColor.x; + this._rgbeColor.y = this._probe!.camera.clearColor.y; + this._rgbeColor.z = this._probe!.camera.clearColor.z; + const rgbe = packRGBE(this._rgbeColor); + colors[0].x = rgbe.x; + colors[0].y = rgbe.y; + colors[0].z = rgbe.z; + colors[0].w = rgbe.w; + } } const device = pipeline.device; cmdBuff.beginRenderPass( @@ -142,9 +154,81 @@ export class ReflectionProbeStage extends RenderStage { this._probeRenderQueue.recordCommandBuffer(device, renderPass, cmdBuff); cmdBuff.endRenderPass(); + if (this._outputFrameBuffer) { + this._renderConvertPass(device, cmdBuff); + } + pipeline.pipelineUBO.updateCameraUBO(camera); } + private _renderConvertPass (device: Device, cmdBuff: CommandBuffer): void { + const mat = this._getConvertMaterial(); + if (!mat || !mat.passes.length) return; + + const pass = mat.passes[0]; + const shader = pass.getShaderVariant(); + if (!pass || !shader) return; + + const fwdPipeline = this._pipeline as ForwardPipeline; + const inputAssembler = fwdPipeline.quadIAOffscreen; + if (!inputAssembler) return; + + const intermediateColorTex = this._frameBuffer!.colorTextures[0]!; + const binding = pass.getBinding('probeColorTex'); + if (binding < 0) return; + + const w = this._renderArea.width; + const h = this._renderArea.height; + const minX = this._renderArea.x / w; + const maxX = (this._renderArea.x + w) / w; + let minY = this._renderArea.y / h; + let maxY = (this._renderArea.y + h) / h; + if (device.capabilities.screenSpaceSignY > 0) { + const temp = maxY; + maxY = minY; + minY = temp; + } + const vbData = new Float32Array(16); + let n = 0; + vbData[n++] = -1.0; vbData[n++] = -1.0; vbData[n++] = minX; vbData[n++] = maxY; + vbData[n++] = 1.0; vbData[n++] = -1.0; vbData[n++] = maxX; vbData[n++] = maxY; + vbData[n++] = -1.0; vbData[n++] = 1.0; vbData[n++] = minX; vbData[n++] = minY; + vbData[n++] = 1.0; vbData[n++] = 1.0; vbData[n++] = maxX; vbData[n++] = minY; + inputAssembler.vertexBuffers[0].update(vbData.buffer); + + const outputRenderPass = this._outputFrameBuffer!.renderPass; + + colors[0].x = 0; + colors[0].y = 0; + colors[0].z = 0; + colors[0].w = 0; + + cmdBuff.beginRenderPass(outputRenderPass, this._outputFrameBuffer!, this._renderArea, colors, 1.0, 0); + cmdBuff.bindDescriptorSet(SetIndex.GLOBAL, fwdPipeline.descriptorSet); + + const sampler = fwdPipeline.globalDSManager.linearSampler; + pass.descriptorSet.bindTexture(binding, intermediateColorTex); + pass.descriptorSet.bindSampler(binding, sampler); + pass.descriptorSet.update(); + cmdBuff.bindDescriptorSet(SetIndex.MATERIAL, pass.descriptorSet); + + const pso = PipelineStateManager.getOrCreatePipelineState(device, pass, shader, outputRenderPass, inputAssembler); + cmdBuff.bindPipelineState(pso); + cmdBuff.bindInputAssembler(inputAssembler); + cmdBuff.draw(inputAssembler); + + cmdBuff.endRenderPass(); + } + + private _getConvertMaterial (): Material { + if (!this._convertMaterial) { + this._convertMaterial = new Material(); + this._convertMaterial._uuid = 'reflection-probe-rgbe-convert-material'; + this._convertMaterial.initialize({ effectName: 'pipeline/probe-rgbe-convert' }); + } + return this._convertMaterial; + } + public activate (pipeline: ForwardPipeline, flow: ReflectionProbeFlow): void { super.activate(pipeline, flow); this._probeRenderQueue = new RenderReflectionProbeQueue(pipeline); diff --git a/cocos/rendering/render-reflection-probe-queue.ts b/cocos/rendering/render-reflection-probe-queue.ts index 77131fa1c3..e4859c79fc 100644 --- a/cocos/rendering/render-reflection-probe-queue.ts +++ b/cocos/rendering/render-reflection-probe-queue.ts @@ -36,6 +36,7 @@ import { RenderInstancedQueue } from './render-instanced-queue'; import { cclegacy, geometry } from '../core'; const CC_USE_RGBE_OUTPUT = 'CC_USE_RGBE_OUTPUT'; +const CC_USE_FLOAT_PROBE_OUTPUT = 'CC_USE_FLOAT_PROBE_OUTPUT'; let _phaseID = getPhaseID('default'); let _phaseReflectMapID = getPhaseID('reflect-map'); function getPassIndex (subModel: SubModel): number { @@ -72,15 +73,20 @@ export class RenderReflectionProbeQueue { private _subModelsArray: SubModel[] = []; private _passArray: Pass[] = []; private _shaderArray: Shader[] = []; + private _transparentSubModelsArray: SubModel[] = []; + private _transparentPassArray: Pass[] = []; + private _transparentShaderArray: Shader[] = []; private _rgbeSubModelsArray: SubModel[] = []; private _instancedQueue: RenderInstancedQueue = new RenderInstancedQueue(); private _patches: IMacroPatch[] = []; + private _useFloatRT = false; public constructor (pipeline: PipelineRuntime) { this._pipeline = pipeline; } public gatherRenderObjects (probe: ReflectionProbe, camera: Camera, cmdBuff: CommandBuffer): void { this.clear(); + this._useFloatRT = probe.useFloatIntermediateRT(); const scene = camera.scene!; const sceneData = this._pipeline.pipelineSceneData; const skybox = sceneData.skybox; @@ -117,6 +123,9 @@ export class RenderReflectionProbeQueue { this._subModelsArray.length = 0; this._shaderArray.length = 0; this._passArray.length = 0; + this._transparentSubModelsArray.length = 0; + this._transparentShaderArray.length = 0; + this._transparentPassArray.length = 0; this._instancedQueue.clear(); this._rgbeSubModelsArray.length = 0; } @@ -126,9 +135,8 @@ export class RenderReflectionProbeQueue { for (let j = 0; j < subModels.length; j++) { const subModel = subModels[j]; - //Filter transparent objects const isTransparent = subModel.passes[0].blendState.targets[0].blend; - if (isTransparent) { + if (isTransparent && !this._useFloatRT) { continue; } @@ -143,21 +151,24 @@ export class RenderReflectionProbeQueue { const pass = subModel.passes[passIdx]; const batchingScheme = pass.batchingScheme; - if (!bUseReflectPass) { + if (!bUseReflectPass || this._useFloatRT) { + const macroName = this._useFloatRT ? CC_USE_FLOAT_PROBE_OUTPUT : CC_USE_RGBE_OUTPUT; this._patches = []; this._patches = this._patches.concat(subModel.patches!); - const useRGBEPatchs: IMacroPatch[] = [ - { name: CC_USE_RGBE_OUTPUT, value: true }, - ]; - this._patches = this._patches.concat(useRGBEPatchs); + this._patches.push({ name: macroName, value: true }); subModel.onMacroPatchesStateChanged(this._patches); this._rgbeSubModelsArray.push(subModel); } - if (batchingScheme === BatchingSchemes.INSTANCING) { // instancing + if (batchingScheme === BatchingSchemes.INSTANCING) { const buffer = pass.getInstancedBuffer(); buffer.merge(subModel, passIdx); this._instancedQueue.queue.add(buffer); + } else if (isTransparent) { + const shader = subModel.shaders[passIdx]; + this._transparentSubModelsArray.push(subModel); + if (shader) this._transparentShaderArray.push(shader); + this._transparentPassArray.push(pass); } else { const shader = subModel.shaders[passIdx]; this._subModelsArray.push(subModel); @@ -188,19 +199,35 @@ export class RenderReflectionProbeQueue { cmdBuff.bindInputAssembler(ia); cmdBuff.draw(ia); } - this.resetRGBEMacro(); + + for (let i = 0; i < this._transparentSubModelsArray.length; ++i) { + const subModel = this._transparentSubModelsArray[i]; + const shader = this._transparentShaderArray[i]; + const pass = this._transparentPassArray[i]; + const ia = subModel.inputAssembler; + const pso = PipelineStateManager.getOrCreatePipelineState(device, pass, shader, renderPass, ia); + const descriptorSet = pass.descriptorSet; + + cmdBuff.bindPipelineState(pso); + cmdBuff.bindDescriptorSet(SetIndex.MATERIAL, descriptorSet); + cmdBuff.bindDescriptorSet(SetIndex.LOCAL, subModel.descriptorSet); + cmdBuff.bindInputAssembler(ia); + cmdBuff.draw(ia); + } + + this.resetProbeMacro(); this._instancedQueue.clear(); } - public resetRGBEMacro (): void { + + public resetProbeMacro (): void { for (let i = 0; i < this._rgbeSubModelsArray.length; i++) { this._patches = []; const subModel = this._rgbeSubModelsArray[i]; - // eslint-disable-next-line prefer-const this._patches = this._patches.concat(subModel.patches!); if (!this._patches) continue; for (let j = 0; j < this._patches.length; j++) { const patch = this._patches[j]; - if (patch.name === CC_USE_RGBE_OUTPUT) { + if (patch.name === CC_USE_RGBE_OUTPUT || patch.name === CC_USE_FLOAT_PROBE_OUTPUT) { this._patches.splice(j, 1); break; } diff --git a/editor/assets/chunks/legacy/output-standard.chunk b/editor/assets/chunks/legacy/output-standard.chunk index 52cb818e9a..c07db1e601 100644 --- a/editor/assets/chunks/legacy/output-standard.chunk +++ b/editor/assets/chunks/legacy/output-standard.chunk @@ -4,7 +4,9 @@ #include vec4 CCFragOutput (vec4 color) { - #if CC_USE_RGBE_OUTPUT + #if CC_USE_FLOAT_PROBE_OUTPUT + // Float probe RT: output linear HDR with alpha for transparency blending + #elif CC_USE_RGBE_OUTPUT color = packRGBE(color.rgb); #elif !CC_USE_FLOAT_OUTPUT #if CC_USE_HDR && CC_TONE_MAPPING_TYPE == TONE_MAPPING_ACES diff --git a/editor/assets/chunks/shading-entries/main-functions/misc/silhouette-edge-fs.chunk b/editor/assets/chunks/shading-entries/main-functions/misc/silhouette-edge-fs.chunk index 75f283169a..fe055c686e 100644 --- a/editor/assets/chunks/shading-entries/main-functions/misc/silhouette-edge-fs.chunk +++ b/editor/assets/chunks/shading-entries/main-functions/misc/silhouette-edge-fs.chunk @@ -7,7 +7,9 @@ void main () { //#uniformStyle need sync here // Color output - #if CC_USE_RGBE_OUTPUT + #if CC_USE_FLOAT_PROBE_OUTPUT + // output linear HDR for float probe RT + #elif CC_USE_RGBE_OUTPUT color = packRGBE(color.rgb); #elif !CC_USE_FLOAT_OUTPUT color.rgb = LinearToSRGB(color.rgb); diff --git a/editor/assets/chunks/shading-entries/main-functions/misc/sky-fs.chunk b/editor/assets/chunks/shading-entries/main-functions/misc/sky-fs.chunk index 4b19e669c8..ecd745728d 100644 --- a/editor/assets/chunks/shading-entries/main-functions/misc/sky-fs.chunk +++ b/editor/assets/chunks/shading-entries/main-functions/misc/sky-fs.chunk @@ -23,7 +23,9 @@ void main() { CC_APPLY_FOG_BASE(color, fogFactor); #endif - #if CC_USE_RGBE_OUTPUT + #if CC_USE_FLOAT_PROBE_OUTPUT + // output linear HDR for float probe RT + #elif CC_USE_RGBE_OUTPUT color = packRGBE(color.rgb); #else//todo: change to #elif !CC_USE_FLOAT_OUTPUT when sky render queue has been fixed with custom pipeline color.rgb = HDRToLDR(color.rgb); diff --git a/editor/assets/chunks/shading-entries/main-functions/render-to-reflectmap/fs.chunk b/editor/assets/chunks/shading-entries/main-functions/render-to-reflectmap/fs.chunk index 4e31ab6d6d..d111b70ec2 100644 --- a/editor/assets/chunks/shading-entries/main-functions/render-to-reflectmap/fs.chunk +++ b/editor/assets/chunks/shading-entries/main-functions/render-to-reflectmap/fs.chunk @@ -48,7 +48,11 @@ layout(location = 0) out vec4 fragColorX; CC_APPLY_FOG_BASE(color, fogFactor); #endif - // Color output (RGBE) - fragColorX = packRGBE(color.rgb); + // Color output + #if CC_USE_FLOAT_PROBE_OUTPUT + fragColorX = color; + #else + fragColorX = packRGBE(color.rgb); + #endif } #endif diff --git a/editor/assets/chunks/shading-entries/main-functions/render-to-scene/pipeline/forward-fs.chunk b/editor/assets/chunks/shading-entries/main-functions/render-to-scene/pipeline/forward-fs.chunk index 386daaf090..9c04a93079 100644 --- a/editor/assets/chunks/shading-entries/main-functions/render-to-scene/pipeline/forward-fs.chunk +++ b/editor/assets/chunks/shading-entries/main-functions/render-to-scene/pipeline/forward-fs.chunk @@ -141,7 +141,9 @@ void main() { #endif // Color output - #if CC_USE_RGBE_OUTPUT + #if CC_USE_FLOAT_PROBE_OUTPUT + // Float probe RT: output linear HDR with alpha for transparency blending + #elif CC_USE_RGBE_OUTPUT color = packRGBE(color.rgb); // for reflection-map #else color = CCSurfacesDebugDisplayInvalidNumber(color); diff --git a/editor/assets/default_renderpipeline/builtin-pipeline.ts b/editor/assets/default_renderpipeline/builtin-pipeline.ts index 5fc0a346e2..671e1d54a3 100644 --- a/editor/assets/default_renderpipeline/builtin-pipeline.ts +++ b/editor/assets/default_renderpipeline/builtin-pipeline.ts @@ -651,37 +651,59 @@ export class BuiltinForwardPassBuilder implements rendering.PipelinePassBuilder if (!cameraConfigs.enablePlanarReflectionProbe) { continue; } + const useFloatRT = probe.useFloatIntermediateRT(); 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); + let sceneColorName = colorName; + if (useFloatRT) { + sceneColorName = `PlanarProbeFloatRT${probeID}`; + ppl.addRenderTarget(sceneColorName, gfx.Format.RGBA16F, width, height); + } + // Rendering const probePass = ppl.addRenderPass(width, height, 'default'); probePass.name = `PlanarReflectionProbe${probeID}`; this._buildReflectionProbePass(probePass, cameraConfigs, id, probe.camera, - colorName, depthStencilName, mainLight, scene); + sceneColorName, depthStencilName, mainLight, scene, useFloatRT); + + if (useFloatRT) { + this._buildProbeConvertPass(ppl, width, height, + sceneColorName, colorName, `PlanarProbeConvert${probeID}`); + } } else if (EDITOR) { + const useFloatRT = probe.useFloatIntermediateRT(); 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); + let sceneColorName = colorName; + if (useFloatRT) { + sceneColorName = `CubeProbeFloatRT${probeID}${faceIdx}`; + ppl.addRenderTarget(sceneColorName, gfx.Format.RGBA16F, width, height); + } + // Rendering const probePass = ppl.addRenderPass(width, height, 'default'); probePass.name = `CubeProbe${probeID}${faceIdx}`; this._buildReflectionProbePass(probePass, cameraConfigs, id, probe.camera, - colorName, depthStencilName, mainLight, scene); + sceneColorName, depthStencilName, mainLight, scene, useFloatRT); + + if (useFloatRT) { + this._buildProbeConvertPass(ppl, width, height, + sceneColorName, colorName, `CubeProbeConvert${probeID}${faceIdx}`); + } } probe.needRender = false; } @@ -691,6 +713,31 @@ export class BuiltinForwardPassBuilder implements rendering.PipelinePassBuilder } } } + + private _getProbeConvertMaterial (): Material { + if (!this._probeRGBEConvertMaterial) { + this._probeRGBEConvertMaterial = new Material(); + this._probeRGBEConvertMaterial._uuid = `builtin-pipeline-probe-rgbe-convert-material`; + this._probeRGBEConvertMaterial.initialize({ effectName: 'pipeline/probe-rgbe-convert' }); + } + return this._probeRGBEConvertMaterial; + } + + private _buildProbeConvertPass ( + ppl: rendering.BasicPipeline, + width: number, + height: number, + floatColorName: string, + outputColorName: string, + passName: string, + ): void { + const convertPass = ppl.addRenderPass(width, height, 'probe-rgbe-convert'); + convertPass.name = passName; + convertPass.addRenderTarget(outputColorName, LoadOp.CLEAR, StoreOp.STORE, sClearColorTransparentBlack); + convertPass.addTexture(floatColorName, 'probeColorTex'); + convertPass.addQueue(rendering.QueueHint.OPAQUE) + .addFullscreenQuad(this._getProbeConvertMaterial(), 0); + } private _buildReflectionProbePass( pass: rendering.BasicRenderPassBuilder, cameraConfigs: Readonly, @@ -700,6 +747,7 @@ export class BuiltinForwardPassBuilder implements rendering.PipelinePassBuilder depthStencilName: string, mainLight: renderer.scene.DirectionalLight | null, scene: renderer.RenderScene | null = null, + useFloatRT = false, ): void { const QueueHint = rendering.QueueHint; const SceneFlags = rendering.SceneFlags; @@ -708,14 +756,21 @@ export class BuiltinForwardPassBuilder implements rendering.PipelinePassBuilder // 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; + if (useFloatRT) { + this._clearColor.x = camera.clearColor.x; + this._clearColor.y = camera.clearColor.y; + this._clearColor.z = camera.clearColor.z; + this._clearColor.w = camera.clearColor.w; + } else { + 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); @@ -748,6 +803,14 @@ export class BuiltinForwardPassBuilder implements rendering.PipelinePassBuilder SceneFlags.OPAQUE | SceneFlags.MASK | SceneFlags.REFLECTION_PROBE, mainLight || undefined, scene ? scene : undefined); + + if (useFloatRT) { + pass.addQueue(QueueHint.BLEND, 'reflect-map') + .addScene(camera, + SceneFlags.BLEND | SceneFlags.REFLECTION_PROBE, + mainLight || undefined, + scene ? scene : undefined); + } } private _addForwardRadiancePasses( ppl: rendering.BasicPipeline, @@ -982,6 +1045,7 @@ export class BuiltinForwardPassBuilder implements rendering.PipelinePassBuilder private readonly _viewport = new Viewport(); private readonly _clearColor = new Color(0, 0, 0, 1); private readonly _reflectionProbeClearColor = new Vec3(0, 0, 0); + private _probeRGBEConvertMaterial: Material | null = null; } export interface BloomPassConfigs { diff --git a/editor/assets/effects/pipeline/probe-rgbe-convert.effect b/editor/assets/effects/pipeline/probe-rgbe-convert.effect new file mode 100644 index 0000000000..21fc8989f9 --- /dev/null +++ b/editor/assets/effects/pipeline/probe-rgbe-convert.effect @@ -0,0 +1,45 @@ +// Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd. + +CCEffect %{ +techniques: + - passes: + - vert: probe-rgbe-convert-vs + frag: probe-rgbe-convert-fs + pass: probe-rgbe-convert + depthStencilState: + depthTest: false + depthWrite: false + rasterizerState: + cullMode: none + +}% + +CCProgram probe-rgbe-convert-vs %{ +precision highp float; + +#include +#include +out vec2 v_uv; + +void main() { + StandardVertInput In; + CCDecode(In); + gl_Position = In.position; + v_uv = a_texCoord; +} +}% + +CCProgram probe-rgbe-convert-fs %{ +precision highp float; +in vec2 v_uv; +#include + +uniform sampler2D probeColorTex; + +layout(location = 0) out vec4 fragColor; + +void main() { + vec4 color = texture(probeColorTex, v_uv); + fragColor = packRGBE(color.rgb); +} +}% diff --git a/editor/assets/effects/pipeline/probe-rgbe-convert.effect.meta b/editor/assets/effects/pipeline/probe-rgbe-convert.effect.meta new file mode 100644 index 0000000000..6bf6aae377 --- /dev/null +++ b/editor/assets/effects/pipeline/probe-rgbe-convert.effect.meta @@ -0,0 +1,11 @@ +{ + "ver": "1.7.1", + "importer": "effect", + "imported": true, + "uuid": "a3f78b2e-6c91-4d5a-b7f3-8e2d4a1c0b96", + "files": [ + ".json" + ], + "subMetas": {}, + "userData": {} +} diff --git a/editor/assets/effects/pipeline/skybox.effect b/editor/assets/effects/pipeline/skybox.effect index d9b90812ab..2a97400695 100644 --- a/editor/assets/effects/pipeline/skybox.effect +++ b/editor/assets/effects/pipeline/skybox.effect @@ -78,7 +78,9 @@ CCProgram sky-fs %{ #endif vec4 color = vec4(c * cc_ambientSky.w, 1.0); - #if CC_USE_RGBE_OUTPUT + #if CC_USE_FLOAT_PROBE_OUTPUT + // output linear HDR for float probe RT + #elif CC_USE_RGBE_OUTPUT color = packRGBE(color.rgb); #else color.rgb = HDRToLDR(color.rgb); From 9f95dde4f1ea8f4f2add0d0b648f69da07b12d7d Mon Sep 17 00:00:00 2001 From: xubing <58895777@qq.com> Date: Wed, 5 Aug 2026 11:23:26 +0800 Subject: [PATCH 02/11] fix reflection probe RGBE convert pass for custom pipeline --- cocos/rendering/custom/scene-culling.ts | 13 +++++-- cocos/rendering/custom/web-pipeline-types.ts | 2 +- .../reflection-probe-stage.ts | 4 +-- .../builtin-pipeline.ts | 24 ++++++------- .../pipeline/probe-rgbe-convert.effect | 34 ++++++++++++++++--- 5 files changed, 54 insertions(+), 23 deletions(-) diff --git a/cocos/rendering/custom/scene-culling.ts b/cocos/rendering/custom/scene-culling.ts index 1ccb1d4eb8..26c2205432 100644 --- a/cocos/rendering/custom/scene-culling.ts +++ b/cocos/rendering/custom/scene-culling.ts @@ -477,8 +477,17 @@ export class SceneCulling { // Get or create render queue const renderQueueID = this.getOrCreateRenderQueue(renderQueueKey, sceneData.flags, sceneData.camera); - if (sceneData.light.probe && sceneData.light.probe.useFloatIntermediateRT()) { - this.renderQueues[renderQueueID].probeQueue.useFloatOutput = true; + if (sceneData.flags & SceneFlags.REFLECTION_PROBE) { + const probeManager = cclegacy.internal.reflectionProbeManager; + if (probeManager) { + const probes = probeManager.getProbes() as ReflectionProbe[]; + for (let i = 0; i < probes.length; i++) { + if (probes[i].camera === sceneData.camera && probes[i].useFloatIntermediateRT()) { + this.renderQueues[renderQueueID].probeQueue.useFloatOutput = true; + break; + } + } + } } // add render queue query diff --git a/cocos/rendering/custom/web-pipeline-types.ts b/cocos/rendering/custom/web-pipeline-types.ts index 13519e943a..4e39a66386 100644 --- a/cocos/rendering/custom/web-pipeline-types.ts +++ b/cocos/rendering/custom/web-pipeline-types.ts @@ -274,7 +274,7 @@ export class ProbeHelperQueue { bUseReflectPass = false; } if (passIdx < 0) { continue; } - if (!bUseReflectPass) { + if (!bUseReflectPass || this.useFloatOutput) { this.probeMap.push(subModel); } } diff --git a/cocos/rendering/reflection-probe/reflection-probe-stage.ts b/cocos/rendering/reflection-probe/reflection-probe-stage.ts index 3fea9a180c..c6b20aed73 100644 --- a/cocos/rendering/reflection-probe/reflection-probe-stage.ts +++ b/cocos/rendering/reflection-probe/reflection-probe-stage.ts @@ -174,7 +174,7 @@ export class ReflectionProbeStage extends RenderStage { if (!inputAssembler) return; const intermediateColorTex = this._frameBuffer!.colorTextures[0]!; - const binding = pass.getBinding('probeColorTex'); + const binding = pass.getBinding('outputResultMap'); if (binding < 0) return; const w = this._renderArea.width; @@ -224,7 +224,7 @@ export class ReflectionProbeStage extends RenderStage { if (!this._convertMaterial) { this._convertMaterial = new Material(); this._convertMaterial._uuid = 'reflection-probe-rgbe-convert-material'; - this._convertMaterial.initialize({ effectName: 'pipeline/probe-rgbe-convert' }); + this._convertMaterial.initialize({ effectName: 'pipeline/probe-rgbe-convert', technique: 1 }); } return this._convertMaterial; } diff --git a/editor/assets/default_renderpipeline/builtin-pipeline.ts b/editor/assets/default_renderpipeline/builtin-pipeline.ts index 671e1d54a3..d3cfd5e417 100644 --- a/editor/assets/default_renderpipeline/builtin-pipeline.ts +++ b/editor/assets/default_renderpipeline/builtin-pipeline.ts @@ -714,16 +714,7 @@ export class BuiltinForwardPassBuilder implements rendering.PipelinePassBuilder } } - private _getProbeConvertMaterial (): Material { - if (!this._probeRGBEConvertMaterial) { - this._probeRGBEConvertMaterial = new Material(); - this._probeRGBEConvertMaterial._uuid = `builtin-pipeline-probe-rgbe-convert-material`; - this._probeRGBEConvertMaterial.initialize({ effectName: 'pipeline/probe-rgbe-convert' }); - } - return this._probeRGBEConvertMaterial; - } - - private _buildProbeConvertPass ( + private _buildProbeConvertPass( ppl: rendering.BasicPipeline, width: number, height: number, @@ -731,12 +722,13 @@ export class BuiltinForwardPassBuilder implements rendering.PipelinePassBuilder outputColorName: string, passName: string, ): void { - const convertPass = ppl.addRenderPass(width, height, 'probe-rgbe-convert'); + if (!this._probeRGBEConvertMaterial) { return; } + const convertPass = ppl.addRenderPass(width, height, 'copy-pass'); convertPass.name = passName; convertPass.addRenderTarget(outputColorName, LoadOp.CLEAR, StoreOp.STORE, sClearColorTransparentBlack); - convertPass.addTexture(floatColorName, 'probeColorTex'); + convertPass.addTexture(floatColorName, 'outputResultMap'); convertPass.addQueue(rendering.QueueHint.OPAQUE) - .addFullscreenQuad(this._getProbeConvertMaterial(), 0); + .addFullscreenQuad(this._probeRGBEConvertMaterial, 0); } private _buildReflectionProbePass( pass: rendering.BasicRenderPassBuilder, @@ -1046,6 +1038,7 @@ export class BuiltinForwardPassBuilder implements rendering.PipelinePassBuilder private readonly _clearColor = new Color(0, 0, 0, 1); private readonly _reflectionProbeClearColor = new Vec3(0, 0, 0); private _probeRGBEConvertMaterial: Material | null = null; + public set probeConvertMaterial(mat: Material) { this._probeRGBEConvertMaterial = mat; } } export interface BloomPassConfigs { @@ -1791,6 +1784,7 @@ if (rendering) { private readonly _cameraConfigs = new CameraConfigs(); // Materials private readonly _copyAndTonemapMaterial = new Material(); + private readonly _probeRGBEConvertMaterial = new Material(); // Internal States private _initialized = false; // TODO(zhouzhenglong): Make default effect asset loading earlier and remove this flag @@ -2098,7 +2092,11 @@ if (rendering) { this._copyAndTonemapMaterial._uuid = `builtin-pipeline-tone-mapping-material`; this._copyAndTonemapMaterial.initialize({ effectName: 'pipeline/post-process/tone-mapping' }); + this._probeRGBEConvertMaterial._uuid = `builtin-pipeline-probe-rgbe-convert-material`; + this._probeRGBEConvertMaterial.initialize({ effectName: 'pipeline/probe-rgbe-convert' }); + if (this._copyAndTonemapMaterial.effectAsset) { + this._forwardPass.probeConvertMaterial = this._probeRGBEConvertMaterial; this._initialized = true; } diff --git a/editor/assets/effects/pipeline/probe-rgbe-convert.effect b/editor/assets/effects/pipeline/probe-rgbe-convert.effect index 21fc8989f9..c239c38dea 100644 --- a/editor/assets/effects/pipeline/probe-rgbe-convert.effect +++ b/editor/assets/effects/pipeline/probe-rgbe-convert.effect @@ -4,8 +4,16 @@ CCEffect %{ techniques: - passes: - vert: probe-rgbe-convert-vs - frag: probe-rgbe-convert-fs - pass: probe-rgbe-convert + frag: probe-rgbe-convert-fs-custom + pass: copy-pass + depthStencilState: + depthTest: false + depthWrite: false + rasterizerState: + cullMode: none + - passes: + - vert: probe-rgbe-convert-vs + frag: probe-rgbe-convert-fs-legacy depthStencilState: depthTest: false depthWrite: false @@ -29,17 +37,33 @@ void main() { } }% -CCProgram probe-rgbe-convert-fs %{ +CCProgram probe-rgbe-convert-fs-custom %{ +precision highp float; +in vec2 v_uv; +#include + +#pragma rate outputResultMap pass +uniform sampler2D outputResultMap; + +layout(location = 0) out vec4 fragColor; + +void main() { + vec4 color = texture(outputResultMap, v_uv); + fragColor = packRGBE(color.rgb); +} +}% + +CCProgram probe-rgbe-convert-fs-legacy %{ precision highp float; in vec2 v_uv; #include -uniform sampler2D probeColorTex; +uniform sampler2D outputResultMap; layout(location = 0) out vec4 fragColor; void main() { - vec4 color = texture(probeColorTex, v_uv); + vec4 color = texture(outputResultMap, v_uv); fragColor = packRGBE(color.rgb); } }% From 0537d79e236b89dc39142129d11a0f4fa7bac4c2 Mon Sep 17 00:00:00 2001 From: xubing <58895777@qq.com> Date: Wed, 5 Aug 2026 17:46:50 +0800 Subject: [PATCH 03/11] fix: legacy pipeline reflection probe RGBE convert pass binding failure --- .../reflection-probe/reflection-probe-stage.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cocos/rendering/reflection-probe/reflection-probe-stage.ts b/cocos/rendering/reflection-probe/reflection-probe-stage.ts index c6b20aed73..7c203954a0 100644 --- a/cocos/rendering/reflection-probe/reflection-probe-stage.ts +++ b/cocos/rendering/reflection-probe/reflection-probe-stage.ts @@ -163,19 +163,19 @@ export class ReflectionProbeStage extends RenderStage { private _renderConvertPass (device: Device, cmdBuff: CommandBuffer): void { const mat = this._getConvertMaterial(); - if (!mat || !mat.passes.length) return; + if (!mat || !mat.passes.length) { return; } const pass = mat.passes[0]; const shader = pass.getShaderVariant(); - if (!pass || !shader) return; + if (!shader) { return; } const fwdPipeline = this._pipeline as ForwardPipeline; const inputAssembler = fwdPipeline.quadIAOffscreen; - if (!inputAssembler) return; + if (!inputAssembler) { return; } const intermediateColorTex = this._frameBuffer!.colorTextures[0]!; - const binding = pass.getBinding('outputResultMap'); - if (binding < 0) return; + const binding = pass.getBinding('probeInputTex'); + if (binding < 0) { return; } const w = this._renderArea.width; const h = this._renderArea.height; From fc7e343196f726cfa7813dce15a7f3529e5908a4 Mon Sep 17 00:00:00 2001 From: xubing <58895777@qq.com> Date: Thu, 6 Aug 2026 11:19:34 +0800 Subject: [PATCH 04/11] fix: add missing pass and sampler rename for legacy probe convert effect The effect file changes from the previous fix were not committed: add pass: copy-pass to technique 1 and rename sampler to probeInputTex. --- editor/assets/effects/pipeline/probe-rgbe-convert.effect | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/editor/assets/effects/pipeline/probe-rgbe-convert.effect b/editor/assets/effects/pipeline/probe-rgbe-convert.effect index c239c38dea..4ead564e8c 100644 --- a/editor/assets/effects/pipeline/probe-rgbe-convert.effect +++ b/editor/assets/effects/pipeline/probe-rgbe-convert.effect @@ -14,6 +14,7 @@ techniques: - passes: - vert: probe-rgbe-convert-vs frag: probe-rgbe-convert-fs-legacy + pass: copy-pass depthStencilState: depthTest: false depthWrite: false @@ -58,12 +59,12 @@ precision highp float; in vec2 v_uv; #include -uniform sampler2D outputResultMap; +uniform sampler2D probeInputTex; layout(location = 0) out vec4 fragColor; void main() { - vec4 color = texture(outputResultMap, v_uv); + vec4 color = texture(probeInputTex, v_uv); fragColor = packRGBE(color.rgb); } }% From 005773c7abceb585d5506bcfe79af37a3b1d8003 Mon Sep 17 00:00:00 2001 From: xubing <58895777@qq.com> Date: Thu, 6 Aug 2026 11:19:42 +0800 Subject: [PATCH 05/11] fix: respect bakeToReflectionProbe flag in custom pipeline Models with bakeToReflectionProbe disabled were still rendered into reflection probes in the custom/builtin pipeline. --- cocos/rendering/custom/scene-culling.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cocos/rendering/custom/scene-culling.ts b/cocos/rendering/custom/scene-culling.ts index 26c2205432..d76f8a9392 100644 --- a/cocos/rendering/custom/scene-culling.ts +++ b/cocos/rendering/custom/scene-culling.ts @@ -229,6 +229,9 @@ function addRenderObject ( ): void { const probeQueue = queue.probeQueue; if (isDrawProbe) { + if (!model.bakeToReflectionProbe) { + return; + } probeQueue.addToProbeQueue(model, phaseLayoutId); } const subModels = model.subModels; From 9afd72bd197deda5ca9f2473b13f6372343e06d8 Mon Sep 17 00:00:00 2001 From: xubing <58895777@qq.com> Date: Thu, 6 Aug 2026 15:49:16 +0800 Subject: [PATCH 06/11] refactor: remove unnecessary guard for quad IA creation in ForwardPipeline --- cocos/rendering/forward/forward-pipeline.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cocos/rendering/forward/forward-pipeline.ts b/cocos/rendering/forward/forward-pipeline.ts index d7716666c9..2a3660507d 100644 --- a/cocos/rendering/forward/forward-pipeline.ts +++ b/cocos/rendering/forward/forward-pipeline.ts @@ -145,10 +145,8 @@ export class ForwardPipeline extends RenderPipeline { descriptorSet.update(); + // Fullscreen quad for reflection probe RGBE convert pass const inputAssemblerData = this._createQuadInputAssembler(); - if (!inputAssemblerData.quadIB || !inputAssemblerData.quadVB || !inputAssemblerData.quadIA) { - return false; - } this._quadVBOffscreen = inputAssemblerData.quadVB; this._quadIB = inputAssemblerData.quadIB; this._quadIAOffscreen = inputAssemblerData.quadIA; From 8ad0ca8ae56a1f00a395788e6d563fcd97d0ef8a Mon Sep 17 00:00:00 2001 From: xubing <58895777@qq.com> Date: Thu, 6 Aug 2026 18:29:04 +0800 Subject: [PATCH 07/11] fix: show supportTransparency for planar probe and skip instancing for transparent objects --- cocos/3d/reflection-probe/reflection-probe-component.ts | 2 +- cocos/rendering/reflection-probe/reflection-probe-flow.ts | 2 ++ cocos/rendering/render-reflection-probe-queue.ts | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cocos/3d/reflection-probe/reflection-probe-component.ts b/cocos/3d/reflection-probe/reflection-probe-component.ts index e1af1a53d2..f2affdd340 100644 --- a/cocos/3d/reflection-probe/reflection-probe-component.ts +++ b/cocos/3d/reflection-probe/reflection-probe-component.ts @@ -275,7 +275,7 @@ export class ReflectionProbe extends Component { * @zh 是否支持半透明物体参与反射探针渲染。 * 开启后使用 RGBA16F 中间 RT 以支持正确的 alpha 混合。 */ - @visible(function (this: ReflectionProbe) { return this.probeType === ProbeType.CUBE; }) + @visible(function (this: ReflectionProbe) { return this.probeType === ProbeType.CUBE || this.probeType === ProbeType.PLANAR; }) @type(CCBoolean) @tooltip('i18n:reflection_probe.supportTransparency') get supportTransparency (): boolean { diff --git a/cocos/rendering/reflection-probe/reflection-probe-flow.ts b/cocos/rendering/reflection-probe/reflection-probe-flow.ts index a663bb97a9..d907bab296 100644 --- a/cocos/rendering/reflection-probe/reflection-probe-flow.ts +++ b/cocos/rendering/reflection-probe/reflection-probe-flow.ts @@ -80,11 +80,13 @@ export class ReflectionProbeFlow extends RenderFlow { for (let i = 0; i < this._stages.length; i++) { const probeStage = this._stages[i] as ReflectionProbeStage; if (probe.probeType === ProbeType.PLANAR) { + // Unbind planar map before rendering to prevent self-reflection cclegacy.internal.reflectionProbeManager.updatePlanarMap(probe, null); const outputFb = probe.realtimePlanarTexture!.window!.framebuffer; const fb = useFloatRT ? probe.intermediateFramebuffers[0] : outputFb; probeStage.setUsageInfo(probe, fb, useFloatRT ? outputFb : null); probeStage.render(camera); + // Rebind the updated planar map for main camera rendering cclegacy.internal.reflectionProbeManager.updatePlanarMap(probe, probe.realtimePlanarTexture!.getGFXTexture()); } else { for (let faceIdx = 0; faceIdx < 6; faceIdx++) { diff --git a/cocos/rendering/render-reflection-probe-queue.ts b/cocos/rendering/render-reflection-probe-queue.ts index e4859c79fc..4f22da9af7 100644 --- a/cocos/rendering/render-reflection-probe-queue.ts +++ b/cocos/rendering/render-reflection-probe-queue.ts @@ -160,7 +160,7 @@ export class RenderReflectionProbeQueue { this._rgbeSubModelsArray.push(subModel); } - if (batchingScheme === BatchingSchemes.INSTANCING) { + if (batchingScheme === BatchingSchemes.INSTANCING && !isTransparent) { const buffer = pass.getInstancedBuffer(); buffer.merge(subModel, passIdx); this._instancedQueue.queue.add(buffer); From 2c0579bf2baec436181fb43a349826bb75681e1f Mon Sep 17 00:00:00 2001 From: xubing <58895777@qq.com> Date: Fri, 7 Aug 2026 16:03:35 +0800 Subject: [PATCH 08/11] fix: enable supportTransparency by default for cube probe and disable for planar --- cocos/3d/reflection-probe/reflection-probe-component.ts | 4 ++-- cocos/render-scene/scene/reflection-probe.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cocos/3d/reflection-probe/reflection-probe-component.ts b/cocos/3d/reflection-probe/reflection-probe-component.ts index f2affdd340..21dc2ec5cf 100644 --- a/cocos/3d/reflection-probe/reflection-probe-component.ts +++ b/cocos/3d/reflection-probe/reflection-probe-component.ts @@ -99,7 +99,7 @@ export class ReflectionProbe extends Component { private _fastBake = false; @serializable - private _supportTransparency = false; + private _supportTransparency = true; protected _probe: scene.ReflectionProbe | null = null; @@ -275,7 +275,7 @@ export class ReflectionProbe extends Component { * @zh 是否支持半透明物体参与反射探针渲染。 * 开启后使用 RGBA16F 中间 RT 以支持正确的 alpha 混合。 */ - @visible(function (this: ReflectionProbe) { return this.probeType === ProbeType.CUBE || this.probeType === ProbeType.PLANAR; }) + @visible(function (this: ReflectionProbe) { return this.probeType === ProbeType.CUBE; }) @type(CCBoolean) @tooltip('i18n:reflection_probe.supportTransparency') get supportTransparency (): boolean { diff --git a/cocos/render-scene/scene/reflection-probe.ts b/cocos/render-scene/scene/reflection-probe.ts index afd79705c7..9bff3fc485 100644 --- a/cocos/render-scene/scene/reflection-probe.ts +++ b/cocos/render-scene/scene/reflection-probe.ts @@ -304,7 +304,7 @@ export class ReflectionProbe { * 调用方在实际使用浮点路径前,还需自行检查设备是否支持 RGBA16F。 */ public useFloatIntermediateRT (): boolean { - return this._supportTransparency; + return this._supportTransparency && this._probeType !== ProbeType.PLANAR; } constructor (id: number) { From 2fed6c1fb1a867a0dd033eecb18a26ddade0b3da Mon Sep 17 00:00:00 2001 From: xubing <58895777@qq.com> Date: Fri, 7 Aug 2026 16:10:29 +0800 Subject: [PATCH 09/11] revert: remove jsb reflection probe changes until native backend supports baking --- .../scene/reflection-probe.jsb.ts | 86 +------------------ 1 file changed, 2 insertions(+), 84 deletions(-) diff --git a/cocos/render-scene/scene/reflection-probe.jsb.ts b/cocos/render-scene/scene/reflection-probe.jsb.ts index a2eb34c4a8..27824d8cad 100644 --- a/cocos/render-scene/scene/reflection-probe.jsb.ts +++ b/cocos/render-scene/scene/reflection-probe.jsb.ts @@ -21,16 +21,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { ClearFlagBit, Texture, Framebuffer, RenderPass, - ColorAttachment, DepthStencilAttachment, RenderPassInfo, - TextureInfo, TextureType, TextureUsageBit, Format, LoadOp, StoreOp, - FramebufferInfo } from '../../gfx'; +import { ClearFlagBit } from '../../gfx'; import { SKYBOX_FLAG } from './camera'; import type { ReflectionProbe as JsbReflectionProbe } from './reflection-probe'; -import { cclegacy } from '../../core'; declare const jsb: any; - + export enum ProbeClearFlag { SKYBOX = SKYBOX_FLAG | ClearFlagBit.DEPTH_STENCIL, SOLID_COLOR = ClearFlagBit.ALL, @@ -46,82 +42,4 @@ export type ReflectionProbe = JsbReflectionProbe; const reflectionProbeProto: any = jsb.ReflectionProbe.prototype; reflectionProbeProto._ctor = function (id:number) { this._probeId = id; - this._supportTransparency = false; - this.intermediateTextures = []; - this.intermediateFramebuffers = []; - this._intermediateRenderPass = null; - this._intermediateDepthStencilTex = null; -}; - -Object.defineProperty(reflectionProbeProto, 'supportTransparency', { - get (this: any): boolean { - return this._supportTransparency; - }, - set (this: any, value: boolean) { - this._supportTransparency = value; - }, -}); - -reflectionProbeProto.useFloatIntermediateRT = function (this: any): boolean { - return this._supportTransparency; -}; - -reflectionProbeProto.initIntermediateTextures = function (this: any, width: number, height: number, count: number): void { - reflectionProbeProto._destroyIntermediateTextures.call(this); - - const root = cclegacy.director.root; - const device = root.device; - - const colorAttachment = new ColorAttachment(); - colorAttachment.format = Format.RGBA16F; - colorAttachment.loadOp = LoadOp.CLEAR; - colorAttachment.storeOp = StoreOp.STORE; - - const depthStencilAttachment = new DepthStencilAttachment(); - depthStencilAttachment.format = Format.DEPTH_STENCIL; - depthStencilAttachment.depthLoadOp = LoadOp.CLEAR; - depthStencilAttachment.depthStoreOp = StoreOp.STORE; - depthStencilAttachment.stencilLoadOp = LoadOp.CLEAR; - depthStencilAttachment.stencilStoreOp = StoreOp.STORE; - - const renderPassInfo = new RenderPassInfo([colorAttachment], depthStencilAttachment); - this._intermediateRenderPass = device.createRenderPass(renderPassInfo) as RenderPass; - - this._intermediateDepthStencilTex = device.createTexture(new TextureInfo( - TextureType.TEX2D, TextureUsageBit.DEPTH_STENCIL_ATTACHMENT, Format.DEPTH_STENCIL, width, height, - )) as Texture; - - for (let i = 0; i < count; i++) { - const colorTex: Texture = device.createTexture(new TextureInfo( - TextureType.TEX2D, TextureUsageBit.COLOR_ATTACHMENT | TextureUsageBit.SAMPLED, Format.RGBA16F, width, height, - )) as Texture; - this.intermediateTextures.push(colorTex); - - const fb: Framebuffer = device.createFramebuffer(new FramebufferInfo( - this._intermediateRenderPass, [colorTex], this._intermediateDepthStencilTex, - )); - this.intermediateFramebuffers.push(fb); - } -}; - -reflectionProbeProto._destroyIntermediateTextures = function (this: any): void { - for (let i = 0; i < this.intermediateFramebuffers.length; i++) { - this.intermediateFramebuffers[i].destroy(); - } - this.intermediateFramebuffers.length = 0; - - for (let i = 0; i < this.intermediateTextures.length; i++) { - this.intermediateTextures[i].destroy(); - } - this.intermediateTextures.length = 0; - - if (this._intermediateDepthStencilTex) { - this._intermediateDepthStencilTex.destroy(); - this._intermediateDepthStencilTex = null; - } - - if (this._intermediateRenderPass) { - this._intermediateRenderPass.destroy(); - this._intermediateRenderPass = null; - } }; From 9f9a90720085960b76242ea6ab540c07cb20c2fd Mon Sep 17 00:00:00 2001 From: xubing <58895777@qq.com> Date: Fri, 7 Aug 2026 17:43:23 +0800 Subject: [PATCH 10/11] fix: add RGBA16F device capability check in useFloatIntermediateRT --- cocos/render-scene/scene/reflection-probe.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/cocos/render-scene/scene/reflection-probe.ts b/cocos/render-scene/scene/reflection-probe.ts index 9bff3fc485..4081a372f1 100644 --- a/cocos/render-scene/scene/reflection-probe.ts +++ b/cocos/render-scene/scene/reflection-probe.ts @@ -32,7 +32,7 @@ import { Format, LoadOp, StoreOp, RenderPassInfo, RenderPass, Texture, TextureInfo, TextureType, TextureUsageBit, - FramebufferInfo, + FramebufferInfo, FormatFeatureBit, } from '../../gfx'; import { TextureCube } from '../../asset/assets/texture-cube'; import { RenderTexture } from '../../asset/assets/render-texture'; @@ -298,13 +298,16 @@ export class ReflectionProbe { /** * @en Whether to use RGBA16F intermediate render target for transparency support. - * Callers should also check device capability (supportsRGBA16HalfFloatTexture) before using the float path. * @zh 是否使用 RGBA16F 中间渲染目标以支持半透明渲染。 - * 当前等价于 supportTransparency,未来可能加入设备能力等额外判断条件。 - * 调用方在实际使用浮点路径前,还需自行检查设备是否支持 RGBA16F。 */ public useFloatIntermediateRT (): boolean { - return this._supportTransparency && this._probeType !== ProbeType.PLANAR; + if (!this._supportTransparency || this._probeType === ProbeType.PLANAR) { return false; } + // Check device RGBA16F capability + const device = cclegacy.director.root?.device; + if (!device) { return false; } + const features = device.getFormatFeatures(Format.RGBA16F); + return (features & (FormatFeatureBit.RENDER_TARGET | FormatFeatureBit.SAMPLED_TEXTURE)) + === (FormatFeatureBit.RENDER_TARGET | FormatFeatureBit.SAMPLED_TEXTURE); } constructor (id: number) { @@ -327,7 +330,7 @@ export class ReflectionProbe { this.bakedCubeTextures.push(renderTexture); } } - if (this._supportTransparency && this.intermediateTextures.length === 0) { + if (this.useFloatIntermediateRT() && this.intermediateTextures.length === 0) { this.initIntermediateTextures(this._resolution, this._resolution, 6); } } @@ -349,7 +352,7 @@ export class ReflectionProbe { const canvasSize = cclegacy.view.getDesignResolutionSize() as Size; this.realtimePlanarTexture = this._createTargetTexture(canvasSize.width, canvasSize.height); cclegacy.internal.reflectionProbeManager.updatePlanarMap(this, this.realtimePlanarTexture.getGFXTexture()); - if (this._supportTransparency) { + if (this.useFloatIntermediateRT()) { this.initIntermediateTextures(canvasSize.width, canvasSize.height, 1); } } From 5961073e974264e12a71eb96d8049ad9dcf5310a Mon Sep 17 00:00:00 2001 From: xubing <58895777@qq.com> Date: Fri, 7 Aug 2026 18:01:24 +0800 Subject: [PATCH 11/11] refactor: add engineInternal annotations and tighten access modifiers for probe transparency --- cocos/render-scene/scene/reflection-probe.ts | 29 +++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/cocos/render-scene/scene/reflection-probe.ts b/cocos/render-scene/scene/reflection-probe.ts index 4081a372f1..7378750c93 100644 --- a/cocos/render-scene/scene/reflection-probe.ts +++ b/cocos/render-scene/scene/reflection-probe.ts @@ -134,7 +134,12 @@ export class ReflectionProbe { private _intermediateRenderPass: RenderPass | null = null; private _intermediateDepthStencil: Texture | null = null; - public intermediateTextures: Texture[] = []; + private _intermediateTextures: Texture[] = []; + + /** + * @engineInternal + * @mangle + */ public intermediateFramebuffers: Framebuffer[] = []; /** @@ -289,9 +294,17 @@ export class ReflectionProbe { return this._previewPlane!; } + /** + * @engineInternal + * @mangle + */ set supportTransparency (value: boolean) { this._supportTransparency = value; } + /** + * @engineInternal + * @mangle + */ get supportTransparency (): boolean { return this._supportTransparency; } @@ -299,6 +312,8 @@ export class ReflectionProbe { /** * @en Whether to use RGBA16F intermediate render target for transparency support. * @zh 是否使用 RGBA16F 中间渲染目标以支持半透明渲染。 + * @engineInternal + * @mangle */ public useFloatIntermediateRT (): boolean { if (!this._supportTransparency || this._probeType === ProbeType.PLANAR) { return false; } @@ -330,7 +345,7 @@ export class ReflectionProbe { this.bakedCubeTextures.push(renderTexture); } } - if (this.useFloatIntermediateRT() && this.intermediateTextures.length === 0) { + if (this.useFloatIntermediateRT() && this._intermediateTextures.length === 0) { this.initIntermediateTextures(this._resolution, this._resolution, 6); } } @@ -519,7 +534,7 @@ export class ReflectionProbe { * @en Create RGBA16F intermediate textures and framebuffers using GFX API directly. * @zh 直接使用 GFX API 创建 RGBA16F 中间纹理和 Framebuffer。 */ - public initIntermediateTextures (width: number, height: number, count: number): void { + private initIntermediateTextures (width: number, height: number, count: number): void { this._destroyIntermediateTextures(); const root = cclegacy.director.root; @@ -556,7 +571,7 @@ export class ReflectionProbe { width, height, )) as Texture; - this.intermediateTextures.push(colorTex); + this._intermediateTextures.push(colorTex); const fb: Framebuffer = device.createFramebuffer(new FramebufferInfo( this._intermediateRenderPass, @@ -573,10 +588,10 @@ export class ReflectionProbe { } this.intermediateFramebuffers.length = 0; - for (let i = 0; i < this.intermediateTextures.length; i++) { - this.intermediateTextures[i].destroy(); + for (let i = 0; i < this._intermediateTextures.length; i++) { + this._intermediateTextures[i].destroy(); } - this.intermediateTextures.length = 0; + this._intermediateTextures.length = 0; if (this._intermediateDepthStencil) { this._intermediateDepthStencil.destroy();