diff --git a/Effects/DeferredPBR_SpotLightEffect.cs b/Effects/DeferredPBR_SpotLightEffect.cs new file mode 100644 index 0000000..ceef3d4 --- /dev/null +++ b/Effects/DeferredPBR_SpotLightEffect.cs @@ -0,0 +1,79 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + public class DeferredPBR_SpotLightEffect : Effect + { + EffectParameter gPositionParam; + EffectParameter gAlbedoParam; + EffectParameter gNormalParam; + EffectParameter gMetallicRoughnessParam; + EffectParameter shadowMapParam; + + EffectParameter eyePositionParam; + + EffectParameter lightPositionParam; + EffectParameter lightDirectionParam; + EffectParameter lightColorParam; + EffectParameter lightCutoffParam; + + EffectParameter farPlaneParam; + + public Texture2D GPosition { get; set; } + public Texture2D GAlbedo { get; set; } + public Texture2D GNormal { get; set; } + public Texture2D GMetallicRoughness { get; set; } + public TextureCube ShadowMap { get; set; } + + public Vector3 EyePosition { get; set; } + + public Vector3 LightPosition { get; set; } + public Vector3 LightDirection { get; set; } + public Vector3 LightColor { get; set; } + public float LightCutoff { get; set; } //could be named lightangle? + + public float FarPlane { get; set; } + + public DeferredPBR_SpotLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_SpotLightEffect) + { + CacheEffectParameters(); + } + + protected override void OnApply() + { + gPositionParam.SetValue(GPosition); + gAlbedoParam.SetValue(GAlbedo); + gNormalParam.SetValue(GNormal); + gMetallicRoughnessParam.SetValue(GMetallicRoughness); + shadowMapParam.SetValue(ShadowMap); + + eyePositionParam.SetValue(EyePosition); + + lightPositionParam.SetValue(LightPosition); + lightDirectionParam.SetValue(LightDirection); + lightColorParam.SetValue(LightColor); + lightCutoffParam.SetValue(LightCutoff); + + farPlaneParam.SetValue(FarPlane); + } + + private void CacheEffectParameters() + { + gPositionParam = Parameters["gPosition"]; + gAlbedoParam = Parameters["gAlbedo"]; + gNormalParam = Parameters["gNormal"]; + gMetallicRoughnessParam = Parameters["gMetallicRoughness"]; + shadowMapParam = Parameters["shadowMap"]; + + eyePositionParam = Parameters["EyePosition"]; + + lightPositionParam = Parameters["LightPosition"]; + lightDirectionParam = Parameters["LightDirection"]; + lightColorParam = Parameters["LightColor"]; + lightCutoffParam = Parameters["LightCutoff"]; + + farPlaneParam = Parameters["FarPlane"]; + } + } +} diff --git a/Effects/FXB/DeferredPBR_SpotLightEffect.fxb b/Effects/FXB/DeferredPBR_SpotLightEffect.fxb new file mode 100644 index 0000000..e363552 --- /dev/null +++ b/Effects/FXB/DeferredPBR_SpotLightEffect.fxb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c6cbb05f6c748839885d4e8bbd4bf95823635d75974da7c217e6d64b5209a1fe +size 3652 diff --git a/Effects/HLSL/DeferredPBR_PointLightEffect.fx b/Effects/HLSL/DeferredPBR_PointLightEffect.fx index d6e1933..f0857c4 100644 --- a/Effects/HLSL/DeferredPBR_PointLightEffect.fx +++ b/Effects/HLSL/DeferredPBR_PointLightEffect.fx @@ -68,7 +68,6 @@ float4 ComputeColor( float3 color = ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, shadow); return float4(color, 1.0); - //return float4(shadow, shadow, shadow, 1.0); } float4 main_ps(PixelInput input) : SV_TARGET0 diff --git a/Effects/HLSL/DeferredPBR_SpotLightEffect.fx b/Effects/HLSL/DeferredPBR_SpotLightEffect.fx new file mode 100644 index 0000000..5959522 --- /dev/null +++ b/Effects/HLSL/DeferredPBR_SpotLightEffect.fx @@ -0,0 +1,109 @@ +#include "Macros.fxh" //from FNA +#include "Lighting.fxh" +#include "Shadow.fxh" + +DECLARE_TEXTURE(gPosition, 0); +DECLARE_TEXTURE(gAlbedo, 1); +DECLARE_TEXTURE(gNormal, 2); +DECLARE_TEXTURE(gMetallicRoughness, 3); +DECLARE_CUBEMAP(shadowMap, 4); + +BEGIN_CONSTANTS + + float3 EyePosition _ps(c0) _cb(c0); + + float3 LightPosition _ps(c1) _cb(c1); + float3 LightDirection _ps(c2) _cb(c2); + float3 LightColor _ps(c3) _cb(c3); + float LightCutoff _ps(c4) _cb(c4); + + float FarPlane _ps(c5) _cb(c5); + +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 = LightPosition - worldPosition; + float3 L = normalize(lightDir); + float distance = length(lightDir); + float attenuation = 1.0 / (distance * distance); + float3 radiance = LightColor * attenuation; + + //float shadow = HardPointShadow(worldPosition, N, L, PointLightPosition, SAMPLER(shadowMap), FarPlane); + 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; + + float3 lightDir = normalize(LightPosition - worldPosition); + + float theta = dot(lightDir, normalize(-LightDirection)); + + if (theta > LightCutoff) + { + return ComputeColor( + worldPosition, + normal, + albedo, + metallicRoughness.r, + metallicRoughness.g + ); + } + else + { + return float4(0.0, 0.0, 0.0, 1.0); + } +} + +Technique DeferredPBR_Point +{ + Pass + { + VertexShader = compile vs_3_0 main_vs(); + PixelShader = compile ps_3_0 main_ps(); + } +} diff --git a/Kav.Core.csproj b/Kav.Core.csproj index 1a7954e..fa3367e 100644 --- a/Kav.Core.csproj +++ b/Kav.Core.csproj @@ -24,6 +24,9 @@ Kav.Resources.DeferredPBR_DirectionalLightEffect.fxb + + Kav.Resources.DeferredPBR_SpotLightEffect.fxb + Kav.Resources.DeferredPBR_GBufferEffect.fxb diff --git a/Kav.Framework.csproj b/Kav.Framework.csproj index c60f949..fb297d8 100644 --- a/Kav.Framework.csproj +++ b/Kav.Framework.csproj @@ -27,6 +27,9 @@ Kav.Resources.DeferredPBR_GBufferEffect.fxb + + Kav.Resources.DeferredPBR_SpotLightEffect.fxb + Kav.Resources.ToneMapEffect.fxb diff --git a/Lights/SpotLight.cs b/Lights/SpotLight.cs new file mode 100644 index 0000000..7bee06c --- /dev/null +++ b/Lights/SpotLight.cs @@ -0,0 +1,22 @@ +using Microsoft.Xna.Framework; + +namespace Kav +{ + public struct SpotLight + { + public Vector3 Position { get; } + public Vector3 Direction { get; } + public Color Color { get; } + public float Intensity { get; } + public float Cutoff { get; } + + public SpotLight(Vector3 position, Vector3 direction, Color color, float intensity, float cutoff) + { + Position = position; + Direction = direction; + Color = color; + Intensity = intensity; + Cutoff = cutoff; + } + } +} diff --git a/Renderer.cs b/Renderer.cs index 2ad0ceb..f9b3ca0 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -28,6 +28,7 @@ namespace Kav private DeferredPBR_AmbientLightEffect DeferredAmbientLightEffect { get; } private DeferredPBR_PointLightEffect DeferredPointLightEffect { get; } private DeferredPBR_DirectionalLightEffect DeferredDirectionalLightEffect { get; } + private DeferredPBR_SpotLightEffect DeferredSpotLightEffect { get; } private Deferred_ToonEffect Deferred_ToonEffect { get; } private SimpleDepthEffect SimpleDepthEffect { get; } private LinearDepthEffect LinearDepthEffect { get; } @@ -156,6 +157,7 @@ namespace Kav DeferredPointLightEffect = new DeferredPBR_PointLightEffect(GraphicsDevice); DeferredDirectionalLightEffect = new DeferredPBR_DirectionalLightEffect(GraphicsDevice); DeferredDirectionalLightEffect.ShadowMapSize = ShadowMapSize; + DeferredSpotLightEffect = new DeferredPBR_SpotLightEffect(GraphicsDevice); ToneMapEffect = new Effect(graphicsDevice, Resources.ToneMapEffect); Deferred_ToonEffect = new Deferred_ToonEffect(GraphicsDevice); SkyboxEffect = new SkyboxEffect(GraphicsDevice); @@ -193,7 +195,7 @@ namespace Kav foreach (var pointLight in pointLights) { - PointLightRender(camera, modelTransforms, pointLight); + PointLightRender(modelTransforms, pointLight); } DirectionalLightRender(camera, modelTransforms, directionalLight); @@ -208,9 +210,10 @@ namespace Kav public void DeferredToonRender( PerspectiveCamera camera, IEnumerable<(Model, Matrix)> modelTransforms, - AmbientLight ambientLight, + AmbientLight? ambientLight, IEnumerable pointLights, - DirectionalLight directionalLight, + IEnumerable spotLights, + DirectionalLight? directionalLight, TextureCube skybox ) { GBufferRender(camera, modelTransforms); @@ -222,12 +225,22 @@ namespace Kav DepthRender(camera, modelTransforms); GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; - AmbientLightRender(ambientLight); + if (ambientLight.HasValue) + { + AmbientLightRender(ambientLight.Value); + } foreach (var pointLight in pointLights) { - PointLightRender(camera, modelTransforms, pointLight); + PointLightRender(modelTransforms, pointLight); + } + foreach (var spotLight in spotLights) + { + SpotLightRender(modelTransforms, spotLight); + } + if (directionalLight.HasValue) + { + DirectionalLightToonRender(camera, modelTransforms, directionalLight.Value); } - DirectionalLightToonRender(camera, modelTransforms, directionalLight); SkyboxRender(camera, skybox); GraphicsDevice.SetRenderTarget(null); @@ -373,11 +386,10 @@ namespace Kav } private void PointLightRender( - PerspectiveCamera camera, IEnumerable<(Model, Matrix)> modelTransforms, PointLight pointLight ) { - RenderPointShadows(camera, modelTransforms, pointLight); + RenderPointShadows(modelTransforms, pointLight); GraphicsDevice.SetRenderTarget(ColorRenderTarget); GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; @@ -403,6 +415,35 @@ namespace Kav } } + private void SpotLightRender( + IEnumerable<(Model, Matrix)> modelTransforms, + SpotLight spotLight + ) { + GraphicsDevice.SetRenderTarget(ColorRenderTarget); + GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; + GraphicsDevice.BlendState = BlendState.Additive; + + DeferredSpotLightEffect.GPosition = gPosition; + DeferredSpotLightEffect.GAlbedo = gAlbedo; + DeferredSpotLightEffect.GNormal = gNormal; + DeferredSpotLightEffect.GMetallicRoughness = gMetallicRoughness; + + DeferredSpotLightEffect.LightPosition = spotLight.Position; + DeferredSpotLightEffect.LightDirection = spotLight.Direction; + DeferredSpotLightEffect.LightColor = + spotLight.Color.ToVector3() * spotLight.Intensity; + DeferredSpotLightEffect.LightCutoff = spotLight.Cutoff; + + DeferredSpotLightEffect.FarPlane = 25f; // FIXME: magic value + + foreach (var pass in DeferredSpotLightEffect.CurrentTechnique.Passes) + { + pass.Apply(); + GraphicsDevice.SetVertexBuffer(FullscreenTriangle); + GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1); + } + } + private void DirectionalLightRender( PerspectiveCamera camera, IEnumerable<(Model, Matrix)> modelTransforms, @@ -617,7 +658,6 @@ namespace Kav } private void RenderPointShadows( - PerspectiveCamera camera, IEnumerable<(Model, Matrix)> modelTransforms, PointLight pointLight ) { diff --git a/Resources.cs b/Resources.cs index fd5daa5..847c960 100644 --- a/Resources.cs +++ b/Resources.cs @@ -39,6 +39,18 @@ namespace Kav } } + public static byte[] DeferredPBR_SpotLightEffect + { + get + { + if (spotLightEffect == null) + { + spotLightEffect = GetResource("DeferredPBR_SpotLightEffect.fxb"); + } + return spotLightEffect; + } + } + public static byte[] DeferredPBR_GBufferEffect { get @@ -150,6 +162,7 @@ namespace Kav private static byte[] ambientLightEffect; private static byte[] pointLightEffect; private static byte[] directionalLightEffect; + private static byte[] spotLightEffect; private static byte[] gBufferEffect; private static byte[] toneMapEffect; private static byte[] deferredToonEffect;