Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generate mipmaps for planar reflection #15629

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from
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
6 changes: 5 additions & 1 deletion EngineErrorMap.md
Original file line number Diff line number Diff line change
Expand Up @@ -3411,4 +3411,8 @@ Skin material needs floating-point render target, please check ENABLE_FLOAT_OUTP

### 16304

Skin material may need more accurate calculations, please select a head model of standard size, check the isGlobalStandardSkinObject option in the MeshRender component.
Skin material may need more accurate calculations, please select a head model of standard size, check the isGlobalStandardSkinObject option in the MeshRender component.

### 16305

The reflection camera is invalid, please set the reflection camera.
4 changes: 2 additions & 2 deletions cocos/3d/reflection-probe/reflection-probe-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
*/
import { ccclass, executeInEditMode, menu, playOnFocus, serializable, tooltip, type, visible } from 'cc.decorator';
import { EDITOR, EDITOR_NOT_IN_PREVIEW } from 'internal:constants';
import { CCBoolean, CCObject, Color, Enum, Vec3 } from '../../core';
import { CCBoolean, CCObject, Color, Enum, Vec3, warnID } from '../../core';

import { TextureCube } from '../../asset/assets';
import { scene } from '../../render-scene';
Expand Down Expand Up @@ -151,7 +151,7 @@
this._objFlags ^= CCObject.Flags.IsRotationLocked;
}
if (!this._sourceCamera) {
console.warn('the reflection camera is invalid, please set the reflection camera');
warnID(16305);
} else {
this.probe.switchProbeType(value, this._sourceCamera.camera);
}
Expand All @@ -171,7 +171,7 @@
* @en set render texture size
* @zh 设置渲染纹理大小
*/
@visible(function (this: ReflectionProbe) { return this.probeType === ProbeType.CUBE; })

Check warning on line 174 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 Down Expand Up @@ -199,7 +199,7 @@
* @en Clearing color of the camera.
* @zh 相机的颜色缓冲默认值。
*/
@visible(function (this: ReflectionProbe) { return this._clearFlag === ProbeClearFlag.SOLID_COLOR; })

Check warning on line 202 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 @@ -227,7 +227,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 230 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 @@ -248,7 +248,7 @@
* @en fast bake no convolution.
* @zh 快速烘焙不会进行卷积。
*/
@visible(function (this: ReflectionProbe) { return this.probeType === ProbeType.CUBE; })

Check warning on line 251 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 Down
6 changes: 3 additions & 3 deletions cocos/3d/reflection-probe/reflection-probe-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ export class ReflectionProbeManager {
if (!this._probes[i].realtimePlanarTexture) {
this.updatePlanarMap(this._probes[i], null);
} else {
this.updatePlanarMap(this._probes[i], this._probes[i].realtimePlanarTexture!.getGFXTexture());
this.updatePlanarMap(this._probes[i], this._probes[i].getPlanarReflectionTexture());
}
}
}
Expand Down Expand Up @@ -327,7 +327,7 @@ export class ReflectionProbeManager {
const meshRender = probe.previewPlane.getComponent(MeshRenderer);
if (meshRender) {
if (probe.realtimePlanarTexture) {
this.updatePlanarMap(probe, probe.realtimePlanarTexture.getGFXTexture());
this.updatePlanarMap(probe, probe.getPlanarReflectionTexture());
}
}
}
Expand Down Expand Up @@ -379,7 +379,7 @@ export class ReflectionProbeManager {
buffer[bufferOffset + 6] = 0.0;
buffer[bufferOffset + 7] = 0.0;
//mipCount;
buffer[bufferOffset + 8] = 1.0;
buffer[bufferOffset + 8] = probe.planarMipmapCount;
}
bufferOffset += 4 * dataWidth;
}
Expand Down
4 changes: 2 additions & 2 deletions cocos/asset/assets/simple-texture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ const _regions: BufferTextureCopy[] = [new BufferTextureCopy()];
export type PresumedGFXTextureInfo = Pick<TextureInfo, 'usage' | 'flags' | 'format' | 'levelCount'>;
export type PresumedGFXTextureViewInfo = Pick<TextureViewInfo, 'texture' | 'format' | 'baseLevel' | 'levelCount'>;

function getMipLevel (width: number, height: number): number {
export function getMipLevel (width: number, height: number): number {
let size = Math.max(width, height);
let level = 0;
while (size) { size >>= 1; level++; }
return level;
}

function isPOT (n: number): boolean | 0 { return n && (n & (n - 1)) === 0; }
function canGenerateMipmap (device: Device, w: number, h: number): boolean | 0 {
export function canGenerateMipmap (device: Device, w: number, h: number): boolean | 0 {
const needCheckPOT = device.gfxAPI === API.WEBGL;
if (needCheckPOT) { return isPOT(w) && isPOT(h); }
return true;
Expand Down
5 changes: 3 additions & 2 deletions cocos/render-scene/scene/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
import { Mat4, Vec3, Vec4, geometry, cclegacy, EPSILON } from '../../core';
import { Attribute, DescriptorSet, Device, Buffer, BufferInfo,
BufferUsageBit, MemoryUsageBit, Filter, Address, SamplerInfo, deviceManager, Texture } from '../../gfx';
import { UBOLocal, UBOSH, UBOWorldBound, UNIFORM_LIGHTMAP_TEXTURE_BINDING, UNIFORM_REFLECTION_PROBE_BLEND_CUBEMAP_BINDING, UNIFORM_REFLECTION_PROBE_CUBEMAP_BINDING, UNIFORM_REFLECTION_PROBE_DATA_MAP_BINDING, UNIFORM_REFLECTION_PROBE_TEXTURE_BINDING } from '../../rendering/define';

Check warning on line 39 in cocos/render-scene/scene/model.ts

View workflow job for this annotation

GitHub Actions / Run ESLint

This line has a length of 281. Maximum allowed is 150
import { Root } from '../../root';
import { TextureCube } from '../../asset/assets';
import { ShadowType } from './shadows';
Expand Down Expand Up @@ -1028,7 +1028,7 @@
const sampler = this._device.getSampler(new SamplerInfo(
Filter.LINEAR,
Filter.LINEAR,
Filter.NONE,
Filter.LINEAR,
Address.CLAMP,
Address.CLAMP,
Address.CLAMP,
Expand Down Expand Up @@ -1110,7 +1110,8 @@
sv[UBOLocal.REFLECTION_PROBE_DATA2] = 1.0;
sv[UBOLocal.REFLECTION_PROBE_DATA2 + 1] = 0.0;
sv[UBOLocal.REFLECTION_PROBE_DATA2 + 2] = 0.0;
sv[UBOLocal.REFLECTION_PROBE_DATA2 + 3] = 1.0;
//mipCount;
sv[UBOLocal.REFLECTION_PROBE_DATA2 + 3] = probe.planarMipmapCount;
} else {
sv[UBOLocal.REFLECTION_PROBE_DATA1] = probe.node.worldPosition.x;
sv[UBOLocal.REFLECTION_PROBE_DATA1 + 1] = probe.node.worldPosition.y;
Expand Down
83 changes: 69 additions & 14 deletions cocos/render-scene/scene/reflection-probe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
import { Node } from '../../scene-graph/node';
import { Color, Quat, Rect, toRadian, Vec2, Vec3, geometry, cclegacy, Vec4 } from '../../core';
import { CAMERA_DEFAULT_MASK } from '../../rendering/define';
import { ClearFlagBit, Framebuffer } from '../../gfx';
import { ClearFlagBit, Filter, Format, Framebuffer, Texture, TextureBlit, TextureFlagBit, TextureInfo, TextureType, TextureUsageBit, deviceManager } from '../../gfx';

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

View workflow job for this annotation

GitHub Actions / Run ESLint

This line has a length of 166. Maximum allowed is 150
import { TextureCube } from '../../asset/assets/texture-cube';
import { RenderTexture } from '../../asset/assets/render-texture';
import { canGenerateMipmap, getMipLevel } from '../../asset/assets/simple-texture';
import { legacyCC } from '../../core/global-exports';

export enum ProbeClearFlag {
SKYBOX = SKYBOX_FLAG | ClearFlagBit.DEPTH_STENCIL,
Expand All @@ -53,7 +55,6 @@

export class ReflectionProbe {
public bakedCubeTextures: RenderTexture[] = [];

public realtimePlanarTexture: RenderTexture | null = null;

protected _resolution = 256;
Expand All @@ -63,7 +64,13 @@
protected _probeType = ProbeType.CUBE;
protected _cubemap: TextureCube | null = null;
protected readonly _size = new Vec3(1, 1, 1);

/**
* @en Reflection probe cube pattern preview sphere
* @zh 反射探针cube模式的预览小球
*/
protected _previewSphere: Node | null = null;
protected _previewPlane: Node | null = null;
protected _planarReflectionTexture: Texture | null = null;
/**
* @en Render cubemap's camera
* @zh 渲染cubemap的相机
Expand Down Expand Up @@ -113,13 +120,9 @@
*/
private _up = new Vec3();

/**
* @en Reflection probe cube pattern preview sphere
* @zh 反射探针cube模式的预览小球
*/
protected _previewSphere: Node | null = null;
private _mipmapCount = 1;

protected _previewPlane: Node | null = null;
private _textureRegion: TextureBlit = new TextureBlit();

/**
* @en Set probe type,cube or planar.
Expand Down Expand Up @@ -273,6 +276,10 @@
return this._previewPlane!;
}

get planarMipmapCount (): number {
return this._mipmapCount;
}

constructor (id: number) {
this._probeId = id;
}
Expand All @@ -283,6 +290,26 @@
const pos = this.node.getWorldPosition();
this._boundingBox = geometry.AABB.create(pos.x, pos.y, pos.z, this._size.x, this._size.y, this._size.z);
this._createCamera(cameraNode);

const width = legacyCC.view.getViewportRect().width;
const height = legacyCC.view.getViewportRect().height;
this.realtimePlanarTexture = this._createTargetTexture(width, height);

if (canGenerateMipmap(deviceManager.gfxDevice, width, height)) {
this._mipmapCount = 1;
this._mipmapCount = getMipLevel(width, height);

this._planarReflectionTexture = deviceManager.gfxDevice.createTexture(new TextureInfo(
TextureType.TEX2D,
TextureUsageBit.SAMPLED | TextureUsageBit.TRANSFER_DST,
Format.RGBA8,
width,
height,
TextureFlagBit.GEN_MIPMAP,
1,
this._mipmapCount,
));
}
}

public initBakedTextures (): void {
Expand All @@ -307,11 +334,6 @@
*/
public renderPlanarReflection (sourceCamera: Camera): void {
if (!sourceCamera) return;
if (!this.realtimePlanarTexture) {
const canvasSize = cclegacy.view.getDesignResolutionSize();
this.realtimePlanarTexture = this._createTargetTexture(canvasSize.width, canvasSize.height);
cclegacy.internal.reflectionProbeManager.updatePlanarMap(this, this.realtimePlanarTexture.getGFXTexture());
}
this._syncCameraParams(sourceCamera);
this._transformReflectionCamera(sourceCamera);
this._needRender = true;
Expand Down Expand Up @@ -363,6 +385,10 @@
this.realtimePlanarTexture.destroy();
this.realtimePlanarTexture = null;
}
if (this._planarReflectionTexture) {
this._planarReflectionTexture.destroy();
this._planarReflectionTexture = null;
}
}
public enable (): void {
}
Expand Down Expand Up @@ -404,6 +430,35 @@
//todo: realtime do not use rgbe
return true;
}
/**
* @engineInternal
*/
public copyTextureToMipmap (): void {
if (!this.realtimePlanarTexture || !this._planarReflectionTexture || !deviceManager.gfxDevice) {
return;
}
let width = this.realtimePlanarTexture.width;
let height = this.realtimePlanarTexture.height;

this._textureRegion.srcExtent.width = width;
this._textureRegion.srcExtent.height = height;
const srcTexture = this.realtimePlanarTexture.getGFXTexture()!;
for (let i = 0; i < this._mipmapCount; i++) {
this._textureRegion.dstExtent.width = width;
this._textureRegion.dstExtent.height = height;
this._textureRegion.dstSubres.mipLevel = i;
deviceManager.gfxDevice.commandBuffer.blitTexture(srcTexture, this._planarReflectionTexture, [this._textureRegion], Filter.LINEAR);
width >>= 1;
height >>= 1;
}
}

/**
* @engineInternal
*/
public getPlanarReflectionTexture (): Texture | null {
return this._planarReflectionTexture ? this._planarReflectionTexture : this.realtimePlanarTexture!.getGFXTexture();
}

private _syncCameraParams (camera: Camera): void {
this.camera.projectionType = camera.projectionType;
Expand Down
1 change: 1 addition & 0 deletions cocos/rendering/post-process/passes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export * from './tone-mapping-pass';
export * from './forward-transparency-pass';
export * from './forward-transparency-simple-pass';
export * from './skin-pass';
export * from './reflection-probe-pass';
74 changes: 74 additions & 0 deletions cocos/rendering/post-process/passes/reflection-probe-pass.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Vec4, cclegacy } from '../../../core';

import { ClearFlagBit, Color, Format, StoreOp, Viewport } from '../../../gfx';
import { Camera, ProbeType, ReflectionProbe } from '../../../render-scene/scene';
import { AttachmentType, LightInfo, QueueHint, ResourceResidency, SceneFlags } from '../../custom/types';
import { Pipeline } from '../../custom/pipeline';
import { passContext } from '../utils/pass-context';
import { BasePass } from './base-pass';
import { RenderWindow } from '../../../render-scene/core/render-window';
import { getLoadOpOfClearFlag, updateCameraUBO } from '../../custom/define';

export class ReflectionProbePass extends BasePass {
name = 'ReflectionProbePass';
outputNames = ['ReflectionProbeColor', 'ReflectionProbeDS']

enableInAllEditorCamera = true;
depthBufferShadingScale = 1;

slotName (camera: Camera, index = 0): string {
return this.lastPass!.slotName(camera, index);
}

public render (camera: Camera, ppl: Pipeline): void {
if (!cclegacy.internal.reflectionProbeManager) return;
const probes = cclegacy.internal.reflectionProbeManager.getProbes();
if (probes.length === 0) return;
for (let i = 0; i < probes.length; i++) {
const probe = probes[i];
if (probe.needRender) {
if (probe.probeType === ProbeType.PLANAR) {
this.buildReflectionProbePass(camera, ppl, probe, probe.realtimePlanarTexture.window!, 0);
probe.copyTextureToMipmap();
} else if (probe.probeType === ProbeType.CUBE) {
for (let faceIdx = 0; faceIdx < probe.bakedCubeTextures.length; faceIdx++) {
probe.updateCameraDir(faceIdx);
this.buildReflectionProbePass(camera, ppl, probe, probe.bakedCubeTextures[faceIdx].window!, faceIdx);
}
probe.needRender = false;
}
}
}
}

public buildReflectionProbePass (camera: Camera,
ppl: Pipeline, probe: ReflectionProbe, renderWindow: RenderWindow, faceIdx: number): void {
const cameraName = `Camera${faceIdx}`;
const area = probe.renderArea();
const width = area.x;
const height = area.y;
const probeCamera = probe.camera;

const probePassRTName = `reflectionProbePassColor${cameraName}`;
const probePassDSName = `reflectionProbePassDS${cameraName}`;

if (!ppl.containsResource(probePassRTName)) {
ppl.addRenderWindow(probePassRTName, Format.RGBA8, width, height, renderWindow);
ppl.addDepthStencil(probePassDSName, Format.DEPTH_STENCIL, width, height, ResourceResidency.EXTERNAL);
}
ppl.updateRenderWindow(probePassRTName, renderWindow);
ppl.updateDepthStencil(probePassDSName, width, height);

const probePass = ppl.addRenderPass(width, height, 'default');

probePass.name = `ReflectionProbePass${faceIdx}`;
probePass.setViewport(new Viewport(0, 0, width, height));
probePass.addRenderTarget(probePassRTName, getLoadOpOfClearFlag(probeCamera.clearFlag, AttachmentType.RENDER_TARGET),
StoreOp.STORE, new Color(probeCamera.clearColor.x, probeCamera.clearColor.y, probeCamera.clearColor.z, probeCamera.clearColor.w));
probePass.addDepthStencil(probePassDSName, getLoadOpOfClearFlag(probeCamera.clearFlag, AttachmentType.DEPTH_STENCIL),
StoreOp.STORE, probeCamera.clearDepth, probeCamera.clearStencil, probeCamera.clearFlag);
const passBuilder = probePass.addQueue(QueueHint.RENDER_OPAQUE);
passBuilder.addSceneOfCamera(camera, new LightInfo(), SceneFlags.REFLECTION_PROBE);
updateCameraUBO(passBuilder as unknown as any, probeCamera, ppl);
}
}
7 changes: 4 additions & 3 deletions cocos/rendering/post-process/post-process-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import { director } from '../../game';

import { Camera as CameraComponent } from '../../misc';
import { BloomPass, ColorGradingPass, ForwardTransparencyPass, ForwardTransparencySimplePass, FxaaPass, SkinPass, ToneMappingPass } from './passes';
import { BloomPass, ColorGradingPass, ForwardTransparencyPass, ForwardTransparencySimplePass, FxaaPass, ReflectionProbePass, SkinPass, ToneMappingPass } from './passes';

Check warning on line 22 in cocos/rendering/post-process/post-process-builder.ts

View workflow job for this annotation

GitHub Actions / Run ESLint

This line has a length of 169. Maximum allowed is 150
import { PipelineEventType } from '../pipeline-event';

export class PostProcessBuilder implements PipelineBuilder {
Expand All @@ -46,6 +46,9 @@
this.addPass(new SkinPass());
this.addPass(new ForwardTransparencyPass());

//reflection probe
this.addPass(new ReflectionProbePass());

// pipeline related
this.addPass(new HBAOPass());
this.addPass(new ToneMappingPass());
Expand Down Expand Up @@ -165,8 +168,6 @@
this.applyPreviewCamera(camera);
}

buildReflectionProbePasss(camera, ppl);

passContext.postProcess = camera.postProcess || globalPP;

director.root!.pipelineEvent.emit(PipelineEventType.RENDER_CAMERA_BEGIN, camera);
Expand Down
3 changes: 1 addition & 2 deletions cocos/rendering/reflection-probe/reflection-probe-flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,9 @@ 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) {
cclegacy.internal.reflectionProbeManager.updatePlanarMap(probe, null);
probeStage.setUsageInfo(probe, probe.realtimePlanarTexture!.window!.framebuffer);
probeStage.render(camera);
cclegacy.internal.reflectionProbeManager.updatePlanarMap(probe, probe.realtimePlanarTexture!.getGFXTexture());
probe.copyTextureToMipmap();
} else {
for (let faceIdx = 0; faceIdx < 6; faceIdx++) {
const renderTexture = probe.bakedCubeTextures[faceIdx];
Expand Down
3 changes: 2 additions & 1 deletion editor/assets/chunks/builtin/functionalities/probe.chunk
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ void GetPlanarReflectionProbeData(out vec4 plane, out float planarReflectionDept
float dataMapWidth = 12.0;
vec4 texData1 = GetTexData(cc_reflectionProbeDataMap, dataMapWidth, 0.0, uv_y);
vec4 texData2 = GetTexData(cc_reflectionProbeDataMap, dataMapWidth, 4.0, uv_y);
vec4 texData3 = GetTexData(cc_reflectionProbeDataMap, dataMapWidth, 8.0, uv_y);
plane.xyz = texData1.xyz;
plane.w = texData2.x;
planarReflectionDepthScale = texData2.y;
mipCount = texData2.z;
mipCount = texData3.x;
#else
plane = cc_reflectionProbeData1;
planarReflectionDepthScale = cc_reflectionProbeData2.x;
Expand Down
5 changes: 5 additions & 0 deletions editor/assets/chunks/common/texture/cubemap.chunk
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ float RoughnessToPerceptualRoughness(float roughness)
return sqrt(roughness);
}

float GetRoughnessWithoutConvolution(float roughness)
{
return pow(roughness, 0.5);
}

// for legacy only
#pragma extension([GL_OES_standard_derivatives, __VERSION__ < 110])
vec3 EnvReflectionWithMipFiltering(vec3 R, float roughness, float mipCount, float denoiseIntensity) {
Expand Down
Loading
Loading