diff --git a/cocos/render-scene/scene/shadows.ts b/cocos/render-scene/scene/shadows.ts index 4e4c5c60ee1..3ad6037c6c8 100644 --- a/cocos/render-scene/scene/shadows.ts +++ b/cocos/render-scene/scene/shadows.ts @@ -255,6 +255,18 @@ export class Shadows { this._distance = val; } + /** + * @en Positional offset values in planar shading calculations. + * @zh 平面阴影计算中的位置偏移值。 + */ + get planeBias (): number { + return this._planeBias; + } + + set planeBias (val: number) { + this._planeBias = val; + } + /** * @en Shadow color. * @zh 阴影颜色。 @@ -326,6 +338,7 @@ export class Shadows { protected _enabled = false; protected _type = SHADOW_TYPE_NONE; protected _distance = 0; + protected _planeBias = 1.0; protected _normal = new Vec3(0, 1, 0); protected _shadowColor = new Color(0, 0, 0, 76); protected _size: Vec2 = new Vec2(1024, 1024); @@ -356,6 +369,7 @@ export class Shadows { this.normal = shadowsInfo.planeDirection; this.distance = shadowsInfo.planeHeight; + this.planeBias = shadowsInfo.planeBias; this.shadowColor = shadowsInfo.shadowColor; this.maxReceived = shadowsInfo.maxReceived; if (shadowsInfo.shadowMapSize !== this._size.x) { diff --git a/cocos/rendering/custom/web-pipeline.ts b/cocos/rendering/custom/web-pipeline.ts index d322396155a..9bf641544d9 100644 --- a/cocos/rendering/custom/web-pipeline.ts +++ b/cocos/rendering/custom/web-pipeline.ts @@ -621,6 +621,11 @@ function setShadowUBOView (setter: WebSetter, camera: Camera | null, layout = 'd _uboVec.set(_uboVec3.x, _uboVec3.y, _uboVec3.z, -shadowInfo.distance); setter.offsetVec4(_uboVec, uniformOffset); } + uniformOffset = setter.getUniformOffset('cc_shadowWHPBInfo', Type.FLOAT4); + if (setter.hasUniform(uniformOffset)) { + _uboVec.set(0, 0, 0, shadowInfo.planeBias); + setter.offsetVec4(_uboVec, uniformOffset); + } } if (hasCCShadow) { setter.setCurrConstant('CCShadow', layout); diff --git a/cocos/rendering/pipeline-ubo.ts b/cocos/rendering/pipeline-ubo.ts index 81c84ebcf3f..e1888383a30 100644 --- a/cocos/rendering/pipeline-ubo.ts +++ b/cocos/rendering/pipeline-ubo.ts @@ -319,6 +319,8 @@ export class PipelineUBO { } } else { PipelineUBO.updatePlanarNormalAndDistance(shadowInfo, sv); + _vec4ShadowInfo.set(0, 0, 0, shadowInfo.planeBias); + Vec4.toArray(sv, _vec4ShadowInfo, UBOShadow.SHADOW_WIDTH_HEIGHT_PCF_BIAS_INFO_OFFSET); } Color.toArray(sv, shadowInfo.shadowColor, UBOShadow.SHADOW_COLOR_OFFSET); diff --git a/cocos/scene-graph/scene-globals.ts b/cocos/scene-graph/scene-globals.ts index 4907c9e0d2f..870e365763c 100644 --- a/cocos/scene-graph/scene-globals.ts +++ b/cocos/scene-graph/scene-globals.ts @@ -930,6 +930,22 @@ export class ShadowsInfo { return this._distance; } + /** + * @en Positional offset values in planar shading calculations. + * @zh 平面阴影计算中的位置偏移值。 + */ + @tooltip('i18n:shadow.planeBias') + @editable + @type(CCFloat) + @visible(function (this: ShadowsInfo) { return this._type === ShadowType.Planar; }) + set planeBias (val: number) { + this._planeBias = val; + if (this._resource) { this._resource.planeBias = val; } + } + get planeBias (): number { + return this._planeBias; + } + /** * @en get or set shadow max received * @zh 获取或者设置阴影接收的最大光源数量 @@ -972,6 +988,8 @@ export class ShadowsInfo { @serializable protected _distance = 0; @serializable + protected _planeBias = 1.0; + @serializable protected _shadowColor = new Color(0, 0, 0, 76); @serializable protected _maxReceived = 4; diff --git a/editor/assets/chunks/common/lighting/functions.chunk b/editor/assets/chunks/common/lighting/functions.chunk index bfedd416ac7..018ca9f1cbc 100644 --- a/editor/assets/chunks/common/lighting/functions.chunk +++ b/editor/assets/chunks/common/lighting/functions.chunk @@ -93,11 +93,11 @@ vec4 CalculatePlanarShadowPos(vec3 meshWorldPos, vec3 cameraPos, vec3 lightDir, return vec4(shadowPos, dist); } // calculate planar clip pos from world pos -vec4 CalculatePlanarShadowClipPos(vec4 shadowPos, vec3 cameraPos, mat4 matView, mat4 matProj, vec4 nearFar) { +vec4 CalculatePlanarShadowClipPos(vec4 shadowPos, vec3 cameraPos, mat4 matView, mat4 matProj, vec4 nearFar, float bias) { vec4 camPos = matView * vec4(shadowPos.xyz, 1.0); // avoid z-fighting with shadow receive plane, add camera bias with perspective correction // notice that near plane should not be too small, assume near 1, far 1000 float lerpCoef = saturate((nearFar.z < 0.0 ? -camPos.z : camPos.z) / (nearFar.y - nearFar.x)); - camPos.z += mix(nearFar.x * 0.01, nearFar.y * EPSILON_LOWP, lerpCoef); + camPos.z += mix(nearFar.x * 0.01, nearFar.y * EPSILON_LOWP * bias, lerpCoef); return matProj * camPos; } diff --git a/editor/assets/chunks/shading-entries/main-functions/render-planar-shadow/vs.chunk b/editor/assets/chunks/shading-entries/main-functions/render-planar-shadow/vs.chunk index 6de8688e421..ab365ea57da 100644 --- a/editor/assets/chunks/shading-entries/main-functions/render-planar-shadow/vs.chunk +++ b/editor/assets/chunks/shading-entries/main-functions/render-planar-shadow/vs.chunk @@ -22,7 +22,7 @@ void main() In.worldPos = shadowPos.xyz; // Clip Space - In.clipPos = CalculatePlanarShadowClipPos(shadowPos, cc_cameraPos.xyz, cc_matView, cc_matProj, cc_nearFar); + In.clipPos = CalculatePlanarShadowClipPos(shadowPos, cc_cameraPos.xyz, cc_matView, cc_matProj, cc_nearFar, cc_shadowWHPBInfo.w); In.clipPos = SurfacesVertexModifyClipPos(In); // Other Surfaces Function diff --git a/editor/assets/effects/builtin-unlit.effect b/editor/assets/effects/builtin-unlit.effect index 9e66463f93e..0161d984524 100644 --- a/editor/assets/effects/builtin-unlit.effect +++ b/editor/assets/effects/builtin-unlit.effect @@ -224,7 +224,7 @@ CCProgram planar-shadow-vs %{ CCGetWorldMatrixFull(matWorld, matWorldIT); vec3 worldPos = (matWorld * position).xyz; vec4 shadowPos = CalculatePlanarShadowPos(worldPos, cc_cameraPos.xyz, cc_mainLitDir.xyz, cc_planarNDInfo); - position = CalculatePlanarShadowClipPos(shadowPos, cc_cameraPos.xyz, cc_matView, cc_matProj, cc_nearFar); + position = CalculatePlanarShadowClipPos(shadowPos, cc_cameraPos.xyz, cc_matView, cc_matProj, cc_nearFar, cc_shadowWHPBInfo.w); v_dist = shadowPos.w; return position; } diff --git a/editor/assets/effects/legacy/standard.effect b/editor/assets/effects/legacy/standard.effect index 1fb84c73d7e..631ebac9d74 100644 --- a/editor/assets/effects/legacy/standard.effect +++ b/editor/assets/effects/legacy/standard.effect @@ -479,7 +479,7 @@ CCProgram planar-shadow-vs %{ CCGetWorldMatrixFull(matWorld, matWorldIT); vec3 worldPos = (matWorld * position).xyz; vec4 shadowPos = CalculatePlanarShadowPos(worldPos, cc_cameraPos.xyz, cc_mainLitDir.xyz, cc_planarNDInfo); - position = CalculatePlanarShadowClipPos(shadowPos, cc_cameraPos.xyz, cc_matView, cc_matProj, cc_nearFar); + position = CalculatePlanarShadowClipPos(shadowPos, cc_cameraPos.xyz, cc_matView, cc_matProj, cc_nearFar, cc_shadowWHPBInfo.w); v_dist = shadowPos.w; return position; } diff --git a/editor/assets/effects/legacy/toon.effect b/editor/assets/effects/legacy/toon.effect index bb675a2c08a..af1f9cc92e5 100644 --- a/editor/assets/effects/legacy/toon.effect +++ b/editor/assets/effects/legacy/toon.effect @@ -383,7 +383,7 @@ CCProgram planar-shadow-vs %{ CCGetWorldMatrixFull(matWorld, matWorldIT); vec3 worldPos = (matWorld * position).xyz; vec4 shadowPos = CalculatePlanarShadowPos(worldPos, cc_cameraPos.xyz, cc_mainLitDir.xyz, cc_planarNDInfo); - position = CalculatePlanarShadowClipPos(shadowPos, cc_cameraPos.xyz, cc_matView, cc_matProj, cc_nearFar); + position = CalculatePlanarShadowClipPos(shadowPos, cc_cameraPos.xyz, cc_matView, cc_matProj, cc_nearFar, cc_shadowWHPBInfo.w); v_dist = shadowPos.w; return position; } diff --git a/editor/assets/effects/pipeline/planar-shadow.effect b/editor/assets/effects/pipeline/planar-shadow.effect index 6d2d2ab39b8..da8f851bb0f 100644 --- a/editor/assets/effects/pipeline/planar-shadow.effect +++ b/editor/assets/effects/pipeline/planar-shadow.effect @@ -42,7 +42,7 @@ CCProgram planar-shadow-vs %{ CCGetWorldMatrixFull(matWorld, matWorldIT); vec3 worldPos = (matWorld * position).xyz; vec4 shadowPos = CalculatePlanarShadowPos(worldPos, cc_cameraPos.xyz, cc_mainLitDir.xyz, cc_planarNDInfo); - position = CalculatePlanarShadowClipPos(shadowPos, cc_cameraPos.xyz, cc_matView, cc_matProj, cc_nearFar); + position = CalculatePlanarShadowClipPos(shadowPos, cc_cameraPos.xyz, cc_matView, cc_matProj, cc_nearFar, cc_shadowWHPBInfo.w); v_dist = shadowPos.w; return position; } diff --git a/editor/i18n/en/localization.js b/editor/i18n/en/localization.js index 7c9e9098f80..1e1703a6c74 100755 --- a/editor/i18n/en/localization.js +++ b/editor/i18n/en/localization.js @@ -239,6 +239,7 @@ module.exports = link(mixin({ shadowColor: 'The planar shadow color', planeDirection: 'The normal vector of the plane which receives shadow.', planeHeight: 'The height from the origin of the plane which receives shadow.', + planeBias:'Positional offset values in planar shading calculations.', shadowMapSize: 'Shadowmap resolutions.', maxReceived: 'Number of the effective light sources that produce shadows.', }, diff --git a/editor/i18n/zh/localization.js b/editor/i18n/zh/localization.js index 63ad5d1e62d..0a0ea4306d6 100755 --- a/editor/i18n/zh/localization.js +++ b/editor/i18n/zh/localization.js @@ -239,6 +239,7 @@ module.exports = link(mixin({ shadowColor: '平面阴影颜色', planeDirection: '阴影接收平面的法线,垂直于阴影,用于调整阴影的倾斜度', planeHeight: '阴影接收平面距离原点的高度', + planeBias: '平面阴影计算中的位置偏移值', shadowMapSize: '阴影贴图分辨率,目前支持 Low_256x256、Medium_512x512、High_1024x1024、Ultra_2048x2048 四种精度的纹理', maxReceived: '产生阴影的有效光源数量', }, diff --git a/native/cocos/renderer/pipeline/PipelineUBO.cpp b/native/cocos/renderer/pipeline/PipelineUBO.cpp index 0415987ae46..194a7dc1a68 100644 --- a/native/cocos/renderer/pipeline/PipelineUBO.cpp +++ b/native/cocos/renderer/pipeline/PipelineUBO.cpp @@ -301,6 +301,8 @@ void PipelineUBO::updateShadowUBOView(const RenderPipeline *pipeline, ccstd::arr } } else if (mainLight && shadowInfo->getType() == scene::ShadowType::PLANAR) { PipelineUBO::updatePlanarNormalAndDistance(shadowInfo, shadowBufferView); + const float shadowWHPBInfos[4] = {0, 0, 0, shadowInfo->getPlaneBias()}; + memcpy(sv.data() + UBOShadow::SHADOW_WIDTH_HEIGHT_PCF_BIAS_INFO_OFFSET, &shadowWHPBInfos, sizeof(shadowWHPBInfos)); } memcpy(sv.data() + UBOShadow::SHADOW_COLOR_OFFSET, shadowInfo->getShadowColor4f().data(), sizeof(float) * 4); diff --git a/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.cpp b/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.cpp index fabf92f5d61..ad997864dcd 100644 --- a/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.cpp @@ -357,6 +357,10 @@ void setShadowUBOView( setVec4Impl(data, layoutGraph, "cc_planarNDInfo", Vec4(tempVec3.x, tempVec3.y, tempVec3.z, -shadowInfo.getDistance())); + vec4ShadowInfo.set( + 0, 0, + 0, shadowInfo.getPlaneBias()); + setVec4Impl(data, layoutGraph, "cc_shadowWHPBInfo", vec4ShadowInfo); } { const auto &color = shadowInfo.getShadowColor4f(); diff --git a/native/cocos/scene/Shadow.cpp b/native/cocos/scene/Shadow.cpp index 3e33bd7be04..a5495e6c442 100644 --- a/native/cocos/scene/Shadow.cpp +++ b/native/cocos/scene/Shadow.cpp @@ -76,6 +76,13 @@ void ShadowsInfo::setPlaneHeight(float val) { } } +void ShadowsInfo::setPlaneBias(float val) { + _planeBias = val; + if (_resource != nullptr) { + _resource->setPlaneBias(val); + } +} + void ShadowsInfo::setMaxReceived(uint32_t val) { _maxReceived = val; if (_resource != nullptr) { @@ -112,6 +119,7 @@ void Shadows::initialize(const ShadowsInfo &shadowsInfo) { setType(shadowsInfo.getType()); setNormal(shadowsInfo.getPlaneDirection()); setDistance(shadowsInfo.getPlaneHeight()); + setPlaneBias(shadowsInfo.getPlaneBias()); setMaxReceived(shadowsInfo.getMaxReceived()); if (fabsf(shadowsInfo.getShadowMapSize() - _size.x) > 0.1F) { setSize(Vec2(shadowsInfo.getShadowMapSize(), shadowsInfo.getShadowMapSize())); diff --git a/native/cocos/scene/Shadow.h b/native/cocos/scene/Shadow.h index ac41a58af50..36b3fbe7150 100644 --- a/native/cocos/scene/Shadow.h +++ b/native/cocos/scene/Shadow.h @@ -247,6 +247,15 @@ class ShadowsInfo : public RefCounted { return _distance; } + /** + * @en Positional offset values in planar shading calculations. + * @zh 平面阴影计算中的位置偏移值 + */ + void setPlaneBias(float val); + inline float getPlaneBias() const { + return _planeBias; + } + /** * @en get or set shadow max received * @zh 获取或者设置阴影接收的最大光源数量 @@ -275,14 +284,21 @@ class ShadowsInfo : public RefCounted { void activate(Shadows *resource); bool _enabled{false}; + ShadowType _type{ShadowType::PLANAR}; - Vec3 _normal{0.F, 1.F, 0.F}; - float _distance{0.F}; - Color _shadowColor{0, 0, 0, 76}; + + Shadows *_resource{nullptr}; + uint32_t _maxReceived{4}; + + float _distance{0.F}; + float _planeBias{1.0F}; + Vec2 _size{1024.F, 1024.F}; - Shadows *_resource{nullptr}; + Vec3 _normal{0.F, 1.F, 0.F}; + + Color _shadowColor{0, 0, 0, 76}; }; class Shadows final { @@ -332,6 +348,13 @@ class Shadows final { inline float getDistance() const { return _distance; } inline void setDistance(float val) { _distance = val; } + /** + * @en Positional offset values in planar shading calculations. + * @zh 平面阴影计算中的位置偏移值 + */ + inline float getPlaneBias() const { return _planeBias; } + inline void setPlaneBias(float val) { _planeBias = val; } + /** * @en Shadow color. * @zh 阴影颜色。 @@ -398,11 +421,16 @@ class Shadows final { void createInstanceMaterial(); void createMaterial(); - /** - * @en The bounding sphere of the shadow map. - * @zh 用于计算固定区域阴影 Shadow map 的场景包围球. - */ - geometry::Sphere _fixedSphere{0.0F, 0.0F, 0.0F, 0.01F}; + bool _enabled{false}; + bool _shadowMapDirty{false}; + + ShadowType _type{ShadowType::NONE}; + + IntrusivePtr _material{nullptr}; + IntrusivePtr _instancingMaterial{nullptr}; + + float _distance{0.F}; + float _planeBias{1.0F}; /** * @en get or set shadow max received. @@ -410,18 +438,21 @@ class Shadows final { */ uint32_t _maxReceived{4}; + /** + * @en The bounding sphere of the shadow map. + * @zh 用于计算固定区域阴影 Shadow map 的场景包围球. + */ + geometry::Sphere _fixedSphere{0.0F, 0.0F, 0.0F, 0.01F}; + + Vec2 _size{1024.F, 1024.F}; + // public properties of shadow Vec3 _normal{0.F, 1.F, 0.F}; + Color _shadowColor{0, 0, 0, 76}; ccstd::array _shadowColor4f{0.F, 0.F, 0.F, 76.F / 255.F}; + Mat4 _matLight; - IntrusivePtr _material{nullptr}; - IntrusivePtr _instancingMaterial{nullptr}; - Vec2 _size{1024.F, 1024.F}; - bool _enabled{false}; - float _distance{0.F}; - ShadowType _type{ShadowType::NONE}; - bool _shadowMapDirty{false}; }; } // namespace scene diff --git a/native/tools/swig-config/scene.i b/native/tools/swig-config/scene.i index f80fd18cece..0dcdb43b647 100644 --- a/native/tools/swig-config/scene.i +++ b/native/tools/swig-config/scene.i @@ -531,6 +531,7 @@ using namespace cc; %attribute(cc::scene::ShadowsInfo, cc::Color&, shadowColor, getShadowColor, setShadowColor); %attribute(cc::scene::ShadowsInfo, cc::Vec3&, planeDirection, getPlaneDirection, setPlaneDirection); %attribute(cc::scene::ShadowsInfo, float, planeHeight, getPlaneHeight, setPlaneHeight); +%attribute(cc::scene::ShadowsInfo, float, planeBias, getPlaneBias, setPlaneBias); %attribute(cc::scene::ShadowsInfo, uint32_t, maxReceived, getMaxReceived, setMaxReceived); %attribute(cc::scene::ShadowsInfo, float, shadowMapSize, getShadowMapSize, setShadowMapSize); @@ -538,6 +539,7 @@ using namespace cc; %attribute(cc::scene::Shadows, cc::scene::ShadowType, type, getType, setType); %attribute(cc::scene::Shadows, cc::Vec3&, normal, getNormal, setNormal); %attribute(cc::scene::Shadows, float, distance, getDistance, setDistance); +%attribute(cc::scene::Shadows, float, planeBias, getPlaneBias, setPlaneBias); %attribute(cc::scene::Shadows, cc::Color&, shadowColor, getShadowColor, setShadowColor); %attribute(cc::scene::Shadows, uint32_t, maxReceived, getMaxReceived, setMaxReceived); %attribute(cc::scene::Shadows, cc::Vec2&, size, getSize, setSize);