diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..40824ed --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.fxb filter=lfs diff=lfs merge=lfs -text diff --git a/Cameras/Camera.cs b/Cameras/Camera.cs index c0729ee..5462985 100644 --- a/Cameras/Camera.cs +++ b/Cameras/Camera.cs @@ -2,22 +2,32 @@ using Microsoft.Xna.Framework; namespace Kav { - public struct Camera + public struct PerspectiveCamera { - public Matrix Transform { get; } - public Matrix View - { - get - { - return Matrix.CreateLookAt(Transform.Translation, Transform.Translation + Transform.Forward, Transform.Up); - } - } + public Matrix View { get; } public Matrix Projection { get; } - public Camera(Matrix transform, Matrix projection) + public Vector3 Position { get; } + public Vector3 Forward { get; } + public Vector3 Up { get; } + + public float FieldOfView { get; } + public float AspectRatio { get; } + public float NearPlane { get; } + public float FarPlane { get; } + + public PerspectiveCamera(Vector3 position, Vector3 forward, Vector3 up, float fieldOfView, float aspectRatio, float nearPlane, float farPlane) { - Transform = transform; - Projection = projection; + Position = position; + Forward = forward; + Up = up; + View = Matrix.CreateLookAt(Position, Position + Forward, Up); + + FieldOfView = fieldOfView; + AspectRatio = aspectRatio; + NearPlane = nearPlane; + FarPlane = farPlane; + Projection = Matrix.CreatePerspectiveFieldOfView(FieldOfView, AspectRatio, NearPlane, FarPlane); } } } diff --git a/EffectInterfaces/DirectionalLightEffect.cs b/EffectInterfaces/DirectionalLightEffect.cs deleted file mode 100644 index 53ba894..0000000 --- a/EffectInterfaces/DirectionalLightEffect.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Kav -{ - public interface DirectionalLightEffect - { - int MaxDirectionalLights { get; } - DirectionalLightCollection DirectionalLights { get; } - } -} diff --git a/Effects/DeferredPBREffect.cs b/Effects/DeferredPBREffect.cs index 97c8615..181d1ec 100644 --- a/Effects/DeferredPBREffect.cs +++ b/Effects/DeferredPBREffect.cs @@ -10,15 +10,47 @@ namespace Kav EffectParameter gNormalParam; EffectParameter gMetallicRoughnessParam; + EffectParameter shadowMapOneParam; + EffectParameter shadowMapTwoParam; + EffectParameter shadowMapThreeParam; + EffectParameter shadowMapFourParam; + + EffectParameter lightSpaceMatrixOneParam; + EffectParameter lightSpaceMatrixTwoParam; + EffectParameter lightSpaceMatrixThreeParam; + EffectParameter lightSpaceMatrixFourParam; + + EffectParameter viewMatrixParam; + EffectParameter cascadeFarPlanesParam; + + EffectParameter directionalLightColorParam; + EffectParameter directionalLightDirectionParam; + EffectParameter eyePositionParam; + PointLightCollection pointLightCollection; - DirectionalLightCollection directionalLightCollection; 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 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 readonly float[] CascadeFarPlanes; + + public Vector3 DirectionalLightColor { get; set; } + public Vector3 DirectionalLightDirection { get; set; } + public Vector3 EyePosition { get; set; } public int MaxPointLights { get; } = 64; @@ -29,16 +61,10 @@ namespace Kav private set { pointLightCollection = value; } } - public int MaxDirectionalLights { get; } = 4; - - public DirectionalLightCollection DirectionalLights - { - get { return directionalLightCollection; } - private set { directionalLightCollection = value; } - } - public DeferredPBREffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBREffect) { + CascadeFarPlanes = new float[4]; + CacheEffectParameters(); pointLightCollection = new PointLightCollection( @@ -46,11 +72,6 @@ namespace Kav Parameters["PointLightColors"], MaxPointLights ); - - DirectionalLights = new DirectionalLightCollection( - Parameters["DirectionalLightDirections"], - Parameters["DirectionalLightColors"] - ); } protected DeferredPBREffect(DeferredPBREffect cloneSource) : base(cloneSource) @@ -72,16 +93,6 @@ namespace Kav { PointLights[i] = cloneSource.PointLights[i]; } - - DirectionalLights = new DirectionalLightCollection( - Parameters["DirectionalLightDirections"], - Parameters["DirectionalLightColors"] - ); - - for (int i = 0; i < MaxDirectionalLights; i++) - { - DirectionalLights[i] = cloneSource.DirectionalLights[i]; - } } public override Effect Clone() @@ -96,17 +107,49 @@ namespace Kav gNormalParam.SetValue(GNormal); gMetallicRoughnessParam.SetValue(GMetallicRoughness); + shadowMapOneParam.SetValue(ShadowMapOne); + shadowMapTwoParam.SetValue(ShadowMapTwo); + shadowMapThreeParam.SetValue(ShadowMapThree); + shadowMapFourParam.SetValue(ShadowMapFour); + + lightSpaceMatrixOneParam.SetValue(LightSpaceMatrixOne); + lightSpaceMatrixTwoParam.SetValue(LightSpaceMatrixTwo); + lightSpaceMatrixThreeParam.SetValue(LightSpaceMatrixThree); + lightSpaceMatrixFourParam.SetValue(LightSpaceMatrixFour); + + viewMatrixParam.SetValue(ViewMatrix); + cascadeFarPlanesParam.SetValue(CascadeFarPlanes); + + directionalLightColorParam.SetValue(DirectionalLightColor); + directionalLightDirectionParam.SetValue(DirectionalLightDirection); + eyePositionParam.SetValue(EyePosition); } void CacheEffectParameters() { - gPositionParam = Parameters["gPosition"]; - gAlbedoParam = Parameters["gAlbedo"]; - gNormalParam = Parameters["gNormal"]; - gMetallicRoughnessParam = Parameters["gMetallicRoughness"]; + gPositionParam = Parameters["gPosition"]; + gAlbedoParam = Parameters["gAlbedo"]; + gNormalParam = Parameters["gNormal"]; + gMetallicRoughnessParam = Parameters["gMetallicRoughness"]; - eyePositionParam = Parameters["EyePosition"]; + shadowMapOneParam = Parameters["shadowMapOne"]; + shadowMapTwoParam = Parameters["shadowMapTwo"]; + shadowMapThreeParam = Parameters["shadowMapThree"]; + shadowMapFourParam = Parameters["shadowMapFour"]; + + lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"]; + lightSpaceMatrixTwoParam = Parameters["LightSpaceMatrixTwo"]; + lightSpaceMatrixThreeParam = Parameters["LightSpaceMatrixThree"]; + lightSpaceMatrixFourParam = Parameters["LightSpaceMatrixFour"]; + + viewMatrixParam = Parameters["ViewMatrix"]; + cascadeFarPlanesParam = Parameters["CascadeFarPlanes"]; + + directionalLightDirectionParam = Parameters["DirectionalLightDirection"]; + directionalLightColorParam = Parameters["DirectionalLightColor"]; + + eyePositionParam = Parameters["EyePosition"]; } } } 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..5e1f919 --- /dev/null +++ b/Effects/DeferredPBR_DirectionalLightEffect.cs @@ -0,0 +1,161 @@ +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 shadowMapSizeParam; + + 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 int ShadowMapSize { get; set; } + + 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]; + } + + ShadowMapSize = cloneSource.ShadowMapSize; + + 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); + shadowMapSizeParam.SetValue(ShadowMapSize); + + 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"]; + + shadowMapSizeParam = Parameters["ShadowMapSize"]; + + 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/DirectionalLightCollection.cs b/Effects/DirectionalLightCollection.cs deleted file mode 100644 index 10bcbda..0000000 --- a/Effects/DirectionalLightCollection.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; - -namespace Kav -{ - public class DirectionalLightCollection - { - private readonly Vector3[] directions = new Vector3[4]; - private readonly Vector3[] colors = new Vector3[4]; - private readonly float[] intensities = new float[4]; - - readonly EffectParameter lightDirectionsParam; - readonly EffectParameter lightColorsParam; - - public DirectionalLightCollection(EffectParameter lightDirectionsParam, EffectParameter lightColorsParam) - { - this.lightDirectionsParam = lightDirectionsParam; - this.lightColorsParam = lightColorsParam; - } - - public DirectionalLight this[int i] - { - get - { - var color = colors[i] / intensities[i]; - return new DirectionalLight( - directions[i], - new Color( - color.X, - color.Y, - color.Z, - 1f - ), - intensities[i] - ); - } - set - { - directions[i] = value.Direction; - colors[i] = value.Color.ToVector3() * value.Intensity; - intensities[i] = value.Intensity; - lightDirectionsParam.SetValue(directions); - lightColorsParam.SetValue(colors); - } - } - } -} diff --git a/Effects/FXB/DeferredPBREffect.fxb b/Effects/FXB/DeferredPBREffect.fxb index 10298d5..1d4d26c 100644 Binary files a/Effects/FXB/DeferredPBREffect.fxb and b/Effects/FXB/DeferredPBREffect.fxb differ diff --git a/Effects/FXB/DeferredPBR_AmbientLightEffect.fxb b/Effects/FXB/DeferredPBR_AmbientLightEffect.fxb new file mode 100644 index 0000000..62de278 --- /dev/null +++ b/Effects/FXB/DeferredPBR_AmbientLightEffect.fxb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0ea0cb071a1e53fec5058fae9919d376e408d33cd3de9485d42e1ebcbee85546 +size 1016 diff --git a/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb b/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb new file mode 100644 index 0000000..9f6cf27 --- /dev/null +++ b/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be44f16057328acf2bf9d2e4ff1e2d448df720711a2900daa39f5fc8c8732711 +size 21692 diff --git a/Effects/FXB/DeferredPBR_GBufferEffect.fxb b/Effects/FXB/DeferredPBR_GBufferEffect.fxb index 9b2d684..3aff2ad 100644 Binary files a/Effects/FXB/DeferredPBR_GBufferEffect.fxb and b/Effects/FXB/DeferredPBR_GBufferEffect.fxb differ diff --git a/Effects/FXB/DeferredPBR_PointLightEffect.fxb b/Effects/FXB/DeferredPBR_PointLightEffect.fxb new file mode 100644 index 0000000..398f66b --- /dev/null +++ b/Effects/FXB/DeferredPBR_PointLightEffect.fxb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e51395e0f0dd1e1b86272dde1e4fb5aea1fdf9b86399416015ad33cfb7260691 +size 3108 diff --git a/Effects/FXB/GBufferEffect.fxb b/Effects/FXB/GBufferEffect.fxb index 9b2d684..ad31fdf 100644 Binary files a/Effects/FXB/GBufferEffect.fxb and b/Effects/FXB/GBufferEffect.fxb differ diff --git a/Effects/FXB/PBREffect.fxb b/Effects/FXB/PBREffect.fxb index 1747aa4..2214837 100644 Binary files a/Effects/FXB/PBREffect.fxb and b/Effects/FXB/PBREffect.fxb differ diff --git a/Effects/FXB/SimpleDepthEffect.fxb b/Effects/FXB/SimpleDepthEffect.fxb index e2baafe..c60bf15 100644 Binary files a/Effects/FXB/SimpleDepthEffect.fxb and b/Effects/FXB/SimpleDepthEffect.fxb differ diff --git a/Effects/FXB/ToneMapEffect.fxb b/Effects/FXB/ToneMapEffect.fxb new file mode 100644 index 0000000..5085f08 --- /dev/null +++ b/Effects/FXB/ToneMapEffect.fxb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:efddb6cc98515293e65c5033e376ff01f38e3589c329e1b52901437df82ea93e +size 584 diff --git a/Effects/HLSL/DeferredPBREffect.fx b/Effects/HLSL/DeferredPBREffect.fx index a49f27a..14a58c2 100644 --- a/Effects/HLSL/DeferredPBREffect.fx +++ b/Effects/HLSL/DeferredPBREffect.fx @@ -2,12 +2,16 @@ static const float PI = 3.141592653589793; static const int MAX_POINT_LIGHTS = 64; -static const int MAX_DIRECTIONAL_LIGHTS = 4; +static const int NUM_SHADOW_CASCADES = 4; DECLARE_TEXTURE(gPosition, 0); DECLARE_TEXTURE(gAlbedo, 1); DECLARE_TEXTURE(gNormal, 2); DECLARE_TEXTURE(gMetallicRoughness, 3); +DECLARE_TEXTURE(shadowMapOne, 4); +DECLARE_TEXTURE(shadowMapTwo, 5); +DECLARE_TEXTURE(shadowMapThree, 6); +DECLARE_TEXTURE(shadowMapFour, 7); BEGIN_CONSTANTS @@ -16,19 +20,45 @@ BEGIN_CONSTANTS float3 PointLightPositions[MAX_POINT_LIGHTS] _ps(c1) _cb(c1); float3 PointLightColors[MAX_POINT_LIGHTS] _ps(c65) _cb(c65); - float3 DirectionalLightDirections[MAX_DIRECTIONAL_LIGHTS] _ps(c129) _cb(c129); - float3 DirectionalLightColors[MAX_DIRECTIONAL_LIGHTS] _ps(c133) _cb(c133); + float3 DirectionalLightDirection _ps(c129) _cb(c129); + float3 DirectionalLightColor _ps(c130) _cb(c130); + + float CascadeFarPlanes[NUM_SHADOW_CASCADES] _ps(c131) _cb(c131); MATRIX_CONSTANTS + float4x4 LightSpaceMatrixOne _ps(c135) _cb(c135); + float4x4 LightSpaceMatrixTwo _ps(c139) _cb(c139); + float4x4 LightSpaceMatrixThree _ps(c143) _cb(c143); + float4x4 LightSpaceMatrixFour _ps(c147) _cb(c147); + + // used to select shadow cascade + float4x4 ViewMatrix _ps(c151) _cb(c151); + 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 float3 FresnelSchlick(float cosTheta, float3 F0) @@ -71,17 +101,119 @@ float GeometrySmith(float3 N, float3 V, float3 L, float roughness) return ggx1 * ggx2; } +float ComputeShadow(float3 positionWorldSpace, float3 N, float L) +{ + float bias = 0.005 * tan(acos(dot(N, L))); + bias = clamp(bias, 0, 0.01); + + float4 positionCameraSpace = mul(float4(positionWorldSpace, 1.0), ViewMatrix); + + int shadowCascadeIndex = 0; // 0 is closest + for (int i = 0; i < NUM_SHADOW_CASCADES; i++) + { + if (abs(positionCameraSpace.z) < CascadeFarPlanes[i]) + { + shadowCascadeIndex = i; + break; + } + } + + float4x4 lightSpaceMatrix; + + if (shadowCascadeIndex == 0) + { + lightSpaceMatrix = LightSpaceMatrixOne; + } + else if (shadowCascadeIndex == 1) + { + lightSpaceMatrix = LightSpaceMatrixTwo; + } + else if (shadowCascadeIndex == 2) + { + lightSpaceMatrix = LightSpaceMatrixThree; + } + else + { + lightSpaceMatrix = LightSpaceMatrixFour; + } + + float4 positionLightSpace = mul(float4(positionWorldSpace, 1.0), lightSpaceMatrix); + + // maps to [-1, 1] + float3 projectionCoords = positionLightSpace.xyz / positionLightSpace.w; + + // maps to [0, 1] + projectionCoords.x = (projectionCoords.x * 0.5) + 0.5; + projectionCoords.y = (projectionCoords.y * 0.5) + 0.5; + projectionCoords.y *= -1; + // in XNA clip z is 0 to 1 already + + float inc = 1.0 / 1024.0; + + float shadowFactor = 0; + for (int row = -1; row <= 1; row++) + { + for (int col = -1; col <= 1; col++) + { + float closestDepth; + if (shadowCascadeIndex == 0) + { + closestDepth = SAMPLE_TEXTURE(shadowMapOne, projectionCoords.xy + float2(row, col) * inc).r; + } + else if (shadowCascadeIndex == 1) + { + closestDepth = SAMPLE_TEXTURE(shadowMapTwo, projectionCoords.xy + float2(row, col) * inc).r; + } + else if (shadowCascadeIndex == 2) + { + closestDepth = SAMPLE_TEXTURE(shadowMapThree, projectionCoords.xy + float2(row, col) * inc).r; + } + else + { + closestDepth = SAMPLE_TEXTURE(shadowMapFour, projectionCoords.xy + float2(row, col) * inc).r; + } + shadowFactor += projectionCoords.z - bias > closestDepth ? 1.0 : 0.0; + } + } + + shadowFactor /= 9.0; + + if (projectionCoords.z > 1.0) + { + shadowFactor = 1.0; + } + + return shadowFactor; + + + // float currentDepth = projectionCoords.z; + + // if (currentDepth > 1.0) + // { + // return 0.0; + // } + + // if (currentDepth - bias > closestDepth) + // { + // return 1.0; + // } + // else + // { + // return 0.0; + // } +} + float3 ComputeLight( - float3 lightDir, + float3 L, float3 radiance, float3 F0, float3 V, float3 N, float3 albedo, float metallic, - float roughness + float roughness, + float shadow ) { - float3 L = normalize(lightDir); float3 H = normalize(V + L); float NDF = DistributionGGX(N, H, roughness); @@ -98,7 +230,7 @@ float3 ComputeLight( kD *= 1.0 - metallic; float NdotL = max(dot(N, L), 0.0); - return (kD * albedo / PI + specular) * radiance * NdotL; + return (kD * albedo / PI + specular) * radiance * NdotL * shadow; } float4 ComputeColor( @@ -120,21 +252,20 @@ float4 ComputeColor( for (int i = 0; i < MAX_POINT_LIGHTS; i++) { float3 lightDir = PointLightPositions[i] - worldPosition; + float3 L = normalize(lightDir); float distance = length(lightDir); float attenuation = 1.0 / (distance * distance); float3 radiance = PointLightColors[i] * attenuation; - Lo += ComputeLight(lightDir, radiance, F0, V, N, albedo, metallic, roughness); + Lo += ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, 1.0); } // directional light - for (int i = 0; i < MAX_DIRECTIONAL_LIGHTS; i++) - { - float3 lightDir = DirectionalLightDirections[i]; - float3 radiance = DirectionalLightColors[i]; + float3 L = normalize(DirectionalLightDirection); + float3 radiance = DirectionalLightColor; - Lo += ComputeLight(lightDir, radiance, F0, V, N, albedo, metallic, roughness); - } + float shadow = ComputeShadow(worldPosition, N, L); + Lo += ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, (1.0 - shadow)); float3 ambient = float3(0.03, 0.03, 0.03) * albedo; // * AO; float3 color = ambient + Lo; @@ -148,13 +279,13 @@ float4 ComputeColor( float4 main_ps(PixelInput input) : SV_TARGET0 { - float3 fragPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb; + 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( - fragPosition, + worldPosition, normal, albedo, metallicRoughness.r, @@ -166,6 +297,7 @@ Technique DeferredPBR { Pass { + VertexShader = compile vs_3_0 main_vs(); PixelShader = compile ps_3_0 main_ps(); } } diff --git a/Effects/HLSL/DeferredPBR_AmbientLightEffect.fx b/Effects/HLSL/DeferredPBR_AmbientLightEffect.fx new file mode 100644 index 0000000..4144c8d --- /dev/null +++ b/Effects/HLSL/DeferredPBR_AmbientLightEffect.fx @@ -0,0 +1,54 @@ +#include "Macros.fxh" // from FNA + +DECLARE_TEXTURE(gPosition, 0); +DECLARE_TEXTURE(gAlbedo, 1); + +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; +} + +float4 ComputeColor( + float3 worldPosition, + float3 albedo +) { + float3 color = float3(0.03, 0.03, 0.03) * albedo; + return float4(color, 1.0); +} + +float4 main_ps(PixelInput input) : SV_TARGET0 +{ + float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb; + float3 albedo = SAMPLE_TEXTURE(gAlbedo, input.TexCoord).rgb; + + return ComputeColor( + worldPosition, + albedo + ); +} + +Technique DeferredPBR_Ambient +{ + Pass + { + VertexShader = compile vs_3_0 main_vs(); + PixelShader = compile ps_3_0 main_ps(); + } +} diff --git a/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx b/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx new file mode 100644 index 0000000..0982521 --- /dev/null +++ b/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx @@ -0,0 +1,252 @@ +#include "Macros.fxh" //from FNA +#include "Lighting.fxh" + +static const int NUM_SHADOW_CASCADES = 4; + +DECLARE_TEXTURE(gPosition, 0); +DECLARE_TEXTURE(gAlbedo, 1); +DECLARE_TEXTURE(gNormal, 2); +DECLARE_TEXTURE(gMetallicRoughness, 3); +DECLARE_TEXTURE(shadowMapOne, 4); +DECLARE_TEXTURE(shadowMapTwo, 5); +DECLARE_TEXTURE(shadowMapThree, 6); +DECLARE_TEXTURE(shadowMapFour, 7); + +BEGIN_CONSTANTS + + float3 EyePosition _ps(c0) _cb(c0); + + float3 DirectionalLightDirection _ps(c1) _cb(c1); + float3 DirectionalLightColor _ps(c2) _cb(c2); + + float CascadeFarPlanes[NUM_SHADOW_CASCADES] _ps(c3) _cb(c3); + + float ShadowMapSize _ps(c7) _cb(c7); + +MATRIX_CONSTANTS + + float4x4 LightSpaceMatrixOne _ps(c8) _cb(c8); + float4x4 LightSpaceMatrixTwo _ps(c12) _cb(c12); + float4x4 LightSpaceMatrixThree _ps(c16) _cb(c16); + float4x4 LightSpaceMatrixFour _ps(c20) _cb(c20); + + // used to select shadow cascade + float4x4 ViewMatrix _ps(c24) _cb(c24); + +END_CONSTANTS + +static float2 poissonDisk[16] = +{ + float2( -0.94201624, -0.39906216 ), + float2( 0.94558609, -0.76890725 ), + float2( -0.094184101, -0.92938870 ), + float2( 0.34495938, 0.29387760 ), + float2( -0.91588581, 0.45771432 ), + float2( -0.81544232, -0.87912464 ), + float2( -0.38277543, 0.27676845 ), + float2( 0.97484398, 0.75648379 ), + float2( 0.44323325, -0.97511554 ), + float2( 0.53742981, -0.47373420 ), + float2( -0.26496911, -0.41893023 ), + float2( 0.79197514, 0.19090188 ), + float2( -0.24188840, 0.99706507 ), + float2( -0.81409955, 0.91437590 ), + float2( 0.19984126, 0.78641367 ), + float2( 0.14383161, -0.14100790 ) +}; + +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 + +// Returns a random number based on a vec3 and an int. +float random(float3 seed, int i){ + float4 seed4 = float4(seed, i); + float dot_product = dot(seed4, float4(12.9898,78.233,45.164,94.673)); + return frac(sin(dot_product) * 43758.5453); +} + +float PoissonCoord(sampler shadowMap, float3 worldPosition, float2 texCoord, float fragmentDepth, float bias) +{ + float visibility = 1.0; + + for (int i = 0; i < 16; i++) + { + int index = int(16.0 * random(floor(worldPosition * 1000.0), i)) % 16; + + if (tex2D(shadowMap, texCoord + poissonDisk[index] / 1024.0).r < fragmentDepth - bias) + { + visibility -= 0.05; + } + } + + return visibility; +} + +float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L) +{ + float bias = 0.005 * tan(acos(dot(N, L))); + bias = clamp(bias, 0, 0.01); + + float4 positionCameraSpace = mul(float4(positionWorldSpace, 1.0), ViewMatrix); + + int shadowCascadeIndex = 0; // 0 is closest + for (int i = 0; i < NUM_SHADOW_CASCADES; i++) + { + if (abs(positionCameraSpace.z) < CascadeFarPlanes[i]) + { + shadowCascadeIndex = i; + break; + } + } + + float4x4 lightSpaceMatrix; + + if (shadowCascadeIndex == 0) + { + lightSpaceMatrix = LightSpaceMatrixOne; + } + else if (shadowCascadeIndex == 1) + { + lightSpaceMatrix = LightSpaceMatrixTwo; + } + else if (shadowCascadeIndex == 2) + { + lightSpaceMatrix = LightSpaceMatrixThree; + } + else + { + lightSpaceMatrix = LightSpaceMatrixFour; + } + + float4 positionLightSpace = mul(float4(positionWorldSpace, 1.0), lightSpaceMatrix); + + // maps to [-1, 1] + float3 projectionCoords = positionLightSpace.xyz / positionLightSpace.w; + + // maps to [0, 1] + projectionCoords.x = (projectionCoords.x * 0.5) + 0.5; + projectionCoords.y = (projectionCoords.y * 0.5) + 0.5; + projectionCoords.y *= -1; + // in XNA clip z is 0 to 1 already + + if (projectionCoords.z > 1.0) + { + return 1.0; + } + + float inc = 1.0 / ShadowMapSize; // TODO: shadow map size uniform + + // PCF + Poisson soft shadows + float visibility = 0.0; + // for (int row = -1; row <= 1; row++) + // { + // for (int col = -1; col <= 1; col++) + // { + // if (shadowCascadeIndex == 0) + // { + // visibility += PoissonCoord(SAMPLER(shadowMapOne), positionWorldSpace, projectionCoords.xy + float2(row, col) * inc, projectionCoords.z, bias); + // } + // else if (shadowCascadeIndex == 1) + // { + // visibility += PoissonCoord(SAMPLER(shadowMapTwo), positionWorldSpace, projectionCoords.xy + float2(row, col) * inc, projectionCoords.z, bias); + // } + // else if (shadowCascadeIndex == 2) + // { + // visibility += PoissonCoord(SAMPLER(shadowMapThree), positionWorldSpace, projectionCoords.xy + float2(row, col) * inc, projectionCoords.z, bias); + // } + // else + // { + // visibility += PoissonCoord(SAMPLER(shadowMapFour), positionWorldSpace, projectionCoords.xy + float2(row, col) * inc, projectionCoords.z, bias); + // } + // } + // } + + // visibility /= 9.0; + + if (shadowCascadeIndex == 0) + { + visibility = PoissonCoord(SAMPLER(shadowMapOne), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias); + } + else if (shadowCascadeIndex == 1) + { + visibility = PoissonCoord(SAMPLER(shadowMapTwo), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias); + } + else if (shadowCascadeIndex == 2) + { + visibility = PoissonCoord(SAMPLER(shadowMapThree), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias); + } + else + { + visibility = PoissonCoord(SAMPLER(shadowMapFour), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias); + } + + return visibility; +} + +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, 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..e57b391 --- /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 visibility +) { + 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 * visibility; +} diff --git a/Effects/HLSL/Macros.fxh b/Effects/HLSL/Macros.fxh index 91d1702..c0aaaac 100644 --- a/Effects/HLSL/Macros.fxh +++ b/Effects/HLSL/Macros.fxh @@ -54,4 +54,5 @@ #define SAMPLE_TEXTURE(Name, texCoord) tex2D(Name##Sampler, texCoord) #define SAMPLE_CUBEMAP(Name, texCoord) texCUBE(Name##Sampler, texCoord) #define SAMPLE_CUBEMAP_LOD(Name, texCoord) texCUBElod(Name##Sampler, texCoord) +#define SAMPLER(Name) Name##Sampler #endif diff --git a/Effects/HLSL/SimpleDepthEffect.fx b/Effects/HLSL/SimpleDepthEffect.fx index 8b19a4e..3598eb5 100644 --- a/Effects/HLSL/SimpleDepthEffect.fx +++ b/Effects/HLSL/SimpleDepthEffect.fx @@ -16,21 +16,29 @@ struct VertexShaderInput struct VertexShaderOutput { float4 Position : SV_Position; + float Depth : TEXCOORD0; }; VertexShaderOutput main_vs(VertexShaderInput input) { VertexShaderOutput output; - output.Position = mul(float4(input.Position.xyz, 1.0), ModelViewProjection); + output.Position = mul(input.Position, ModelViewProjection); + output.Depth = output.Position.z; return output; } +float4 main_ps(VertexShaderOutput input) : SV_TARGET0 +{ + return float4(input.Depth, 0.0, 0.0, 0.0); +} + Technique SimpleDepth { Pass { VertexShader = compile vs_3_0 main_vs(); + PixelShader = compile ps_3_0 main_ps(); } } diff --git a/Effects/HLSL/ToneMapEffect.fx b/Effects/HLSL/ToneMapEffect.fx new file mode 100644 index 0000000..281b714 --- /dev/null +++ b/Effects/HLSL/ToneMapEffect.fx @@ -0,0 +1,20 @@ +sampler TextureSampler : register(s0); + +float4 main_ps(float2 texCoord : TEXCOORD0) : COLOR0 +{ + float3 color = tex2D(TextureSampler, texCoord).xyz; + + color = color / (color + float3(1.0, 1.0, 1.0)); + float exposureConstant = 1.0 / 2.2; + color = pow(color, float3(exposureConstant, exposureConstant, exposureConstant)); + + return float4(color, 1.0); +} + +Technique DeferredPBR +{ + Pass + { + PixelShader = compile ps_3_0 main_ps(); + } +} diff --git a/Effects/PBREffect.cs b/Effects/PBREffect.cs index db04a25..2df74cd 100644 --- a/Effects/PBREffect.cs +++ b/Effects/PBREffect.cs @@ -3,7 +3,7 @@ using Microsoft.Xna.Framework.Graphics; namespace Kav { - public class PBREffect : Effect, TransformEffect, PointLightEffect, DirectionalLightEffect + public class PBREffect : Effect, TransformEffect, PointLightEffect { EffectParameter worldParam; EffectParameter worldViewProjectionParam; @@ -31,7 +31,6 @@ namespace Kav Matrix view = Matrix.Identity; Matrix projection = Matrix.Identity; PointLightCollection pointLightCollection; - DirectionalLightCollection directionalLightCollection; Vector3 albedo; float metallic; @@ -84,14 +83,6 @@ namespace Kav private set { pointLightCollection = value; } } - public int MaxDirectionalLights { get; } = 4; - - public DirectionalLightCollection DirectionalLights - { - get { return directionalLightCollection; } - private set { directionalLightCollection = value; } - } - public Vector3 Albedo { get { return albedo; } @@ -204,11 +195,6 @@ namespace Kav Parameters["PositionLightColors"], MaxPointLights ); - - directionalLightCollection = new DirectionalLightCollection( - Parameters["LightDirections"], - Parameters["DirectionLightColors"] - ); } protected PBREffect(PBREffect cloneSource) : base(cloneSource) @@ -230,16 +216,6 @@ namespace Kav PointLights[i] = cloneSource.PointLights[i]; } - DirectionalLights = new DirectionalLightCollection( - Parameters["LightDirections"], - Parameters["DirectionLightColors"] - ); - - for (int i = 0; i < MaxDirectionalLights; i++) - { - DirectionalLights[i] = cloneSource.DirectionalLights[i]; - } - AlbedoTexture = cloneSource.AlbedoTexture; NormalTexture = cloneSource.NormalTexture; EmissionTexture = cloneSource.EmissionTexture; diff --git a/Effects/SimpleDepthEffect.cs b/Effects/SimpleDepthEffect.cs index f68f513..1854e3c 100644 --- a/Effects/SimpleDepthEffect.cs +++ b/Effects/SimpleDepthEffect.cs @@ -42,6 +42,7 @@ namespace Kav dirtyFlags |= EffectDirtyFlags.WorldViewProj; } } + public SimpleDepthEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.SimpleDepthEffect) { CacheEffectParameters(); @@ -51,8 +52,8 @@ namespace Kav { if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0) { - Matrix.Multiply(ref model, ref view, out Matrix modelView); - Matrix.Multiply(ref modelView, ref projection, out Matrix worldViewProj); + Matrix.Multiply(ref view, ref projection, out Matrix viewProjection); + Matrix.Multiply(ref model, ref viewProjection, out Matrix worldViewProj); modelViewProjectionParam.SetValue(worldViewProj); diff --git a/Kav.Core.csproj b/Kav.Core.csproj index 7957739..327d7bb 100644 --- a/Kav.Core.csproj +++ b/Kav.Core.csproj @@ -15,9 +15,21 @@ + + Kav.Resources.DeferredPBR_AmbientLightEffect.fxb + + + Kav.Resources.DeferredPBR_PointLightEffect.fxb + + + Kav.Resources.DeferredPBR_DirectionalLightEffect.fxb + Kav.Resources.DeferredPBR_GBufferEffect.fxb + + Kav.Resources.ToneMapEffect.fxb + Kav.Resources.DeferredPBREffect.fxb diff --git a/Kav.Framework.csproj b/Kav.Framework.csproj index 0557675..2b2dd4f 100644 --- a/Kav.Framework.csproj +++ b/Kav.Framework.csproj @@ -15,9 +15,21 @@ + + Kav.Resources.DeferredPBR_AmbientLightEffect.fxb + + + Kav.Resources.DeferredPBR_PointLightEffect.fxb + + + Kav.Resources.DeferredPBR_DirectionalLightEffect.fxb + Kav.Resources.DeferredPBR_GBufferEffect.fxb + + Kav.Resources.ToneMapEffect.fxb + Kav.Resources.DeferredPBREffect.fxb diff --git a/Lights/DirectionalLight.cs b/Lights/DirectionalLight.cs index 31ca4fd..a22ab33 100644 --- a/Lights/DirectionalLight.cs +++ b/Lights/DirectionalLight.cs @@ -12,7 +12,7 @@ namespace Kav { get { - return Matrix.CreateLookAt(-Direction * 100f, Vector3.Zero, Vector3.Up); + return Matrix.CreateLookAt(Direction * 100f, Vector3.Zero, Vector3.Up); } } diff --git a/README.md b/README.md new file mode 100644 index 0000000..8f3190b --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# Kav + +A 3D renderer built on top of FNA. + +## Roadmap + +Essential + +- [x] PBR shading +- [x] Deferred rendering +- [x] Point lighting +- [x] Directional lighting +- [x] Directional shadow maps +- [x] Cascading shadow maps +- [x] Tone map shader +- [x] Poisson soft shadowing +- [ ] Frustum culling +- [ ] Shadow-casting point lights +- [ ] Parabolic lights +- [ ] Skyboxes +- [ ] Screen-space reflection + +Nice-To-Haves + +- [ ] Anti-aliasing +- [ ] Image-based lighting +- [ ] Volumetric lighting +- [ ] Volumetric smoke +- [ ] diff --git a/Renderer.cs b/Renderer.cs index c53f855..63b34e5 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -6,38 +6,84 @@ namespace Kav { public class Renderer { + private const int MAX_SHADOW_CASCADES = 4; + private int ShadowMapSize { get; } + private GraphicsDevice GraphicsDevice { get; } private int RenderDimensionsX { get; } private int RenderDimensionsY { get; } - private RenderTarget2D DepthRenderTarget { 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 Effect ToneMapEffect { 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 DeferredPBREffect DeferredPBREffect { get; } - private SpriteBatch SpriteBatch { get; } - public Renderer(GraphicsDevice graphicsDevice, int renderDimensionsX, int renderDimensionsY) - { + public Renderer( + GraphicsDevice graphicsDevice, + int renderDimensionsX, + int renderDimensionsY, + int numShadowCascades, + int shadowMapSize + ) { GraphicsDevice = graphicsDevice; RenderDimensionsX = renderDimensionsX; RenderDimensionsY = renderDimensionsY; - DepthRenderTarget = new RenderTarget2D( - GraphicsDevice, + ShadowMapSize = shadowMapSize; + + NumShadowCascades = (int)MathHelper.Clamp(numShadowCascades, 1, MAX_SHADOW_CASCADES); + ShadowRenderTargets = new RenderTarget2D[numShadowCascades]; + + for (var i = 0; i < numShadowCascades; i++) + { + ShadowRenderTargets[i] = new RenderTarget2D( + GraphicsDevice, + ShadowMapSize, + ShadowMapSize, + false, + SurfaceFormat.Single, + DepthFormat.Depth24 + ); + } + + ColorRenderTarget = new RenderTarget2D( + graphicsDevice, renderDimensionsX, renderDimensionsY, false, - SurfaceFormat.HalfSingle, // unused - DepthFormat.Depth24 + SurfaceFormat.Color, + DepthFormat.None, + 0, + RenderTargetUsage.PreserveContents + ); + + DirectionalRenderTarget = new RenderTarget2D( + graphicsDevice, + renderDimensionsX, + renderDimensionsY, + false, + SurfaceFormat.Color, + DepthFormat.None, + 0, + RenderTargetUsage.PreserveContents ); gPosition = new RenderTarget2D( @@ -82,29 +128,33 @@ 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); + DeferredDirectionalLightEffect.ShadowMapSize = ShadowMapSize; + ToneMapEffect = new Effect(graphicsDevice, Resources.ToneMapEffect); - SpriteBatch = new SpriteBatch(GraphicsDevice); + FullscreenTriangle = new VertexBuffer(GraphicsDevice, typeof(VertexPositionTexture), 3, BufferUsage.WriteOnly); + FullscreenTriangle.SetData(new VertexPositionTexture[3] { + 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( - Camera camera, + PerspectiveCamera camera, IEnumerable<(Model, Matrix)> modelTransforms, IEnumerable pointLights, - IEnumerable directionalLights + DirectionalLight directionalLight ) { + // g-buffer pass + GraphicsDevice.SetRenderTargets(GBuffer); GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0); GraphicsDevice.DepthStencilState = DepthStencilState.Default; @@ -143,53 +193,186 @@ 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.EyePosition = Matrix.Invert(camera.View).Translation; + DeferredAmbientLightEffect.GPosition = gPosition; + DeferredAmbientLightEffect.GAlbedo = gAlbedo; + + foreach (var pass in DeferredAmbientLightEffect.CurrentTechnique.Passes) + { + pass.Apply(); + GraphicsDevice.SetVertexBuffer(FullscreenTriangle); + GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1); + } + + DeferredPointLightEffect.EyePosition = camera.Position; - int i = 0; foreach (var pointLight in pointLights) { - if (i > DeferredPBREffect.MaxPointLights) { break; } - DeferredPBREffect.PointLights[i] = pointLight; - i++; + PointLightRender(pointLight); } - i = 0; - foreach (var directionalLight in directionalLights) - { - if (i > DeferredPBREffect.MaxDirectionalLights) { break; } - DeferredPBREffect.DirectionalLights[i] = directionalLight; - i++; - } + DirectionalLightRender(camera, modelTransforms, directionalLight); + // return; + // GraphicsDevice.SetRenderTarget(null); + // SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied); + // SpriteBatch.Draw(DirectionalRenderTarget, Vector2.Zero, Color.White); + // SpriteBatch.End(); - SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, null, null, DeferredPBREffect); - SpriteBatch.Draw(deferredRenderTarget, Vector2.Zero, Color.White); + GraphicsDevice.SetRenderTarget(null); + GraphicsDevice.Clear(Color.Black); + SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, null, null, null, ToneMapEffect); + SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White); SpriteBatch.End(); } - public void Render( - Camera camera, - IEnumerable<(Model, Matrix)> modelTransforms, - IEnumerable pointLights, - IEnumerable directionalLights - ) { - Render(camera.View, camera.Projection, modelTransforms, pointLights, directionalLights); + 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); + GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1); + } } - // for shadow mapping - public void DepthRender(IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight) - { - GraphicsDevice.SetRenderTarget(DepthRenderTarget); - GraphicsDevice.Clear(ClearOptions.DepthBuffer, Color.Black, 1, 0); + private void DirectionalLightRender( + PerspectiveCamera camera, + IEnumerable<(Model, Matrix)> modelTransforms, + DirectionalLight directionalLight + ) { + // render the individual shadow cascades + var previousFarPlane = camera.NearPlane; + for (var i = 0; i < NumShadowCascades; i++) + { + var farPlane = camera.FarPlane / (MathHelper.Max((NumShadowCascades - i - 1) * 2f, 1f)); - SimpleDepthEffect.View = directionalLight.View; - SimpleDepthEffect.Projection = directionalLight.Projection; + // divide the view frustum + var shadowCamera = new PerspectiveCamera( + camera.Position, + camera.Forward, + camera.Up, + camera.FieldOfView, + camera.AspectRatio, + previousFarPlane, + 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( + PerspectiveCamera camera, + IEnumerable<(Model, Matrix)> modelTransforms, + DirectionalLight directionalLight, + int shadowCascadeIndex + ) { + GraphicsDevice.SetRenderTarget(ShadowRenderTargets[shadowCascadeIndex]); + GraphicsDevice.Clear(Color.White); + GraphicsDevice.DepthStencilState = DepthStencilState.Default; + GraphicsDevice.BlendState = BlendState.Opaque; + + var cameraBoundingFrustum = new BoundingFrustum(camera.View * camera.Projection); + Vector3[] frustumCorners = cameraBoundingFrustum.GetCorners(); + + Vector3 frustumCenter = Vector3.Zero; + for (var i = 0; i < frustumCorners.Length; i++) + { + frustumCenter += frustumCorners[i]; + } + frustumCenter /= 8f; + + var lightView = Matrix.CreateLookAt(frustumCenter + directionalLight.Direction, frustumCenter, Vector3.Backward); + + for (var i = 0; i < frustumCorners.Length; i++) + { + frustumCorners[i] = Vector3.Transform(frustumCorners[i], lightView); + } + + BoundingBox lightBox = BoundingBox.CreateFromPoints(frustumCorners); + + SimpleDepthEffect.View = lightView; + SimpleDepthEffect.Projection = Matrix.CreateOrthographicOffCenter( + lightBox.Min.X, + lightBox.Max.X, + lightBox.Min.Y, + lightBox.Max.Y, + -lightBox.Max.Z - 10f, // TODO: near clip plane needs scene AABB info to get rid of this magic value + -lightBox.Min.Z + ); + + var lightSpaceMatrix = SimpleDepthEffect.View * SimpleDepthEffect.Projection; + + if (shadowCascadeIndex == 0) + { + DeferredDirectionalLightEffect.LightSpaceMatrixOne = lightSpaceMatrix; + } + else if (shadowCascadeIndex == 1) + { + DeferredDirectionalLightEffect.LightSpaceMatrixTwo = lightSpaceMatrix; + } + else if (shadowCascadeIndex == 2) + { + DeferredDirectionalLightEffect.LightSpaceMatrixThree = lightSpaceMatrix; + } + else if (shadowCascadeIndex == 3) + { + DeferredDirectionalLightEffect.LightSpaceMatrixFour = lightSpaceMatrix; + } + + DeferredDirectionalLightEffect.CascadeFarPlanes[shadowCascadeIndex] = camera.FarPlane; foreach (var (model, transform) in modelTransforms) { @@ -220,6 +403,15 @@ namespace Kav } } + public void Render( + PerspectiveCamera camera, + IEnumerable<(Model, Matrix)> modelTransforms, + IEnumerable pointLights, + IEnumerable directionalLights + ) { + Render(camera.View, camera.Projection, modelTransforms, pointLights, directionalLights); + } + private void Render( Matrix view, Matrix projection, @@ -254,17 +446,6 @@ namespace Kav } } - if (meshPart.Effect is DirectionalLightEffect directionalLightEffect) - { - int i = 0; - foreach (var directionalLight in directionalLights) - { - if (i > directionalLightEffect.MaxDirectionalLights) { break; } - directionalLightEffect.DirectionalLights[i] = directionalLight; - i++; - } - } - foreach (var pass in meshPart.Effect.CurrentTechnique.Passes) { pass.Apply(); diff --git a/Resources.cs b/Resources.cs index 83d4b04..7791eaa 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 @@ -16,6 +51,18 @@ namespace Kav } } + public static byte[] ToneMapEffect + { + get + { + if (toneMapEffect == null) + { + toneMapEffect = GetResource("ToneMapEffect"); + } + return toneMapEffect; + } + } + public static byte[] DeferredPBREffect { get @@ -52,7 +99,11 @@ namespace Kav } } + private static byte[] ambientLightEffect; + private static byte[] pointLightEffect; + private static byte[] directionalLightEffect; private static byte[] gBufferEffect; + private static byte[] toneMapEffect; private static byte[] deferredPBREffect; private static byte[] pbrEffect; private static byte[] simpleDepthEffect;