From ba6607b0ad92a02498a14792874761f70c9f6a13 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Mon, 23 Dec 2024 20:52:51 -0800 Subject: [PATCH 1/2] Unhooking Metal shadows from the lighting system The D3D9 shadow system would manipulate lights to properly cast shadows. This change decouples Metal shadows from the lighting system and instead passes the required variables directly to the shadow renderer. This allows Metal to avoid expensive light changes to require shadows. Also includes a fix for high shadow strength causing shadows to be too dark. The Metal pipeline was not clamping the output diffuse color into a valid range. --- .../ShaderSrc/FixedPipelineShaders.metal | 5 ++-- .../pfMetalPipeline/plMetalPipeline.cpp | 27 +------------------ .../pfMetalPipeline/plMetalPipeline.h | 3 +-- 3 files changed, 4 insertions(+), 31 deletions(-) diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal index 9dbbbff11a..fcbdb1d953 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal @@ -623,8 +623,6 @@ vertex ColorInOut shadowCastVertexShader(Vertex in float4 position = (float4(in.position, 1.f) * uniforms.localToWorldMatrix); const float3 Ndirection = normalize(float4(in.normal, 0.f) * uniforms.localToWorldMatrix).xyz; - // Shadow casting uses the diffuse material color to control opacity - const half4 MDiffuse = uniforms.diffuseCol; //w is attenation float4 direction; @@ -640,7 +638,8 @@ vertex ColorInOut shadowCastVertexShader(Vertex in } const float3 dotResult = dot(Ndirection, direction.xyz); - const half3 diffuse = MDiffuse.rgb * half3(max(0.h, dotResult)) * shadowState.power; + // Post lighting diffuse color needs to be clamped to the 0..1 range even though >1.f is valid. + const half3 diffuse = clamp(shadowState.opacity * half3(max(0.h, dotResult)) * shadowState.power, 0.f, 1.f); out.vtxColor = half4(diffuse, 1.f); const float4 vCamPosition = position * uniforms.worldToCameraMatrix; diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp index 2ffc8ba3f7..648fffe740 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp @@ -3202,9 +3202,6 @@ bool plMetalPipeline::IPushShadowCastState(plShadowSlave* slave) if (!slave->SetupViewTransform(this)) return false; - // Set texture to U_LUT - fCurrentRenderPassUniforms->specularSrc = 0.0; - // if( !ref->fTexture ) //{ // if( ref->fData ) @@ -3759,6 +3756,7 @@ void plMetalPipeline::IRenderShadowsOntoSpan(const plRenderPrimFunc& render, con // The shadow light isn't used in generating the shadow map, it's used // in projecting the shadow map onto the scene. plShadowState shadowState; + shadowState.opacity = first? mat->GetLayer(0)->GetOpacity() : 1.f; ISetupShadowState(fShadows[i], shadowState); struct plMetalFragmentShaderDescription passDescription{}; @@ -3846,9 +3844,6 @@ void plMetalPipeline::ISetupShadowRcvTextureStages(hsGMaterial* mat) // same one. fForceMatHandle = true; - // Set the D3D lighting/material model - ISetShadowLightState(mat); - // Zbuffering on read-only if (fState.fCurrentDepthStencilState != fDevice.fNoZWriteStencilState) { @@ -3885,26 +3880,6 @@ void plMetalPipeline::ISetupShadowRcvTextureStages(hsGMaterial* mat) fDevice.CurrentRenderCommandEncoder()->setFragmentBytes(&layerIndex, sizeof(int), FragmentShaderArgumentShadowCastAlphaSrc); } -// ISetShadowLightState ////////////////////////////////////////////////////////////////// -// Set the D3D lighting/material model for projecting the shadow map onto this material. -void plMetalPipeline::ISetShadowLightState(hsGMaterial* mat) -{ - fCurrLightingMethod = plSpan::kLiteShadow; - - if (mat && mat->GetNumLayers() && mat->GetLayer(0)) - fCurrentRenderPassUniforms->diffuseCol.r = fCurrentRenderPassUniforms->diffuseCol.g = fCurrentRenderPassUniforms->diffuseCol.b = mat->GetLayer(0)->GetOpacity(); - else - fCurrentRenderPassUniforms->diffuseCol.r = fCurrentRenderPassUniforms->diffuseCol.g = fCurrentRenderPassUniforms->diffuseCol.b = 1.f; - fCurrentRenderPassUniforms->diffuseCol.a = 1.f; - - fCurrentRenderPassUniforms->diffuseSrc = 1.0f; - fCurrentRenderPassUniforms->emissiveSrc = 1.0f; - fCurrentRenderPassUniforms->emissiveCol = 0.0f; - fCurrentRenderPassUniforms->specularSrc = 0.0f; - fCurrentRenderPassUniforms->ambientSrc = 0.0f; - fCurrentRenderPassUniforms->globalAmb = 0.0f; -} - // IDisableLightsForShadow /////////////////////////////////////////////////////////// // Disable any lights that are enabled. We'll only want the shadow light illuminating // the surface. diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h index 4d819490c7..2cda3b4699 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h @@ -232,8 +232,7 @@ class plMetalPipeline : public pl3DPipeline hsGDeviceRef* SharedRenderTargetRef(plRenderTarget* share, plRenderTarget* owner); void IRenderShadowsOntoSpan(const plRenderPrimFunc& render, const plSpan* span, hsGMaterial* mat, plMetalVertexBufferRef* vRef); void ISetupShadowRcvTextureStages(hsGMaterial* mat); - void ISetupShadowSlaveTextures(plShadowSlave* slave); - void ISetShadowLightState(hsGMaterial* mat); + void ISetupShadowSlaveTextures(plShadowSlave* slave); void ISetupShadowState(plShadowSlave* slave, plShadowState& shadowState); void IDisableLightsForShadow(); void IReleaseRenderTargetPools(); From 7ed39a1874f432a2b4d5bd9a369642885f056e21 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sat, 28 Dec 2024 12:30:47 -0800 Subject: [PATCH 2/2] Update Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp Co-authored-by: Adam Johnson --- Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp index 648fffe740..e02ae3e238 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp @@ -3756,7 +3756,7 @@ void plMetalPipeline::IRenderShadowsOntoSpan(const plRenderPrimFunc& render, con // The shadow light isn't used in generating the shadow map, it's used // in projecting the shadow map onto the scene. plShadowState shadowState; - shadowState.opacity = first? mat->GetLayer(0)->GetOpacity() : 1.f; + shadowState.opacity = first ? mat->GetLayer(0)->GetOpacity() : 1.f; ISetupShadowState(fShadows[i], shadowState); struct plMetalFragmentShaderDescription passDescription{};