Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions cc.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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"]
},
Expand Down
24 changes: 24 additions & 0 deletions cocos/3d/reflection-probe/reflection-probe-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@
@serializable
private _fastBake = false;

@serializable
private _supportTransparency = true;

protected _probe: scene.ReflectionProbe | null = null;

protected _previewSphere: Node | null = null;
Expand Down Expand Up @@ -177,7 +180,7 @@
* @en set render texture size
* @zh 设置渲染纹理大小
*/
@visible(function (this: ReflectionProbe) { return this.probeType === ProbeType.CUBE; })

Check warning on line 183 in cocos/3d/reflection-probe/reflection-probe-component.ts

View workflow job for this annotation

GitHub Actions / Run ESLint

Unexpected unnamed function
@type(Enum(ProbeResolution))
set resolution (value: number) {
this._resolution = value;
Expand All @@ -193,7 +196,7 @@
* @zh 相机的缓冲清除标志位,指定帧缓冲的哪部分要每帧清除。
*/
@type(Enum(ProbeClearFlag))
@visible(function (this: ReflectionProbe) { return this.probeType === ProbeType.CUBE; })

Check warning on line 199 in cocos/3d/reflection-probe/reflection-probe-component.ts

View workflow job for this annotation

GitHub Actions / Run ESLint

Unexpected unnamed function
set clearFlag (value: number) {
this._clearFlag = value;
this.probe.clearFlag = this._clearFlag;
Expand All @@ -206,7 +209,7 @@
* @en Clearing color of the camera.
* @zh 相机的颜色缓冲默认值。
*/
@visible(function (this: ReflectionProbe) { return this._clearFlag === ProbeClearFlag.SOLID_COLOR && this.probeType === ProbeType.CUBE; })

Check warning on line 212 in cocos/3d/reflection-probe/reflection-probe-component.ts

View workflow job for this annotation

GitHub Actions / Run ESLint

Unexpected unnamed function
@type(Color)
set backgroundColor (val: Color) {
this._backgroundColor = val;
Expand Down Expand Up @@ -234,7 +237,7 @@
* @en The camera to render planar reflections, specified by the user
* @zh 需要渲染平面反射的相机,由用户指定
*/
@visible(function (this: ReflectionProbe) { return this.probeType === ProbeType.PLANAR; })

Check warning on line 240 in cocos/3d/reflection-probe/reflection-probe-component.ts

View workflow job for this annotation

GitHub Actions / Run ESLint

Unexpected unnamed function
@type(Camera)
set sourceCamera (camera: Camera) {
this._sourceCamera = camera;
Expand All @@ -255,7 +258,7 @@
* @en fast bake no convolution.
* @zh 快速烘焙不会进行卷积。
*/
@visible(function (this: ReflectionProbe) { return this.probeType === ProbeType.CUBE; })

Check warning on line 261 in cocos/3d/reflection-probe/reflection-probe-component.ts

View workflow job for this annotation

GitHub Actions / Run ESLint

Unexpected unnamed function
@type(CCBoolean)
@tooltip('i18n:reflection_probe.fastBake')
get fastBake (): boolean {
Expand All @@ -266,6 +269,26 @@
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; })

Check warning on line 278 in cocos/3d/reflection-probe/reflection-probe-component.ts

View workflow job for this annotation

GitHub Actions / Run ESLint

Unexpected unnamed function
@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;
Expand Down Expand Up @@ -441,6 +464,7 @@
this._probe.probeType = this._probeType;
this._probe.size = this._size;
this._probe.cubemap = this._cubemap!;
this._probe.supportTransparency = this._supportTransparency;
}
}
}
134 changes: 133 additions & 1 deletion cocos/render-scene/scene/reflection-probe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,18 @@
THE SOFTWARE.
*/
import { EDITOR } from 'internal:constants';
import { Camera, CameraAperture, CameraFOVAxis, CameraISO, CameraProjection, CameraShutter, CameraType, SkyBoxFlagValue, TrackingType } from './camera';

Check warning on line 25 in cocos/render-scene/scene/reflection-probe.ts

View workflow job for this annotation

GitHub Actions / Run ESLint

This line has a length of 152. Maximum allowed is 150
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, FormatFeatureBit,
} from '../../gfx';
import { TextureCube } from '../../asset/assets/texture-cube';
import { RenderTexture } from '../../asset/assets/render-texture';

Expand Down Expand Up @@ -125,6 +132,18 @@

protected _previewPlane: Node | null = null;

private _supportTransparency = false;
private _intermediateRenderPass: RenderPass | null = null;
private _intermediateDepthStencil: Texture | null = null;

private _intermediateTextures: Texture[] = [];

/**
* @engineInternal
* @mangle
*/
public intermediateFramebuffers: Framebuffer[] = [];

/**
* @en Set probe type,cube or planar.
* @zh 设置探针类型,cube或者planar
Expand Down Expand Up @@ -280,6 +299,37 @@
return this._previewPlane!;
}

/**
* @engineInternal
* @mangle
*/
set supportTransparency (value: boolean) {
this._supportTransparency = value;
}
/**
* @engineInternal
* @mangle
*/
get supportTransparency (): boolean {
return this._supportTransparency;
}

/**
* @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; }
// 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) {
this._probeId = id;
}
Expand All @@ -300,6 +350,9 @@
this.bakedCubeTextures.push(renderTexture);
}
}
if (this.useFloatIntermediateRT() && this._intermediateTextures.length === 0) {
this.initIntermediateTextures(this._resolution, this._resolution, 6);
}
}

public captureCubemap (): void {
Expand All @@ -319,6 +372,9 @@
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.useFloatIntermediateRT()) {
this.initIntermediateTextures(canvasSize.width, canvasSize.height, 1);
}
}
this._syncCameraParams(sourceCamera);
this._transformReflectionCamera(sourceCamera);
Expand Down Expand Up @@ -392,6 +448,8 @@
this.realtimePlanarTexture.destroy();
this.realtimePlanarTexture = null;
}

this._destroyIntermediateTextures();
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
public enable (): void {
Expand Down Expand Up @@ -504,6 +562,80 @@
this.camera.update(true);
}

/**
* @en Create RGBA16F intermediate textures and framebuffers using GFX API directly.
* @zh 直接使用 GFX API 创建 RGBA16F 中间纹理和 Framebuffer。
*/
private 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 });
Expand Down
16 changes: 16 additions & 0 deletions cocos/rendering/custom/scene-culling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -477,6 +480,19 @@ export class SceneCulling {
// Get or create render queue
const renderQueueID = this.getOrCreateRenderQueue(renderQueueKey, sceneData.flags, sceneData.camera);

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
const renderQueueQuery = this.cullingPools.renderQueueQueryRecycle.add();
renderQueueQuery.update(frustumCulledResultID, lightBoundsCullingID, renderQueueID);
Expand Down
19 changes: 12 additions & 7 deletions cocos/rendering/custom/web-pipeline-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -221,15 +222,18 @@ function getPassIndexFromLayout (subModel: SubModel, phaseLayoutId: number): num
export class ProbeHelperQueue {
probeMap: Array<SubModel> = new Array<SubModel>();
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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -269,7 +274,7 @@ export class ProbeHelperQueue {
bUseReflectPass = false;
}
if (passIdx < 0) { continue; }
if (!bUseReflectPass) {
if (!bUseReflectPass || this.useFloatOutput) {
this.probeMap.push(subModel);
}
}
Expand Down
6 changes: 6 additions & 0 deletions cocos/rendering/forward/forward-pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ export class ForwardPipeline extends RenderPipeline {

descriptorSet.update();

// Fullscreen quad for reflection probe RGBE convert pass
const inputAssemblerData = this._createQuadInputAssembler();
this._quadVBOffscreen = inputAssemblerData.quadVB;
this._quadIB = inputAssemblerData.quadIB;
this._quadIAOffscreen = inputAssemblerData.quadIA;

return true;
}

Expand Down
12 changes: 9 additions & 3 deletions cocos/rendering/reflection-probe/reflection-probe-flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,26 @@ export class ReflectionProbeFlow extends RenderFlow {
super.destroy();
}
private _renderStage (camera: Camera, probe: ReflectionProbe, reflectionCamera?: Camera): 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) {
// Unbind planar map before rendering to prevent self-reflection
cclegacy.internal.reflectionProbeManager.updatePlanarMap(probe, null);
probeStage.setUsageInfo(probe, probe.realtimePlanarTexture!.window!.framebuffer, reflectionCamera);
const outputFb = probe.realtimePlanarTexture!.window!.framebuffer;
const fb = useFloatRT ? probe.intermediateFramebuffers[0] : outputFb;
probeStage.setUsageInfo(probe, fb, useFloatRT ? outputFb : null, reflectionCamera);
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++) {
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;
Expand Down
Loading
Loading