From 7ea2b57315fe0dcd4677b91e683e2eb97897b368 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Sun, 20 Sep 2020 02:05:19 -0700 Subject: [PATCH] restructure lights to use one pass per light --- Effects/DeferredPBR_AmbientLightEffect.cs | 31 +++ Effects/DeferredPBR_DirectionalLightEffect.cs | 152 ++++++++++++++ Effects/DeferredPBR_PointLightEffect.cs | 77 +++++++ .../FXB/DeferredPBR_AmbientLightEffect.fxb | Bin 0 -> 1016 bytes .../DeferredPBR_DirectionalLightEffect.fxb | Bin 0 -> 6916 bytes Effects/FXB/DeferredPBR_GBufferEffect.fxb | Bin 7476 -> 7476 bytes Effects/FXB/DeferredPBR_PointLightEffect.fxb | Bin 0 -> 3108 bytes .../HLSL/DeferredPBR_AmbientLightEffect.fx | 54 +++++ .../DeferredPBR_DirectionalLightEffect.fx | 190 ++++++++++++++++++ Effects/HLSL/DeferredPBR_GBufferEffect.fx | 40 ++-- Effects/HLSL/DeferredPBR_PointLightEffect.fx | 91 +++++++++ Effects/HLSL/Lighting.fxh | 71 +++++++ Kav.Core.csproj | 9 + Kav.Framework.csproj | 9 + README.md | 1 + Renderer.cs | 173 +++++++++++----- Resources.cs | 38 ++++ 17 files changed, 864 insertions(+), 72 deletions(-) create mode 100644 Effects/DeferredPBR_AmbientLightEffect.cs create mode 100644 Effects/DeferredPBR_DirectionalLightEffect.cs create mode 100644 Effects/DeferredPBR_PointLightEffect.cs create mode 100644 Effects/FXB/DeferredPBR_AmbientLightEffect.fxb create mode 100644 Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb create mode 100644 Effects/FXB/DeferredPBR_PointLightEffect.fxb create mode 100644 Effects/HLSL/DeferredPBR_AmbientLightEffect.fx create mode 100644 Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx create mode 100644 Effects/HLSL/DeferredPBR_PointLightEffect.fx create mode 100644 Effects/HLSL/Lighting.fxh diff --git a/Effects/DeferredPBR_AmbientLightEffect.cs b/Effects/DeferredPBR_AmbientLightEffect.cs new file mode 100644 index 0000000..f1d5e31 --- /dev/null +++ b/Effects/DeferredPBR_AmbientLightEffect.cs @@ -0,0 +1,31 @@ +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + + public class DeferredPBR_AmbientLightEffect : Effect + { + EffectParameter gPositionParam; + EffectParameter gAlbedoParam; + + public Texture2D GPosition { get; set; } + public Texture2D GAlbedo { get; set; } + + public DeferredPBR_AmbientLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_AmbientLightEffect) + { + CacheEffectParameters(); + } + + protected override void OnApply() + { + gPositionParam.SetValue(GPosition); + gAlbedoParam.SetValue(GAlbedo); + } + + void CacheEffectParameters() + { + gPositionParam = Parameters["gPosition"]; + gAlbedoParam = Parameters["gAlbedo"]; + } + } +} diff --git a/Effects/DeferredPBR_DirectionalLightEffect.cs b/Effects/DeferredPBR_DirectionalLightEffect.cs new file mode 100644 index 0000000..fb24e78 --- /dev/null +++ b/Effects/DeferredPBR_DirectionalLightEffect.cs @@ -0,0 +1,152 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + public class DeferredPBR_DirectionalLightEffect : Effect + { + EffectParameter gPositionParam; + EffectParameter gAlbedoParam; + EffectParameter gNormalParam; + EffectParameter gMetallicRoughnessParam; + + EffectParameter shadowMapOneParam; + EffectParameter shadowMapTwoParam; + EffectParameter shadowMapThreeParam; + EffectParameter shadowMapFourParam; + + EffectParameter eyePositionParam; + + EffectParameter directionalLightColorParam; + EffectParameter directionalLightDirectionParam; + + EffectParameter cascadeFarPlanesParam; + + EffectParameter lightSpaceMatrixOneParam; + EffectParameter lightSpaceMatrixTwoParam; + EffectParameter lightSpaceMatrixThreeParam; + EffectParameter lightSpaceMatrixFourParam; + + EffectParameter viewMatrixParam; + + public Texture2D GPosition { get; set; } + public Texture2D GAlbedo { get; set; } + public Texture2D GNormal { get; set; } + public Texture2D GMetallicRoughness { get; set; } + + public Texture2D ShadowMapOne { get; set; } + public Texture2D ShadowMapTwo { get; set; } + public Texture2D ShadowMapThree { get; set; } + public Texture2D ShadowMapFour { get; set; } + + public Vector3 EyePosition { get; set; } + + public Vector3 DirectionalLightDirection { get; set; } + public Vector3 DirectionalLightColor { get; set; } + + public readonly float[] CascadeFarPlanes; + + public Matrix LightSpaceMatrixOne { get; set; } + public Matrix LightSpaceMatrixTwo { get; set; } + public Matrix LightSpaceMatrixThree { get; set; } + public Matrix LightSpaceMatrixFour { get; set; } + + public Matrix ViewMatrix { get; set; } + + public DeferredPBR_DirectionalLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_DirectionalLightEffect) + { + CascadeFarPlanes = new float[4]; + CacheEffectParameters(); + } + + public DeferredPBR_DirectionalLightEffect(DeferredPBR_DirectionalLightEffect cloneSource) : base(cloneSource) + { + GPosition = cloneSource.GPosition; + GAlbedo = cloneSource.GAlbedo; + GNormal = cloneSource.GNormal; + GMetallicRoughness = cloneSource.GMetallicRoughness; + + ShadowMapOne = cloneSource.ShadowMapOne; + ShadowMapTwo = cloneSource.ShadowMapTwo; + ShadowMapThree = cloneSource.ShadowMapThree; + ShadowMapFour = cloneSource.ShadowMapFour; + + EyePosition = cloneSource.EyePosition; + + DirectionalLightDirection = cloneSource.DirectionalLightDirection; + DirectionalLightColor = cloneSource.DirectionalLightColor; + + CascadeFarPlanes = new float[4]; + for (int i = 0 ; i < 4; i++) + { + CascadeFarPlanes[i] = cloneSource.CascadeFarPlanes[i]; + } + + LightSpaceMatrixOne = cloneSource.LightSpaceMatrixOne; + LightSpaceMatrixTwo = cloneSource.LightSpaceMatrixTwo; + LightSpaceMatrixThree = cloneSource.LightSpaceMatrixThree; + LightSpaceMatrixFour = cloneSource.LightSpaceMatrixFour; + + ViewMatrix = cloneSource.ViewMatrix; + } + + public override Effect Clone() + { + return new DeferredPBR_DirectionalLightEffect(this); + } + + protected override void OnApply() + { + gPositionParam.SetValue(GPosition); + gAlbedoParam.SetValue(GAlbedo); + gNormalParam.SetValue(GNormal); + gMetallicRoughnessParam.SetValue(GMetallicRoughness); + + shadowMapOneParam.SetValue(ShadowMapOne); + shadowMapTwoParam.SetValue(ShadowMapTwo); + shadowMapThreeParam.SetValue(ShadowMapThree); + shadowMapFourParam.SetValue(ShadowMapFour); + + eyePositionParam.SetValue(EyePosition); + + directionalLightDirectionParam.SetValue(DirectionalLightDirection); + directionalLightColorParam.SetValue(DirectionalLightColor); + + cascadeFarPlanesParam.SetValue(CascadeFarPlanes); + + lightSpaceMatrixOneParam.SetValue(LightSpaceMatrixOne); + lightSpaceMatrixTwoParam.SetValue(LightSpaceMatrixTwo); + lightSpaceMatrixThreeParam.SetValue(LightSpaceMatrixThree); + lightSpaceMatrixFourParam.SetValue(LightSpaceMatrixFour); + + viewMatrixParam.SetValue(ViewMatrix); + } + + void CacheEffectParameters() + { + gPositionParam = Parameters["gPosition"]; + gAlbedoParam = Parameters["gAlbedo"]; + gNormalParam = Parameters["gNormal"]; + gMetallicRoughnessParam = Parameters["gMetallicRoughness"]; + + shadowMapOneParam = Parameters["shadowMapOne"]; + shadowMapTwoParam = Parameters["shadowMapTwo"]; + shadowMapThreeParam = Parameters["shadowMapThree"]; + shadowMapFourParam = Parameters["shadowMapFour"]; + + eyePositionParam = Parameters["EyePosition"]; + + directionalLightDirectionParam = Parameters["DirectionalLightDirection"]; + directionalLightColorParam = Parameters["DirectionalLightColor"]; + + cascadeFarPlanesParam = Parameters["CascadeFarPlanes"]; + + lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"]; + lightSpaceMatrixTwoParam = Parameters["LightSpaceMatrixTwo"]; + lightSpaceMatrixThreeParam = Parameters["LightSpaceMatrixThree"]; + lightSpaceMatrixFourParam = Parameters["LightSpaceMatrixFour"]; + + viewMatrixParam = Parameters["ViewMatrix"]; + } + } +} diff --git a/Effects/DeferredPBR_PointLightEffect.cs b/Effects/DeferredPBR_PointLightEffect.cs new file mode 100644 index 0000000..6f959ea --- /dev/null +++ b/Effects/DeferredPBR_PointLightEffect.cs @@ -0,0 +1,77 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + public class DeferredPBR_PointLightEffect : Effect + { + EffectParameter gPositionParam; + EffectParameter gAlbedoParam; + EffectParameter gNormalParam; + EffectParameter gMetallicRoughnessParam; + + EffectParameter eyePositionParam; + + EffectParameter pointLightColorParam; + EffectParameter pointLightPositionParam; + + public Texture2D GPosition { get; set; } + public Texture2D GAlbedo { get; set; } + public Texture2D GNormal { get; set; } + public Texture2D GMetallicRoughness { get; set; } + + public Vector3 EyePosition { get; set; } + + public Vector3 PointLightPosition { get; set; } + public Vector3 PointLightColor { get; set; } + + public DeferredPBR_PointLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_PointLightEffect) + { + CacheEffectParameters(); + } + + public DeferredPBR_PointLightEffect(DeferredPBR_PointLightEffect cloneSource) : base(cloneSource) + { + GPosition = cloneSource.GPosition; + GAlbedo = cloneSource.GAlbedo; + GNormal = cloneSource.GNormal; + GMetallicRoughness = cloneSource.GMetallicRoughness; + + EyePosition = cloneSource.EyePosition; + + PointLightPosition = cloneSource.PointLightPosition; + PointLightColor = cloneSource.PointLightColor; + } + + public override Effect Clone() + { + return new DeferredPBR_PointLightEffect(this); + } + + protected override void OnApply() + { + gPositionParam.SetValue(GPosition); + gAlbedoParam.SetValue(GAlbedo); + gNormalParam.SetValue(GNormal); + gMetallicRoughnessParam.SetValue(GMetallicRoughness); + + eyePositionParam.SetValue(EyePosition); + + pointLightPositionParam.SetValue(PointLightPosition); + pointLightColorParam.SetValue(PointLightColor); + } + + void CacheEffectParameters() + { + gPositionParam = Parameters["gPosition"]; + gAlbedoParam = Parameters["gAlbedo"]; + gNormalParam = Parameters["gNormal"]; + gMetallicRoughnessParam = Parameters["gMetallicRoughness"]; + + eyePositionParam = Parameters["EyePosition"]; + + pointLightPositionParam = Parameters["PointLightPosition"]; + pointLightColorParam = Parameters["PointLightColor"]; + } + } +} diff --git a/Effects/FXB/DeferredPBR_AmbientLightEffect.fxb b/Effects/FXB/DeferredPBR_AmbientLightEffect.fxb new file mode 100644 index 0000000000000000000000000000000000000000..7cb8ee712be74b75e6f90b30631a3ba3dd848762 GIT binary patch literal 1016 zcmb7DJxc>Y5Z%jZa*7az6e$ExY!re-v=lTSg++vD6zzhVOFT$&;S#J<*c-I5u(-lP z@Gn@1zri11k;1~(PIA7t*$p9$I50c&-ptO<&g~g9;hY_R$em>z7O0FkYfrfMhq1J`iKb2f`1%@As8i`f%YHim;=lpF`B`O;&cwI z%~@Ri^FFHAE?$)En|Cmjk8Et>T+dy{ HC;#DJKYo&2 literal 0 HcmV?d00001 diff --git a/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb b/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb new file mode 100644 index 0000000000000000000000000000000000000000..e47ec4036bb500428a8c76598e5740752765984b GIT binary patch literal 6916 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 OLKDro+x+(@=zjr((opOG literal 0 HcmV?d00001 diff --git a/Effects/FXB/DeferredPBR_GBufferEffect.fxb b/Effects/FXB/DeferredPBR_GBufferEffect.fxb index 9b2d6844f486894e8a28f804c05221fee8d81938..413c4150f99c4b125895c10345c85e7fc9caf58a 100644 GIT binary patch delta 146 zcmdmDwZ&?~J2s{U`^f_QDw9Rn(#Vb4j delta 160 zcmdmDwZ&?~J2o~3AZW0koWrFsS%f`}F?6y&ry?^m1HH8KHa~W9VjSZf{1I$N>(W%`#<0Nkc2W&i*H diff --git a/Effects/FXB/DeferredPBR_PointLightEffect.fxb b/Effects/FXB/DeferredPBR_PointLightEffect.fxb new file mode 100644 index 0000000000000000000000000000000000000000..e3457052ceb8360da247256aaa21341ede7f6c7f GIT binary patch literal 3108 zcmb7`&ud&&6vyxTG0n6}Qfn7Q1PAIup-77^Txd;`2nwmhq=D{YY^IZeNnV(YV!_3f zx^N*l=%x!7FOr2!wl0c9bm1S6MZtxeE@siCJ1Iz?pYQ$g^75kuZ+mjj`JSKm+#heE z)5X(zV(b{>ZwkHaMEf`5?-{5ymuV4CH>?U@17y>QL)i=hNm&W@}OI$6l{V@=3XW$7UCrfPAbnbMQT{V*RKw+q@rT z-L>UTnfH^5%_(exZ}8JxUGv)rZ$UP%i*+RLH(wby&I z?bd3qbT9L6pWuJ?fd6#X$-3q=m~i!_I&@~$^TT*(-8pnK&D+gxx4AMmHNRx*0(%aU zje06x{uiJyF2URk0`phU{fhh({KJa;FZ|h%6utpJTakP4UsvR9_`Qmp(vwD|FYmzL ztjJ%&->=AjdtMZ4>vL+ez3NJqTb{LK6Zq<+Qxi@0S08(Ay@J5^%;fF&Tp>OT1e z=ES1o_MTy4W-R97-SBoR7IS&O@L2T>M>d9{A3yfbtvh%7a(RvqJu5pqDK|ychL}h1 zbhvxR*dBZZaU_d^`9+1OlqbLq0V-oDk%HnZEk#%uGhH$IqMm~AYqF0VAZjp=M{ zqs`;nxN`Z8D`w|owmmTu`Ck(sPSS;|#`NEVCWWWqXa2nSEA+1GQ2#gKHE`Kv=kEPd z6nAFsJ^AtKD{Lp{r_as|#A81&^@%+2QH75yd;&kE?KBhkj_?`fI46-AlYBQaPUd!6 z87$}h!@k}}XNmKMOC5dDrv~p^SRKdofG*g{_OLFtIf{N<$w5Bl?N@VfZ_>ooV{@tw z<#ja{*8WxUrr0Ftv_5()d=Rrf>1tjV)>6YRg(OF;MZEGleMJ1K99vm=d9SdKuwBGw zm^(Q}HWfKVF0Ab8jOfkKQnj{(+MGV7?nO&lEC2Cdw3M16cbIY-Xh_^*%lRe9BTFHV z!G^}``mlz`6nC`-z85~X&V%+Q^eFW{A%TiyxN Z^1oZ9XaCDl>izF&>4k+M{_yYp{{XO closestDepth ? 1.0 : 0.0; + } + } + + shadowFactor /= 9.0; + + if (projectionCoords.z > 1.0) + { + shadowFactor = 1.0; + } + + return shadowFactor; +} + +float4 ComputeColor( + float3 worldPosition, + float3 worldNormal, + float3 albedo, + float metallic, + float roughness +) { + float3 V = normalize(EyePosition - worldPosition); + float3 N = normalize(worldNormal); + + float3 F0 = float3(0.04, 0.04, 0.04); + F0 = lerp(F0, albedo, metallic); + + float3 L = normalize(DirectionalLightDirection); + float3 radiance = DirectionalLightColor; + + float shadow = ComputeShadow(worldPosition, N, L); + float3 color = ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, (1.0 - shadow)); + + return float4(color, 1.0); +} + +float4 main_ps(PixelInput input) : SV_TARGET0 +{ + float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb; + float3 normal = SAMPLE_TEXTURE(gNormal, input.TexCoord).xyz; + float3 albedo = SAMPLE_TEXTURE(gAlbedo, input.TexCoord).rgb; + float2 metallicRoughness = SAMPLE_TEXTURE(gMetallicRoughness, input.TexCoord).rg; + + return ComputeColor( + worldPosition, + normal, + albedo, + metallicRoughness.r, + metallicRoughness.g + ); +} + +Technique DeferredPBR_Directional +{ + Pass + { + VertexShader = compile vs_3_0 main_vs(); + PixelShader = compile ps_3_0 main_ps(); + } +} diff --git a/Effects/HLSL/DeferredPBR_GBufferEffect.fx b/Effects/HLSL/DeferredPBR_GBufferEffect.fx index 63cb2ce..f24da19 100644 --- a/Effects/HLSL/DeferredPBR_GBufferEffect.fx +++ b/Effects/HLSL/DeferredPBR_GBufferEffect.fx @@ -79,10 +79,10 @@ PixelOutput NonePS(PixelInput input) { PixelOutput output; - output.gPosition = float4(input.PositionWorld, 0.0); - output.gNormal = float4(normalize(input.NormalWorld), 0.0); + output.gPosition = float4(input.PositionWorld, 1.0); + output.gNormal = float4(normalize(input.NormalWorld), 1.0); output.gAlbedo = float4(AlbedoValue, 1.0); - output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0); + output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0); return output; } @@ -91,10 +91,10 @@ PixelOutput AlbedoPS(PixelInput input) { PixelOutput output; - output.gPosition = float4(input.PositionWorld, 0.0); - output.gNormal = float4(normalize(input.NormalWorld), 0.0); + output.gPosition = float4(input.PositionWorld, 1.0); + output.gNormal = float4(normalize(input.NormalWorld), 1.0); output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord); - output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0); + output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0); return output; } @@ -103,8 +103,8 @@ PixelOutput MetallicRoughnessPS(PixelInput input) { PixelOutput output; - output.gPosition = float4(input.PositionWorld, 0.0); - output.gNormal = float4(normalize(input.NormalWorld), 0.0); + output.gPosition = float4(input.PositionWorld, 1.0); + output.gNormal = float4(normalize(input.NormalWorld), 1.0); output.gAlbedo = float4(AlbedoValue, 1.0); output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord); @@ -115,10 +115,10 @@ PixelOutput NormalPS(PixelInput input) { PixelOutput output; - output.gPosition = float4(input.PositionWorld, 0.0); - output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0); + output.gPosition = float4(input.PositionWorld, 1.0); + output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0); output.gAlbedo = float4(AlbedoValue, 1.0); - output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0); + output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0); return output; } @@ -127,8 +127,8 @@ PixelOutput AlbedoMetallicRoughnessPS(PixelInput input) { PixelOutput output; - output.gPosition = float4(input.PositionWorld, 0.0); - output.gNormal = float4(normalize(input.NormalWorld), 0.0); + output.gPosition = float4(input.PositionWorld, 1.0); + output.gNormal = float4(normalize(input.NormalWorld), 1.0); output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord); output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord); @@ -139,10 +139,10 @@ PixelOutput AlbedoNormalPS(PixelInput input) { PixelOutput output; - output.gPosition = float4(input.PositionWorld, 0.0); - output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0); + output.gPosition = float4(input.PositionWorld, 1.0); + output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0); output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord); - output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0); + output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0); return output; } @@ -151,8 +151,8 @@ PixelOutput MetallicRoughnessNormalPS(PixelInput input) { PixelOutput output; - output.gPosition = float4(input.PositionWorld, 0.0); - output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0); + output.gPosition = float4(input.PositionWorld, 1.0); + output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0); output.gAlbedo = float4(AlbedoValue, 1.0); output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord); @@ -163,8 +163,8 @@ PixelOutput AlbedoMetallicRoughnessNormalMapPS(PixelInput input) { PixelOutput output; - output.gPosition = float4(input.PositionWorld, 0.0); - output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0); + output.gPosition = float4(input.PositionWorld, 1.0); + output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0); output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord); output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord); diff --git a/Effects/HLSL/DeferredPBR_PointLightEffect.fx b/Effects/HLSL/DeferredPBR_PointLightEffect.fx new file mode 100644 index 0000000..fba3c6e --- /dev/null +++ b/Effects/HLSL/DeferredPBR_PointLightEffect.fx @@ -0,0 +1,91 @@ +#include "Macros.fxh" //from FNA +#include "Lighting.fxh" + +DECLARE_TEXTURE(gPosition, 0); +DECLARE_TEXTURE(gAlbedo, 1); +DECLARE_TEXTURE(gNormal, 2); +DECLARE_TEXTURE(gMetallicRoughness, 3); + +BEGIN_CONSTANTS + + float3 EyePosition _ps(c0) _cb(c0); + + float3 PointLightPosition _ps(c1) _cb(c1); + float3 PointLightColor _ps(c2) _cb(c2); + +MATRIX_CONSTANTS + +END_CONSTANTS + +struct VertexInput +{ + float4 Position : POSITION; + float2 TexCoord : TEXCOORD; +}; + +struct PixelInput +{ + float4 Position : SV_POSITION; + float2 TexCoord : TEXCOORD0; +}; + +PixelInput main_vs(VertexInput input) +{ + PixelInput output; + + output.Position = input.Position; + output.TexCoord = input.TexCoord; + + return output; +} + +// Pixel Shader + +float4 ComputeColor( + float3 worldPosition, + float3 worldNormal, + float3 albedo, + float metallic, + float roughness +) { + float3 V = normalize(EyePosition - worldPosition); + float3 N = normalize(worldNormal); + + float3 F0 = float3(0.04, 0.04, 0.04); + F0 = lerp(F0, albedo, metallic); + + float3 lightDir = PointLightPosition - worldPosition; + float3 L = normalize(lightDir); + float distance = length(lightDir); + float attenuation = 1.0 / (distance * distance); + float3 radiance = PointLightColor * attenuation; + + float3 color = ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, 1.0); + + return float4(color, 1.0); +} + +float4 main_ps(PixelInput input) : SV_TARGET0 +{ + float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb; + float3 normal = SAMPLE_TEXTURE(gNormal, input.TexCoord).xyz; + float3 albedo = SAMPLE_TEXTURE(gAlbedo, input.TexCoord).rgb; + float2 metallicRoughness = SAMPLE_TEXTURE(gMetallicRoughness, input.TexCoord).rg; + + return ComputeColor( + worldPosition, + normal, + albedo, + metallicRoughness.r, + metallicRoughness.g + ); +} + +Technique DeferredPBR_Point +{ + Pass + { + VertexShader = compile vs_3_0 main_vs(); + PixelShader = compile ps_3_0 main_ps(); + } +} diff --git a/Effects/HLSL/Lighting.fxh b/Effects/HLSL/Lighting.fxh new file mode 100644 index 0000000..1cc6ae8 --- /dev/null +++ b/Effects/HLSL/Lighting.fxh @@ -0,0 +1,71 @@ +static const float PI = 3.141592653589793; + +float3 FresnelSchlick(float cosTheta, float3 F0) +{ + return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); +} + +float DistributionGGX(float3 N, float3 H, float roughness) +{ + float a = roughness * roughness; + float a2 = a * a; + float NdotH = max(dot(N, H), 0.0); + float NdotH2 = NdotH * NdotH; + + float num = a2; + float denom = (NdotH2 * (a2 - 1.0) + 1.0); + denom = PI * denom * denom; + + return num / denom; +} + +float GeometrySchlickGGX(float NdotV, float roughness) +{ + float r = (roughness + 1.0); + float k = (r * r) / 8.0; + + float num = NdotV; + float denom = NdotV * (1.0 - k) + k; + + return num / denom; +} + +float GeometrySmith(float3 N, float3 V, float3 L, float roughness) +{ + float NdotV = max(dot(N, V), 0.0); + float NdotL = max(dot(N, L), 0.0); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + + return ggx1 * ggx2; +} + +float3 ComputeLight( + float3 L, + float3 radiance, + float3 F0, + float3 V, + float3 N, + float3 albedo, + float metallic, + float roughness, + float shadow +) { + float3 H = normalize(V + L); + + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + float3 F = FresnelSchlick(max(dot(H, V), 0.0), F0); + + float3 numerator = NDF * G * F; + float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0); + float3 specular = numerator / max(denominator, 0.001); + + float3 kS = F; + float3 kD = float3(1.0, 1.0, 1.0) - kS; + + kD *= 1.0 - metallic; + + float NdotL = max(dot(N, L), 0.0); + return (kD * albedo / PI + specular) * radiance * NdotL * shadow; +} diff --git a/Kav.Core.csproj b/Kav.Core.csproj index 7957739..c7c437f 100644 --- a/Kav.Core.csproj +++ b/Kav.Core.csproj @@ -15,6 +15,15 @@ + + Kav.Resources.DeferredPBR_AmbientLightEffect.fxb + + + Kav.Resources.DeferredPBR_PointLightEffect.fxb + + + Kav.Resources.DeferredPBR_DirectionalLightEffect.fxb + Kav.Resources.DeferredPBR_GBufferEffect.fxb diff --git a/Kav.Framework.csproj b/Kav.Framework.csproj index 0557675..a98bb21 100644 --- a/Kav.Framework.csproj +++ b/Kav.Framework.csproj @@ -15,6 +15,15 @@ + + Kav.Resources.DeferredPBR_AmbientLightEffect.fxb + + + Kav.Resources.DeferredPBR_PointLightEffect.fxb + + + Kav.Resources.DeferredPBR_DirectionalLightEffect.fxb + Kav.Resources.DeferredPBR_GBufferEffect.fxb diff --git a/README.md b/README.md index 7a2bff0..bf40a9f 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Essential - [x] Directional lighting - [x] Directional shadow maps - [x] Cascading shadow maps +- [ ] Tone map shader - [ ] Frustum culling - [ ] PCF soft shadowing - [ ] Shadow-casting point lights diff --git a/Renderer.cs b/Renderer.cs index b99fe02..82a9a0f 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -8,26 +8,32 @@ namespace Kav { private const int MAX_SHADOW_CASCADES = 4; - private GraphicsDevice GraphicsDevice { get; } private int RenderDimensionsX { get; } private int RenderDimensionsY { get; } private VertexBuffer FullscreenTriangle { get; } private int NumShadowCascades { get; } + + private RenderTarget2D ColorRenderTarget { get; } + private RenderTarget2D DirectionalRenderTarget { get; } private RenderTarget2D[] ShadowRenderTargets { get; } private DeferredPBREffect DeferredPBREffect { get; } + private DeferredPBR_AmbientLightEffect DeferredAmbientLightEffect { get; } + private DeferredPBR_PointLightEffect DeferredPointLightEffect { get; } + private DeferredPBR_DirectionalLightEffect DeferredDirectionalLightEffect { get; } private SimpleDepthEffect SimpleDepthEffect { get; } private RenderTarget2D gPosition { get; } private RenderTarget2D gNormal { get; } private RenderTarget2D gAlbedo { get; } private RenderTarget2D gMetallicRoughness { get; } - private RenderTarget2D deferredRenderTarget { get; } private RenderTargetBinding[] GBuffer { get; } + private SpriteBatch SpriteBatch { get; } + public Renderer(GraphicsDevice graphicsDevice, int renderDimensionsX, int renderDimensionsY, int numShadowCascades) { GraphicsDevice = graphicsDevice; @@ -49,6 +55,28 @@ namespace Kav ); } + ColorRenderTarget = new RenderTarget2D( + graphicsDevice, + renderDimensionsX, + renderDimensionsY, + false, + SurfaceFormat.Color, + DepthFormat.None, + 0, + RenderTargetUsage.PreserveContents + ); + + DirectionalRenderTarget = new RenderTarget2D( + graphicsDevice, + renderDimensionsX, + renderDimensionsY, + false, + SurfaceFormat.Color, + DepthFormat.None, + 0, + RenderTargetUsage.PreserveContents + ); + gPosition = new RenderTarget2D( GraphicsDevice, renderDimensionsX, @@ -91,26 +119,21 @@ namespace Kav new RenderTargetBinding(gAlbedo), new RenderTargetBinding(gMetallicRoughness) }; - - deferredRenderTarget = new RenderTarget2D( - GraphicsDevice, - renderDimensionsX, - renderDimensionsY - ); SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice); DeferredPBREffect = new DeferredPBREffect(GraphicsDevice); + DeferredAmbientLightEffect = new DeferredPBR_AmbientLightEffect(GraphicsDevice); + DeferredPointLightEffect = new DeferredPBR_PointLightEffect(GraphicsDevice); + DeferredDirectionalLightEffect = new DeferredPBR_DirectionalLightEffect(GraphicsDevice); FullscreenTriangle = new VertexBuffer(GraphicsDevice, typeof(VertexPositionTexture), 3, BufferUsage.WriteOnly); FullscreenTriangle.SetData(new VertexPositionTexture[3] { - new VertexPositionTexture(new Vector3(-1, -1, 0), new Vector2(0, 0)), - new VertexPositionTexture(new Vector3(-1, 3, 0), new Vector2(0, -2)), - new VertexPositionTexture(new Vector3(3, -1, 0), new Vector2(2, 0)) + new VertexPositionTexture(new Vector3(-1, -3, 0), new Vector2(0, 2)), + new VertexPositionTexture(new Vector3(-1, 1, 0), new Vector2(0, 0)), + new VertexPositionTexture(new Vector3(3, 1, 0), new Vector2(2, 0)) }); - GraphicsDevice.SetRenderTarget(deferredRenderTarget); - graphicsDevice.Clear(Color.White); - GraphicsDevice.SetRenderTarget(null); + SpriteBatch = new SpriteBatch(graphicsDevice); } public void DeferredRender( @@ -119,7 +142,7 @@ namespace Kav IEnumerable pointLights, DirectionalLight directionalLight ) { - ShadowMapRender(camera, modelTransforms, directionalLight); + // g-buffer pass GraphicsDevice.SetRenderTargets(GBuffer); GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0); @@ -159,45 +182,54 @@ namespace Kav } } - GraphicsDevice.SetRenderTarget(null); - GraphicsDevice.Clear(Color.CornflowerBlue); + GraphicsDevice.SetRenderTarget(ColorRenderTarget); + GraphicsDevice.Clear(Color.Black); + GraphicsDevice.BlendState = BlendState.Additive; + GraphicsDevice.DepthStencilState = DepthStencilState.None; - DeferredPBREffect.GPosition = gPosition; - DeferredPBREffect.GAlbedo = gAlbedo; - DeferredPBREffect.GNormal = gNormal; - DeferredPBREffect.GMetallicRoughness = gMetallicRoughness; - - DeferredPBREffect.ShadowMapOne = ShadowRenderTargets[0]; - if (NumShadowCascades > 1) + DeferredAmbientLightEffect.GPosition = gPosition; + DeferredAmbientLightEffect.GAlbedo = gAlbedo; + + foreach (var pass in DeferredAmbientLightEffect.CurrentTechnique.Passes) { - DeferredPBREffect.ShadowMapTwo = ShadowRenderTargets[1]; - } - if (NumShadowCascades > 2) - { - DeferredPBREffect.ShadowMapThree = ShadowRenderTargets[2]; - } - if (NumShadowCascades > 3) - { - DeferredPBREffect.ShadowMapFour = ShadowRenderTargets[3]; + pass.Apply(); + GraphicsDevice.SetVertexBuffer(FullscreenTriangle); + GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1); } - DeferredPBREffect.ViewMatrix = camera.View; + DeferredPointLightEffect.EyePosition = camera.Position; - DeferredPBREffect.EyePosition = Matrix.Invert(camera.View).Translation; - - int i = 0; foreach (var pointLight in pointLights) { - if (i > DeferredPBREffect.MaxPointLights) { break; } - DeferredPBREffect.PointLights[i] = pointLight; - i++; + PointLightRender(pointLight); } - DeferredPBREffect.DirectionalLightColor = directionalLight.Color.ToVector3() * directionalLight.Intensity; - DeferredPBREffect.DirectionalLightDirection = directionalLight.Direction; + DirectionalLightRender(camera, modelTransforms, directionalLight); + // return; + // GraphicsDevice.SetRenderTarget(null); + // SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied); + // SpriteBatch.Draw(DirectionalRenderTarget, Vector2.Zero, Color.White); + // SpriteBatch.End(); + GraphicsDevice.SetRenderTarget(null); + GraphicsDevice.Clear(Color.Black); + SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque); + SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White); + SpriteBatch.End(); + } - foreach (EffectPass pass in DeferredPBREffect.CurrentTechnique.Passes) + private void PointLightRender(PointLight pointLight) + { + DeferredPointLightEffect.GPosition = gPosition; + DeferredPointLightEffect.GAlbedo = gAlbedo; + DeferredPointLightEffect.GNormal = gNormal; + DeferredPointLightEffect.GMetallicRoughness = gMetallicRoughness; + + DeferredPointLightEffect.PointLightPosition = pointLight.Position; + DeferredPointLightEffect.PointLightColor = + pointLight.Color.ToVector3() * pointLight.Intensity; + + foreach (var pass in DeferredPointLightEffect.CurrentTechnique.Passes) { pass.Apply(); GraphicsDevice.SetVertexBuffer(FullscreenTriangle); @@ -205,12 +237,12 @@ namespace Kav } } - public void ShadowMapRender( - PerspectiveCamera camera, - IEnumerable<(Model, Matrix)> modelTransforms, + private void DirectionalLightRender( + PerspectiveCamera camera, + IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight ) { - // render the individual shadow maps + // render the individual shadow cascades var previousFarPlane = camera.NearPlane; for (var i = 0; i < NumShadowCascades; i++) { @@ -227,10 +259,47 @@ namespace Kav farPlane ); + // TODO: This is tightly coupled to the effect and it sucks RenderShadowMap(shadowCamera, modelTransforms, directionalLight, i); previousFarPlane = farPlane; } + + DeferredDirectionalLightEffect.GPosition = gPosition; + DeferredDirectionalLightEffect.GAlbedo = gAlbedo; + DeferredDirectionalLightEffect.GNormal = gNormal; + DeferredDirectionalLightEffect.GMetallicRoughness = gMetallicRoughness; + + DeferredDirectionalLightEffect.ShadowMapOne = ShadowRenderTargets[0]; + if (NumShadowCascades > 1) + { + DeferredDirectionalLightEffect.ShadowMapTwo = ShadowRenderTargets[1]; + } + if (NumShadowCascades > 2) + { + DeferredDirectionalLightEffect.ShadowMapThree = ShadowRenderTargets[2]; + } + if (NumShadowCascades > 3) + { + DeferredDirectionalLightEffect.ShadowMapFour = ShadowRenderTargets[3]; + } + + DeferredDirectionalLightEffect.DirectionalLightDirection = directionalLight.Direction; + DeferredDirectionalLightEffect.DirectionalLightColor = + directionalLight.Color.ToVector3() * directionalLight.Intensity; + + DeferredDirectionalLightEffect.ViewMatrix = camera.View; + DeferredDirectionalLightEffect.EyePosition = Matrix.Invert(camera.View).Translation; + + GraphicsDevice.SetRenderTarget(ColorRenderTarget); + GraphicsDevice.BlendState = BlendState.Additive; + + foreach (EffectPass pass in DeferredDirectionalLightEffect.CurrentTechnique.Passes) + { + pass.Apply(); + GraphicsDevice.SetVertexBuffer(FullscreenTriangle); + GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1); + } } private void RenderShadowMap( @@ -277,22 +346,22 @@ namespace Kav if (shadowCascadeIndex == 0) { - DeferredPBREffect.LightSpaceMatrixOne = lightSpaceMatrix; + DeferredDirectionalLightEffect.LightSpaceMatrixOne = lightSpaceMatrix; } else if (shadowCascadeIndex == 1) { - DeferredPBREffect.LightSpaceMatrixTwo = lightSpaceMatrix; + DeferredDirectionalLightEffect.LightSpaceMatrixTwo = lightSpaceMatrix; } else if (shadowCascadeIndex == 2) { - DeferredPBREffect.LightSpaceMatrixThree = lightSpaceMatrix; + DeferredDirectionalLightEffect.LightSpaceMatrixThree = lightSpaceMatrix; } else if (shadowCascadeIndex == 3) { - DeferredPBREffect.LightSpaceMatrixFour = lightSpaceMatrix; + DeferredDirectionalLightEffect.LightSpaceMatrixFour = lightSpaceMatrix; } - DeferredPBREffect.CascadeFarPlanes[shadowCascadeIndex] = camera.FarPlane; + DeferredDirectionalLightEffect.CascadeFarPlanes[shadowCascadeIndex] = camera.FarPlane; foreach (var (model, transform) in modelTransforms) { diff --git a/Resources.cs b/Resources.cs index 83d4b04..9ac9358 100644 --- a/Resources.cs +++ b/Resources.cs @@ -4,6 +4,41 @@ namespace Kav { internal class Resources { + public static byte[] DeferredPBR_AmbientLightEffect + { + get + { + if (ambientLightEffect == null) + { + ambientLightEffect = GetResource("DeferredPBR_AmbientLightEffect"); + } + return ambientLightEffect; + } + } + public static byte[] DeferredPBR_PointLightEffect + { + get + { + if (pointLightEffect == null) + { + pointLightEffect = GetResource("DeferredPBR_PointLightEffect"); + } + return pointLightEffect; + } + } + + public static byte[] DeferredPBR_DirectionalLightEffect + { + get + { + if (directionalLightEffect == null) + { + directionalLightEffect = GetResource("DeferredPBR_DirectionalLightEffect"); + } + return directionalLightEffect; + } + } + public static byte[] DeferredPBR_GBufferEffect { get @@ -52,6 +87,9 @@ namespace Kav } } + private static byte[] ambientLightEffect; + private static byte[] pointLightEffect; + private static byte[] directionalLightEffect; private static byte[] gBufferEffect; private static byte[] deferredPBREffect; private static byte[] pbrEffect;