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;