From 565be374bbb539ee652d0fa3b240bafb066bab18 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Thu, 1 Oct 2020 14:52:19 -0700 Subject: [PATCH 01/11] basic toon shading + controllable ambient light setting --- Effects/DeferredPBR_AmbientLightEffect.cs | 11 ++- Effects/Deferred_ToonEffect.cs | 47 +++++++++ .../FXB/DeferredPBR_AmbientLightEffect.fxb | 4 +- Effects/FXB/Deferred_ToonEffect.fxb | 3 + .../HLSL/DeferredPBR_AmbientLightEffect.fx | 8 +- Effects/HLSL/Deferred_ToonEffect.fx | 60 ++++++++++++ Kav.Core.csproj | 3 + Kav.Framework.csproj | 3 + Lights/AmbientLight.cs | 14 +++ Lights/DirectionalLight.cs | 6 +- Renderer.cs | 98 ++++++++++++++----- Resources.cs | 13 +++ 12 files changed, 240 insertions(+), 30 deletions(-) create mode 100644 Effects/Deferred_ToonEffect.cs create mode 100644 Effects/FXB/Deferred_ToonEffect.fxb create mode 100644 Effects/HLSL/Deferred_ToonEffect.fx create mode 100644 Lights/AmbientLight.cs diff --git a/Effects/DeferredPBR_AmbientLightEffect.cs b/Effects/DeferredPBR_AmbientLightEffect.cs index f1d5e31..8595426 100644 --- a/Effects/DeferredPBR_AmbientLightEffect.cs +++ b/Effects/DeferredPBR_AmbientLightEffect.cs @@ -1,3 +1,4 @@ +using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace Kav @@ -8,9 +9,13 @@ namespace Kav EffectParameter gPositionParam; EffectParameter gAlbedoParam; + EffectParameter ambientColorParam; + public Texture2D GPosition { get; set; } public Texture2D GAlbedo { get; set; } + public Vector3 AmbientColor { get; set; } + public DeferredPBR_AmbientLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_AmbientLightEffect) { CacheEffectParameters(); @@ -20,12 +25,14 @@ namespace Kav { gPositionParam.SetValue(GPosition); gAlbedoParam.SetValue(GAlbedo); + ambientColorParam.SetValue(AmbientColor); } void CacheEffectParameters() { - gPositionParam = Parameters["gPosition"]; - gAlbedoParam = Parameters["gAlbedo"]; + gPositionParam = Parameters["gPosition"]; + gAlbedoParam = Parameters["gAlbedo"]; + ambientColorParam = Parameters["AmbientLightColor"]; } } } diff --git a/Effects/Deferred_ToonEffect.cs b/Effects/Deferred_ToonEffect.cs new file mode 100644 index 0000000..5b2cca3 --- /dev/null +++ b/Effects/Deferred_ToonEffect.cs @@ -0,0 +1,47 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + public class Deferred_ToonEffect : Effect + { + EffectParameter gPositionParam; + EffectParameter gAlbedoParam; + EffectParameter gNormalParam; + + EffectParameter eyePositionParam; + EffectParameter directionalLightDirectionParam; + + public Texture2D GPosition { get; set; } + public Texture2D GAlbedo { get; set; } + public Texture2D GNormal { get; set; } + + public Vector3 EyePosition { get; set; } + public Vector3 DirectionalLightDirection { get; set; } + + public Deferred_ToonEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.Deferred_ToonEffect) + { + CacheEffectParameters(); + } + + protected override void OnApply() + { + gPositionParam.SetValue(GPosition); + gAlbedoParam.SetValue(GAlbedo); + gNormalParam.SetValue(GNormal); + + eyePositionParam.SetValue(EyePosition); + directionalLightDirectionParam.SetValue(DirectionalLightDirection); + } + + void CacheEffectParameters() + { + gPositionParam = Parameters["gPosition"]; + gAlbedoParam = Parameters["gAlbedo"]; + gNormalParam = Parameters["gNormal"]; + + eyePositionParam = Parameters["EyePosition"]; + directionalLightDirectionParam = Parameters["DirectionalLightDirection"]; + } + } +} diff --git a/Effects/FXB/DeferredPBR_AmbientLightEffect.fxb b/Effects/FXB/DeferredPBR_AmbientLightEffect.fxb index 62de278..a439c6a 100644 --- a/Effects/FXB/DeferredPBR_AmbientLightEffect.fxb +++ b/Effects/FXB/DeferredPBR_AmbientLightEffect.fxb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ea0cb071a1e53fec5058fae9919d376e408d33cd3de9485d42e1ebcbee85546 -size 1016 +oid sha256:244ded9c443bc10e538958075cabd14d2b066b9fd0743186ce620823380cb488 +size 1168 diff --git a/Effects/FXB/Deferred_ToonEffect.fxb b/Effects/FXB/Deferred_ToonEffect.fxb new file mode 100644 index 0000000..1dbb741 --- /dev/null +++ b/Effects/FXB/Deferred_ToonEffect.fxb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e49380a4f06e418f7056355d4b1426aa62d1025734c643c12ee9501c8a3d59b1 +size 1568 diff --git a/Effects/HLSL/DeferredPBR_AmbientLightEffect.fx b/Effects/HLSL/DeferredPBR_AmbientLightEffect.fx index 4144c8d..6c6e173 100644 --- a/Effects/HLSL/DeferredPBR_AmbientLightEffect.fx +++ b/Effects/HLSL/DeferredPBR_AmbientLightEffect.fx @@ -3,6 +3,12 @@ DECLARE_TEXTURE(gPosition, 0); DECLARE_TEXTURE(gAlbedo, 1); +BEGIN_CONSTANTS + +float3 AmbientLightColor _ps(c0) _cb(c0); + +END_CONSTANTS + struct VertexInput { float4 Position : POSITION; @@ -29,7 +35,7 @@ float4 ComputeColor( float3 worldPosition, float3 albedo ) { - float3 color = float3(0.03, 0.03, 0.03) * albedo; + float3 color = AmbientLightColor * albedo; return float4(color, 1.0); } diff --git a/Effects/HLSL/Deferred_ToonEffect.fx b/Effects/HLSL/Deferred_ToonEffect.fx new file mode 100644 index 0000000..7423236 --- /dev/null +++ b/Effects/HLSL/Deferred_ToonEffect.fx @@ -0,0 +1,60 @@ +#include "Macros.fxh" + +DECLARE_TEXTURE(gPosition, 0); +DECLARE_TEXTURE(gAlbedo, 1); +DECLARE_TEXTURE(gNormal, 2); + +BEGIN_CONSTANTS + +float3 EyePosition _ps(c0) _cb(c0); + +float3 DirectionalLightDirection _ps(c1) _cb(c1); +float3 AmbientLightColor _ps(c2) _cb(c2); + +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; +} + +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; + + float3 V = normalize(EyePosition - worldPosition); + float3 L = normalize(DirectionalLightDirection); + float3 N = normalize(normal); + float NdotL = dot(N, L); + + float lightIntensity = (NdotL > 0.0) ? 1.0 : 0.0; + return float4(albedo * (lightIntensity, 1.0); +} + +Technique Deferred_Toon +{ + 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 327d7bb..b018e72 100644 --- a/Kav.Core.csproj +++ b/Kav.Core.csproj @@ -30,6 +30,9 @@ Kav.Resources.ToneMapEffect.fxb + + Kav.Resources.Deferred_ToonEffect.fxb + Kav.Resources.DeferredPBREffect.fxb diff --git a/Kav.Framework.csproj b/Kav.Framework.csproj index 2b2dd4f..c2d9079 100644 --- a/Kav.Framework.csproj +++ b/Kav.Framework.csproj @@ -30,6 +30,9 @@ Kav.Resources.ToneMapEffect.fxb + + Kav.Resources.Deferred_ToonEffect.fxb + Kav.Resources.DeferredPBREffect.fxb diff --git a/Lights/AmbientLight.cs b/Lights/AmbientLight.cs new file mode 100644 index 0000000..94bb42e --- /dev/null +++ b/Lights/AmbientLight.cs @@ -0,0 +1,14 @@ +using Microsoft.Xna.Framework; + +namespace Kav +{ + public struct AmbientLight + { + public Color Color { get; set; } + + public AmbientLight(Color color) + { + Color = color; + } + } +} diff --git a/Lights/DirectionalLight.cs b/Lights/DirectionalLight.cs index a22ab33..985745f 100644 --- a/Lights/DirectionalLight.cs +++ b/Lights/DirectionalLight.cs @@ -4,9 +4,9 @@ namespace Kav { public struct DirectionalLight { - public Vector3 Direction { get; set; } - public Color Color { get; set; } - public float Intensity { get; set; } + public Vector3 Direction { get; } + public Color Color { get; } + public float Intensity { get; } public Matrix View { diff --git a/Renderer.cs b/Renderer.cs index 63b34e5..5978274 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -24,6 +24,7 @@ namespace Kav private DeferredPBR_AmbientLightEffect DeferredAmbientLightEffect { get; } private DeferredPBR_PointLightEffect DeferredPointLightEffect { get; } private DeferredPBR_DirectionalLightEffect DeferredDirectionalLightEffect { get; } + private Deferred_ToonEffect Deferred_ToonEffect { get; } private SimpleDepthEffect SimpleDepthEffect { get; } private Effect ToneMapEffect { get; } @@ -136,6 +137,7 @@ namespace Kav DeferredDirectionalLightEffect = new DeferredPBR_DirectionalLightEffect(GraphicsDevice); DeferredDirectionalLightEffect.ShadowMapSize = ShadowMapSize; ToneMapEffect = new Effect(graphicsDevice, Resources.ToneMapEffect); + Deferred_ToonEffect = new Deferred_ToonEffect(GraphicsDevice); FullscreenTriangle = new VertexBuffer(GraphicsDevice, typeof(VertexPositionTexture), 3, BufferUsage.WriteOnly); FullscreenTriangle.SetData(new VertexPositionTexture[3] { @@ -150,11 +152,62 @@ namespace Kav public void DeferredRender( PerspectiveCamera camera, IEnumerable<(Model, Matrix)> modelTransforms, + AmbientLight ambientLight, IEnumerable pointLights, DirectionalLight directionalLight ) { - // g-buffer pass + GBufferRender(camera, modelTransforms); + GraphicsDevice.SetRenderTarget(ColorRenderTarget); + GraphicsDevice.Clear(Color.Black); + + AmbientLightRender(ambientLight); + + DeferredPointLightEffect.EyePosition = camera.Position; + + foreach (var pointLight in pointLights) + { + PointLightRender(pointLight); + } + + DirectionalLightRender(camera, modelTransforms, directionalLight); + + 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 DeferredToonRender( + PerspectiveCamera camera, + IEnumerable<(Model, Matrix)> modelTransforms, + AmbientLight ambientLight, + DirectionalLight directionalLight + ) { + GBufferRender(camera, modelTransforms); + + GraphicsDevice.SetRenderTarget(ColorRenderTarget); + GraphicsDevice.Clear(Color.Black); + + AmbientLightRender(ambientLight); + + Deferred_ToonEffect.GPosition = gPosition; + Deferred_ToonEffect.GAlbedo = gAlbedo; + Deferred_ToonEffect.GNormal = gNormal; + DirectionalLightToonRender(camera, directionalLight); + + 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(); + } + + private void GBufferRender( + PerspectiveCamera camera, + IEnumerable<(Model, Matrix)> modelTransforms + ) { GraphicsDevice.SetRenderTargets(GBuffer); GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0); GraphicsDevice.DepthStencilState = DepthStencilState.Default; @@ -192,14 +245,17 @@ namespace Kav } } } + } + private void AmbientLightRender(AmbientLight ambientLight) + { GraphicsDevice.SetRenderTarget(ColorRenderTarget); - GraphicsDevice.Clear(Color.Black); GraphicsDevice.BlendState = BlendState.Additive; GraphicsDevice.DepthStencilState = DepthStencilState.None; DeferredAmbientLightEffect.GPosition = gPosition; DeferredAmbientLightEffect.GAlbedo = gAlbedo; + DeferredAmbientLightEffect.AmbientColor = ambientLight.Color.ToVector3(); foreach (var pass in DeferredAmbientLightEffect.CurrentTechnique.Passes) { @@ -207,26 +263,6 @@ namespace Kav GraphicsDevice.SetVertexBuffer(FullscreenTriangle); GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1); } - - DeferredPointLightEffect.EyePosition = camera.Position; - - foreach (var pointLight in pointLights) - { - PointLightRender(pointLight); - } - - 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, null, null, null, ToneMapEffect); - SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White); - SpriteBatch.End(); } private void PointLightRender(PointLight pointLight) @@ -313,6 +349,24 @@ namespace Kav } } + private void DirectionalLightToonRender( + PerspectiveCamera camera, + DirectionalLight directionalLight + ) { + Deferred_ToonEffect.EyePosition = camera.Position; + Deferred_ToonEffect.DirectionalLightDirection = directionalLight.Direction; + + GraphicsDevice.SetRenderTarget(ColorRenderTarget); + GraphicsDevice.BlendState = BlendState.Additive; + + foreach (EffectPass pass in Deferred_ToonEffect.CurrentTechnique.Passes) + { + pass.Apply(); + GraphicsDevice.SetVertexBuffer(FullscreenTriangle); + GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1); + } + } + private void RenderShadowMap( PerspectiveCamera camera, IEnumerable<(Model, Matrix)> modelTransforms, diff --git a/Resources.cs b/Resources.cs index 7791eaa..9badc01 100644 --- a/Resources.cs +++ b/Resources.cs @@ -63,6 +63,18 @@ namespace Kav } } + public static byte[] Deferred_ToonEffect + { + get + { + if (deferredToonEffect == null) + { + deferredToonEffect = GetResource("Deferred_ToonEffect"); + } + return deferredToonEffect; + } + } + public static byte[] DeferredPBREffect { get @@ -104,6 +116,7 @@ namespace Kav private static byte[] directionalLightEffect; private static byte[] gBufferEffect; private static byte[] toneMapEffect; + private static byte[] deferredToonEffect; private static byte[] deferredPBREffect; private static byte[] pbrEffect; private static byte[] simpleDepthEffect; -- 2.25.1 From 2fb20747e4978d1b745cde76a1bcac452dfd34e7 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Thu, 1 Oct 2020 22:26:40 -0700 Subject: [PATCH 02/11] more toon effect + remove material from MeshPart --- Effects/Deferred_ToonEffect.cs | 14 ++++- Effects/FXB/Deferred_ToonEffect.fxb | 4 +- Effects/HLSL/Deferred_ToonEffect.fx | 39 ++++++++++-- Geometry/MeshPart.cs | 40 ++++++++++-- Geometry/Model.cs | 50 +++++++++++++++ Loaders/ModelLoader.cs | 30 ++++----- Renderer.cs | 98 ++++++++--------------------- 7 files changed, 174 insertions(+), 101 deletions(-) diff --git a/Effects/Deferred_ToonEffect.cs b/Effects/Deferred_ToonEffect.cs index 5b2cca3..9196197 100644 --- a/Effects/Deferred_ToonEffect.cs +++ b/Effects/Deferred_ToonEffect.cs @@ -11,6 +11,9 @@ namespace Kav EffectParameter eyePositionParam; EffectParameter directionalLightDirectionParam; + EffectParameter directionalLightColorParam; + + EffectParameter softnessParam; public Texture2D GPosition { get; set; } public Texture2D GAlbedo { get; set; } @@ -18,6 +21,9 @@ namespace Kav public Vector3 EyePosition { get; set; } public Vector3 DirectionalLightDirection { get; set; } + public Vector3 DirectionalLightColor { get; set; } + + public float Softness { get; set; } public Deferred_ToonEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.Deferred_ToonEffect) { @@ -29,9 +35,12 @@ namespace Kav gPositionParam.SetValue(GPosition); gAlbedoParam.SetValue(GAlbedo); gNormalParam.SetValue(GNormal); - + eyePositionParam.SetValue(EyePosition); directionalLightDirectionParam.SetValue(DirectionalLightDirection); + directionalLightColorParam.SetValue(DirectionalLightColor); + + softnessParam.SetValue(Softness); } void CacheEffectParameters() @@ -42,6 +51,9 @@ namespace Kav eyePositionParam = Parameters["EyePosition"]; directionalLightDirectionParam = Parameters["DirectionalLightDirection"]; + directionalLightColorParam = Parameters["DirectionalLightColor"]; + + softnessParam = Parameters["Softness"]; } } } diff --git a/Effects/FXB/Deferred_ToonEffect.fxb b/Effects/FXB/Deferred_ToonEffect.fxb index 1dbb741..4b5284e 100644 --- a/Effects/FXB/Deferred_ToonEffect.fxb +++ b/Effects/FXB/Deferred_ToonEffect.fxb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e49380a4f06e418f7056355d4b1426aa62d1025734c643c12ee9501c8a3d59b1 -size 1568 +oid sha256:1e5bd52fb74a48a7165a50bce38428a5f6d84550910129a6a17c66e87b2d8b0d +size 2504 diff --git a/Effects/HLSL/Deferred_ToonEffect.fx b/Effects/HLSL/Deferred_ToonEffect.fx index 7423236..42251a4 100644 --- a/Effects/HLSL/Deferred_ToonEffect.fx +++ b/Effects/HLSL/Deferred_ToonEffect.fx @@ -9,7 +9,9 @@ BEGIN_CONSTANTS float3 EyePosition _ps(c0) _cb(c0); float3 DirectionalLightDirection _ps(c1) _cb(c1); -float3 AmbientLightColor _ps(c2) _cb(c2); +float3 DirectionalLightColor _ps(c2) _cb(c2); + +float Softness _ps(c3) _cb(c3); END_CONSTANTS @@ -44,10 +46,39 @@ float4 main_ps(PixelInput input) : SV_TARGET0 float3 V = normalize(EyePosition - worldPosition); float3 L = normalize(DirectionalLightDirection); float3 N = normalize(normal); - float NdotL = dot(N, L); + float3 H = normalize(V + L); - float lightIntensity = (NdotL > 0.0) ? 1.0 : 0.0; - return float4(albedo * (lightIntensity, 1.0); + float NdotL = dot(N, L); + float NdotH = max(dot(N, H), 0.0); + + float lightIntensity; + if (Softness > 0.0) + { + lightIntensity = smoothstep(0, Softness, NdotL); + } + else + { + lightIntensity = (NdotL > 0.0) ? 1.0 : 0.0; + } + + float3 light = lightIntensity * DirectionalLightColor; + + float specularIntensity = pow(NdotH * lightIntensity, 32 * 32); + float specularSmooth = smoothstep(0.005, 0.01, specularIntensity); + + float3 specular = specularSmooth * float3(1.0, 1.0, 1.0); + + float rimColor = float3(1.0, 1.0, 1.0); + float rimThreshold = 0.1; + float rimAmount = 0.76; + float rimDot = 1 - dot(V, N); + float rimIntensity = rimDot * pow(max(NdotL, 0.0), rimThreshold); + rimIntensity = smoothstep(rimAmount - 0.01, rimAmount + 0.01, rimIntensity); + float3 rim = rimIntensity * rimColor; + + float3 color = albedo * (light + specular + rim); + + return float4(color, 1.0); } Technique Deferred_Toon diff --git a/Geometry/MeshPart.cs b/Geometry/MeshPart.cs index e97b49c..248fb1a 100644 --- a/Geometry/MeshPart.cs +++ b/Geometry/MeshPart.cs @@ -9,15 +9,47 @@ namespace Kav public VertexBuffer VertexBuffer { get; } public Triangle[] Triangles { get; } public Vector3[] Positions { get; } - public Effect Effect { get; } + + private Texture2D albedoTexture = null; + private Texture2D normalTexture = null; + private Texture2D metallicRoughnessTexture = null; - public MeshPart(VertexBuffer vertexBuffer, IndexBuffer indexBuffer, Vector3[] positions, Triangle[] triangles, Effect effect) - { + public Texture2D AlbedoTexture + { + get { return DisableAlbedoMap ? null : albedoTexture; } + set { albedoTexture = value; } + } + + public Texture2D NormalTexture + { + get { return DisableNormalMap ? null : normalTexture; } + set { normalTexture = value; } + } + + public Texture2D MetallicRoughnessTexture + { + get { return DisableMetallicRoughnessMap ? null : metallicRoughnessTexture; } + set { metallicRoughnessTexture = value; } + } + + public Vector3 Albedo { get; set; } = Vector3.One; + public float Metallic { get; set; } = 0.5f; + public float Roughness { get; set; } = 0.5f; + + public bool DisableAlbedoMap { get; set; } = false; + public bool DisableNormalMap { get; set; } = false; + public bool DisableMetallicRoughnessMap { get; set; } = false; + + public MeshPart( + VertexBuffer vertexBuffer, + IndexBuffer indexBuffer, + Vector3[] positions, + Triangle[] triangles + ) { VertexBuffer = vertexBuffer; IndexBuffer = indexBuffer; Positions = positions; Triangles = triangles; - Effect = effect; } } } diff --git a/Geometry/Model.cs b/Geometry/Model.cs index 1d83410..e9ad690 100644 --- a/Geometry/Model.cs +++ b/Geometry/Model.cs @@ -6,9 +6,59 @@ namespace Kav { public Mesh[] Meshes { get; } + private Color albedoValue; + + public Color Albedo + { + get { return albedoValue; } + set + { + foreach (var mesh in Meshes) + { + foreach (var meshPart in mesh.MeshParts) + { + meshPart.Albedo = value.ToVector3(); + } + } + } + } + public Model(Mesh[] meshes) { Meshes = meshes; } + + public void DisableAlbedoMaps() + { + foreach (var mesh in Meshes) + { + foreach (var meshPart in mesh.MeshParts) + { + meshPart.DisableAlbedoMap = true; + } + } + } + + public void DisableNormalMaps() + { + foreach (var mesh in Meshes) + { + foreach (var meshPart in mesh.MeshParts) + { + meshPart.DisableNormalMap = true; + } + } + } + + public void DisableMetallicRoughnessMaps() + { + foreach (var mesh in Meshes) + { + foreach (var meshPart in mesh.MeshParts) + { + meshPart.DisableMetallicRoughnessMap = true; + } + } + } } } diff --git a/Loaders/ModelLoader.cs b/Loaders/ModelLoader.cs index 4e908a0..634ae94 100644 --- a/Loaders/ModelLoader.cs +++ b/Loaders/ModelLoader.cs @@ -15,19 +15,6 @@ namespace Kav foreach (var meshPartData in meshData.MeshParts) { - var effect = new Kav.DeferredPBR_GBufferEffect( - graphicsDevice - ) - { - Albedo = meshPartData.Albedo, - Metallic = meshPartData.Metallic, - Roughness = meshPartData.Roughness, - - AlbedoTexture = meshPartData.AlbedoTexture, - NormalTexture = meshPartData.NormalTexture, - MetallicRoughnessTexture = meshPartData.MetallicRoughnessTexture - }; - var triangles = new Kav.Triangle[meshPartData.Triangles.Length]; for (int i = 0; i < meshPartData.Triangles.Length; i++) { @@ -40,13 +27,22 @@ namespace Kav ); } - meshParts.Add(new Kav.MeshPart( + var meshPart = new Kav.MeshPart( meshPartData.VertexBuffer, meshPartData.IndexBuffer, meshPartData.Positions, - triangles, - effect - )); + triangles + ); + + meshPart.Albedo = meshPartData.Albedo; + meshPart.Metallic = meshPartData.Metallic; + meshPart.Roughness = meshPartData.Roughness; + + meshPart.AlbedoTexture = meshPartData.AlbedoTexture; + meshPart.NormalTexture = meshPartData.NormalTexture; + meshPart.MetallicRoughnessTexture = meshPartData.MetallicRoughnessTexture; + + meshParts.Add(meshPart); } meshes.Add(new Kav.Mesh(meshParts.ToArray())); diff --git a/Renderer.cs b/Renderer.cs index 5978274..4861f2d 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -21,6 +21,8 @@ namespace Kav private RenderTarget2D[] ShadowRenderTargets { get; } private DeferredPBREffect DeferredPBREffect { get; } + /* FIXME: these next two dont actually have anything to do with PBR */ + private DeferredPBR_GBufferEffect Deferred_GBufferEffect { get; } private DeferredPBR_AmbientLightEffect DeferredAmbientLightEffect { get; } private DeferredPBR_PointLightEffect DeferredPointLightEffect { get; } private DeferredPBR_DirectionalLightEffect DeferredDirectionalLightEffect { get; } @@ -132,6 +134,8 @@ namespace Kav SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice); DeferredPBREffect = new DeferredPBREffect(GraphicsDevice); + + Deferred_GBufferEffect = new DeferredPBR_GBufferEffect(GraphicsDevice); DeferredAmbientLightEffect = new DeferredPBR_AmbientLightEffect(GraphicsDevice); DeferredPointLightEffect = new DeferredPBR_PointLightEffect(GraphicsDevice); DeferredDirectionalLightEffect = new DeferredPBR_DirectionalLightEffect(GraphicsDevice); @@ -199,7 +203,7 @@ namespace Kav GraphicsDevice.SetRenderTarget(null); GraphicsDevice.Clear(Color.Black); - SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, null, null, null, ToneMapEffect); + SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, null, null, null, null); SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White); SpriteBatch.End(); } @@ -219,17 +223,22 @@ namespace Kav { foreach (var meshPart in modelMesh.MeshParts) { + Deferred_GBufferEffect.World = transform; + Deferred_GBufferEffect.View = camera.View; + Deferred_GBufferEffect.Projection = camera.Projection; + + Deferred_GBufferEffect.Albedo = meshPart.Albedo; + Deferred_GBufferEffect.Metallic = meshPart.Metallic; + Deferred_GBufferEffect.Roughness = meshPart.Roughness; + + Deferred_GBufferEffect.AlbedoTexture = meshPart.AlbedoTexture; + Deferred_GBufferEffect.NormalTexture = meshPart.NormalTexture; + Deferred_GBufferEffect.MetallicRoughnessTexture = meshPart.MetallicRoughnessTexture; + GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer); GraphicsDevice.Indices = meshPart.IndexBuffer; - if (meshPart.Effect is TransformEffect transformEffect) - { - transformEffect.World = transform; - transformEffect.View = camera.View; - transformEffect.Projection = camera.Projection; - } - - foreach (var pass in meshPart.Effect.CurrentTechnique.Passes) + foreach (var pass in Deferred_GBufferEffect.CurrentTechnique.Passes) { pass.Apply(); @@ -353,12 +362,16 @@ namespace Kav PerspectiveCamera camera, DirectionalLight directionalLight ) { - Deferred_ToonEffect.EyePosition = camera.Position; - Deferred_ToonEffect.DirectionalLightDirection = directionalLight.Direction; - GraphicsDevice.SetRenderTarget(ColorRenderTarget); GraphicsDevice.BlendState = BlendState.Additive; + Deferred_ToonEffect.EyePosition = camera.Position; + Deferred_ToonEffect.DirectionalLightDirection = directionalLight.Direction; + Deferred_ToonEffect.DirectionalLightColor = + directionalLight.Color.ToVector3() * directionalLight.Intensity; + + Deferred_ToonEffect.Softness = 0.01f; + foreach (EffectPass pass in Deferred_ToonEffect.CurrentTechnique.Passes) { pass.Apply(); @@ -456,66 +469,5 @@ 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, - IEnumerable<(Model, Matrix)> modelTransforms, - IEnumerable pointLights, - IEnumerable directionalLights - ) { - foreach (var (model, transform) in modelTransforms) - { - foreach (var modelMesh in model.Meshes) - { - foreach (var meshPart in modelMesh.MeshParts) - { - GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer); - GraphicsDevice.Indices = meshPart.IndexBuffer; - - if (meshPart.Effect is TransformEffect transformEffect) - { - transformEffect.World = transform; - transformEffect.View = view; - transformEffect.Projection = projection; - } - - if (meshPart.Effect is PointLightEffect pointLightEffect) - { - int i = 0; - foreach (var pointLight in pointLights) - { - if (i > pointLightEffect.MaxPointLights) { break; } - pointLightEffect.PointLights[i] = pointLight; - i++; - } - } - - foreach (var pass in meshPart.Effect.CurrentTechnique.Passes) - { - pass.Apply(); - - GraphicsDevice.DrawIndexedPrimitives( - PrimitiveType.TriangleList, - 0, - 0, - meshPart.VertexBuffer.VertexCount, - 0, - meshPart.Triangles.Length - ); - } - } - } - } - } } } -- 2.25.1 From 632f0a5b061428e59c2ac5488bbf76801de2d540 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Fri, 2 Oct 2020 11:57:13 -0700 Subject: [PATCH 03/11] break shadow computes out into include file --- .../DeferredPBR_DirectionalLightEffect.fxb | 4 +- .../DeferredPBR_DirectionalLightEffect.fx | 129 +++++------------- Effects/HLSL/Shadow.fxh | 87 ++++++++++++ 3 files changed, 122 insertions(+), 98 deletions(-) create mode 100644 Effects/HLSL/Shadow.fxh diff --git a/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb b/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb index 9f6cf27..1ec8b6e 100644 --- a/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb +++ b/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be44f16057328acf2bf9d2e4ff1e2d448df720711a2900daa39f5fc8c8732711 -size 21692 +oid sha256:3eb5c7dfc3e166c9baf57be77405a644fdf390aa8deb4bb7d39e044b7b3d338b +size 21776 diff --git a/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx b/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx index 0982521..de683cd 100644 --- a/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx +++ b/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx @@ -1,5 +1,6 @@ #include "Macros.fxh" //from FNA #include "Lighting.fxh" +#include "Shadow.fxh" static const int NUM_SHADOW_CASCADES = 4; @@ -35,26 +36,6 @@ MATRIX_CONSTANTS 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; @@ -79,35 +60,8 @@ PixelInput main_vs(VertexInput input) // 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 @@ -139,69 +93,52 @@ float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L) 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); + return PoissonShadow( + positionWorldSpace, + N, + L, + lightSpaceMatrix, + SAMPLER(shadowMapOne), + ShadowMapSize + ); } else if (shadowCascadeIndex == 1) { - visibility = PoissonCoord(SAMPLER(shadowMapTwo), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias); + return PoissonShadow( + positionWorldSpace, + N, + L, + lightSpaceMatrix, + SAMPLER(shadowMapTwo), + ShadowMapSize + ); } else if (shadowCascadeIndex == 2) { - visibility = PoissonCoord(SAMPLER(shadowMapThree), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias); + return PoissonShadow( + positionWorldSpace, + N, + L, + lightSpaceMatrix, + SAMPLER(shadowMapThree), + ShadowMapSize + ); } else { - visibility = PoissonCoord(SAMPLER(shadowMapFour), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias); + return PoissonShadow( + positionWorldSpace, + N, + L, + lightSpaceMatrix, + SAMPLER(shadowMapFour), + ShadowMapSize + ); } - - return visibility; } float4 ComputeColor( diff --git a/Effects/HLSL/Shadow.fxh b/Effects/HLSL/Shadow.fxh new file mode 100644 index 0000000..38efc3e --- /dev/null +++ b/Effects/HLSL/Shadow.fxh @@ -0,0 +1,87 @@ +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 ) +}; + +// TODO: this should probably sample a noise texture instead +// 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 PoissonShadow( + float3 positionWorldSpace, + float3 N, + float3 L, + float4x4 lightSpaceMatrix, + sampler shadowMap, + int shadowMapSize +) { + float bias = 0.005 * tan(acos(dot(N, L))); + bias = clamp(bias, 0, 0.01); + + float4 positionLightSpace = mul(float4(positionWorldSpace, 1.0), lightSpaceMatrix); + + // maps to [-1, 1] + float3 projectionCoords = positionLightSpace.xyz / positionLightSpace.w; + + // maps to [0, 1] + // NOTE: In XNA, clip space z is [0, 1] range + projectionCoords.x = (projectionCoords.x * 0.5) + 0.5; + projectionCoords.y = (projectionCoords.y * 0.5) + 0.5; + projectionCoords.y *= -1; + + if (projectionCoords.z > 1.0) + { + return 1.0; + } + + float inc = 1.0 / shadowMapSize; + + // Poisson soft shadows + float visibility = 0.0; + + visibility = PoissonCoord( + shadowMap, + positionWorldSpace, + projectionCoords.xy, + projectionCoords.z, + bias + ); + + return visibility; +} -- 2.25.1 From d370d4e2e452aa4de5ffe210a90dccba1dd72a8f Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Fri, 2 Oct 2020 12:28:28 -0700 Subject: [PATCH 04/11] toon shadows --- EffectInterfaces/ShadowCascadeEffect.cs | 14 +++ Effects/DeferredPBR_DirectionalLightEffect.cs | 5 +- Effects/Deferred_ToonEffect.cs | 63 +++++++++- Effects/FXB/Deferred_ToonEffect.fxb | 4 +- Effects/HLSL/Deferred_ToonEffect.fx | 113 +++++++++++++++++- Renderer.cs | 96 +++++++++------ 6 files changed, 250 insertions(+), 45 deletions(-) create mode 100644 EffectInterfaces/ShadowCascadeEffect.cs diff --git a/EffectInterfaces/ShadowCascadeEffect.cs b/EffectInterfaces/ShadowCascadeEffect.cs new file mode 100644 index 0000000..e55ba70 --- /dev/null +++ b/EffectInterfaces/ShadowCascadeEffect.cs @@ -0,0 +1,14 @@ +using Microsoft.Xna.Framework; + +namespace Kav +{ + public interface ShadowCascadeEffect + { + Matrix LightSpaceMatrixOne { get; set; } + Matrix LightSpaceMatrixTwo { get; set; } + Matrix LightSpaceMatrixThree { get; set; } + Matrix LightSpaceMatrixFour { get; set; } + + float[] CascadeFarPlanes { get; } + } +} diff --git a/Effects/DeferredPBR_DirectionalLightEffect.cs b/Effects/DeferredPBR_DirectionalLightEffect.cs index 5e1f919..a30c1fe 100644 --- a/Effects/DeferredPBR_DirectionalLightEffect.cs +++ b/Effects/DeferredPBR_DirectionalLightEffect.cs @@ -3,7 +3,7 @@ using Microsoft.Xna.Framework.Graphics; namespace Kav { - public class DeferredPBR_DirectionalLightEffect : Effect + public class DeferredPBR_DirectionalLightEffect : Effect, ShadowCascadeEffect { EffectParameter gPositionParam; EffectParameter gAlbedoParam; @@ -46,7 +46,7 @@ namespace Kav public Vector3 DirectionalLightDirection { get; set; } public Vector3 DirectionalLightColor { get; set; } - public readonly float[] CascadeFarPlanes; + public float[] CascadeFarPlanes { get; } public int ShadowMapSize { get; set; } @@ -147,7 +147,6 @@ namespace Kav directionalLightColorParam = Parameters["DirectionalLightColor"]; cascadeFarPlanesParam = Parameters["CascadeFarPlanes"]; - shadowMapSizeParam = Parameters["ShadowMapSize"]; lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"]; diff --git a/Effects/Deferred_ToonEffect.cs b/Effects/Deferred_ToonEffect.cs index 9196197..a55b678 100644 --- a/Effects/Deferred_ToonEffect.cs +++ b/Effects/Deferred_ToonEffect.cs @@ -3,30 +3,61 @@ using Microsoft.Xna.Framework.Graphics; namespace Kav { - public class Deferred_ToonEffect : Effect + public class Deferred_ToonEffect : Effect, ShadowCascadeEffect { EffectParameter gPositionParam; EffectParameter gAlbedoParam; EffectParameter gNormalParam; + EffectParameter shadowMapOneParam; + EffectParameter shadowMapTwoParam; + EffectParameter shadowMapThreeParam; + EffectParameter shadowMapFourParam; + EffectParameter eyePositionParam; EffectParameter directionalLightDirectionParam; EffectParameter directionalLightColorParam; EffectParameter softnessParam; + 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 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 float Softness { get; set; } + public float[] CascadeFarPlanes { get; } + public float 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 Deferred_ToonEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.Deferred_ToonEffect) { + CascadeFarPlanes = new float[4]; CacheEffectParameters(); } @@ -36,11 +67,26 @@ namespace Kav gAlbedoParam.SetValue(GAlbedo); gNormalParam.SetValue(GNormal); + shadowMapOneParam.SetValue(ShadowMapOne); + shadowMapTwoParam.SetValue(ShadowMapTwo); + shadowMapThreeParam.SetValue(ShadowMapThree); + shadowMapFourParam.SetValue(ShadowMapFour); + eyePositionParam.SetValue(EyePosition); directionalLightDirectionParam.SetValue(DirectionalLightDirection); directionalLightColorParam.SetValue(DirectionalLightColor); softnessParam.SetValue(Softness); + + cascadeFarPlanesParam.SetValue(CascadeFarPlanes); + shadowMapSizeParam.SetValue(ShadowMapSize); + + lightSpaceMatrixOneParam.SetValue(LightSpaceMatrixOne); + lightSpaceMatrixTwoParam.SetValue(LightSpaceMatrixTwo); + lightSpaceMatrixThreeParam.SetValue(LightSpaceMatrixThree); + lightSpaceMatrixFourParam.SetValue(LightSpaceMatrixFour); + + viewMatrixParam.SetValue(ViewMatrix); } void CacheEffectParameters() @@ -49,11 +95,26 @@ namespace Kav gAlbedoParam = Parameters["gAlbedo"]; gNormalParam = Parameters["gNormal"]; + shadowMapOneParam = Parameters["shadowMapOne"]; + shadowMapTwoParam = Parameters["shadowMapTwo"]; + shadowMapThreeParam = Parameters["shadowMapThree"]; + shadowMapFourParam = Parameters["shadowMapFour"]; + eyePositionParam = Parameters["EyePosition"]; directionalLightDirectionParam = Parameters["DirectionalLightDirection"]; directionalLightColorParam = Parameters["DirectionalLightColor"]; softnessParam = Parameters["Softness"]; + + 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/FXB/Deferred_ToonEffect.fxb b/Effects/FXB/Deferred_ToonEffect.fxb index 4b5284e..f40c541 100644 --- a/Effects/FXB/Deferred_ToonEffect.fxb +++ b/Effects/FXB/Deferred_ToonEffect.fxb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e5bd52fb74a48a7165a50bce38428a5f6d84550910129a6a17c66e87b2d8b0d -size 2504 +oid sha256:6f1008f0dd9ab943b47e548d9f61d122e44785f7fbb9ea47b7d73448af681016 +size 21180 diff --git a/Effects/HLSL/Deferred_ToonEffect.fx b/Effects/HLSL/Deferred_ToonEffect.fx index 42251a4..9da8035 100644 --- a/Effects/HLSL/Deferred_ToonEffect.fx +++ b/Effects/HLSL/Deferred_ToonEffect.fx @@ -1,17 +1,37 @@ #include "Macros.fxh" +#include "Shadow.fxh" + +static const int NUM_SHADOW_CASCADES = 4; DECLARE_TEXTURE(gPosition, 0); DECLARE_TEXTURE(gAlbedo, 1); DECLARE_TEXTURE(gNormal, 2); +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 EyePosition _ps(c0) _cb(c0); -float3 DirectionalLightDirection _ps(c1) _cb(c1); -float3 DirectionalLightColor _ps(c2) _cb(c2); +float3 DirectionalLightDirection _ps(c1) _cb(c1); +float3 DirectionalLightColor _ps(c2) _cb(c2); -float Softness _ps(c3) _cb(c3); +float Softness _ps(c3) _cb(c3); + +float CascadeFarPlanes[NUM_SHADOW_CASCADES] _ps(c4) _cb(c4); + +float ShadowMapSize _ps(c8) _cb(c8); + +MATRIX_CONSTANTS + +float4x4 LightSpaceMatrixOne _ps(c9) _cb(c9); +float4x4 LightSpaceMatrixTwo _ps(c13) _cb(c13); +float4x4 LightSpaceMatrixThree _ps(c17) _cb(c17); +float4x4 LightSpaceMatrixFour _ps(c21) _cb(c21); + +float4x4 ViewMatrix _ps(c25) _cb(c25); END_CONSTANTS @@ -37,6 +57,88 @@ PixelInput main_vs(VertexInput input) return output; } +float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L) +{ + 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; + } + + // PCF + Poisson soft shadows + + if (shadowCascadeIndex == 0) + { + return PoissonShadow( + positionWorldSpace, + N, + L, + lightSpaceMatrix, + SAMPLER(shadowMapOne), + ShadowMapSize + ); + } + else if (shadowCascadeIndex == 1) + { + return PoissonShadow( + positionWorldSpace, + N, + L, + lightSpaceMatrix, + SAMPLER(shadowMapTwo), + ShadowMapSize + ); + } + else if (shadowCascadeIndex == 2) + { + return PoissonShadow( + positionWorldSpace, + N, + L, + lightSpaceMatrix, + SAMPLER(shadowMapThree), + ShadowMapSize + ); + } + else + { + return PoissonShadow( + positionWorldSpace, + N, + L, + lightSpaceMatrix, + SAMPLER(shadowMapFour), + ShadowMapSize + ); + } +} + +// FIXME: organize this float4 main_ps(PixelInput input) : SV_TARGET0 { float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb; @@ -76,7 +178,8 @@ float4 main_ps(PixelInput input) : SV_TARGET0 rimIntensity = smoothstep(rimAmount - 0.01, rimAmount + 0.01, rimIntensity); float3 rim = rimIntensity * rimColor; - float3 color = albedo * (light + specular + rim); + float shadow = ComputeShadow(worldPosition, N, L); + float3 color = albedo * (light + specular + rim) * shadow; return float4(color, 1.0); } diff --git a/Renderer.cs b/Renderer.cs index 4861f2d..a67e3b8 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -195,11 +195,7 @@ namespace Kav GraphicsDevice.Clear(Color.Black); AmbientLightRender(ambientLight); - - Deferred_ToonEffect.GPosition = gPosition; - Deferred_ToonEffect.GAlbedo = gAlbedo; - Deferred_ToonEffect.GNormal = gNormal; - DirectionalLightToonRender(camera, directionalLight); + DirectionalLightToonRender(camera, modelTransforms, directionalLight); GraphicsDevice.SetRenderTarget(null); GraphicsDevice.Clear(Color.Black); @@ -298,28 +294,7 @@ namespace Kav 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)); - - // 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; - } + RenderShadows(camera, modelTransforms, directionalLight, DeferredDirectionalLightEffect); DeferredDirectionalLightEffect.GPosition = gPosition; DeferredDirectionalLightEffect.GAlbedo = gAlbedo; @@ -345,7 +320,7 @@ namespace Kav directionalLight.Color.ToVector3() * directionalLight.Intensity; DeferredDirectionalLightEffect.ViewMatrix = camera.View; - DeferredDirectionalLightEffect.EyePosition = Matrix.Invert(camera.View).Translation; + DeferredDirectionalLightEffect.EyePosition = camera.Position; GraphicsDevice.SetRenderTarget(ColorRenderTarget); GraphicsDevice.BlendState = BlendState.Additive; @@ -360,11 +335,18 @@ namespace Kav private void DirectionalLightToonRender( PerspectiveCamera camera, + IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight ) { + RenderShadows(camera, modelTransforms, directionalLight, Deferred_ToonEffect); + GraphicsDevice.SetRenderTarget(ColorRenderTarget); GraphicsDevice.BlendState = BlendState.Additive; + Deferred_ToonEffect.GPosition = gPosition; + Deferred_ToonEffect.GAlbedo = gAlbedo; + Deferred_ToonEffect.GNormal = gNormal; + Deferred_ToonEffect.EyePosition = camera.Position; Deferred_ToonEffect.DirectionalLightDirection = directionalLight.Direction; Deferred_ToonEffect.DirectionalLightColor = @@ -372,6 +354,22 @@ namespace Kav Deferred_ToonEffect.Softness = 0.01f; + Deferred_ToonEffect.ShadowMapOne = ShadowRenderTargets[0]; + if (NumShadowCascades > 1) + { + Deferred_ToonEffect.ShadowMapTwo = ShadowRenderTargets[1]; + } + if (NumShadowCascades > 2) + { + Deferred_ToonEffect.ShadowMapThree = ShadowRenderTargets[2]; + } + if (NumShadowCascades > 3) + { + Deferred_ToonEffect.ShadowMapFour = ShadowRenderTargets[3]; + } + + Deferred_ToonEffect.ViewMatrix = camera.View; + foreach (EffectPass pass in Deferred_ToonEffect.CurrentTechnique.Passes) { pass.Apply(); @@ -380,10 +378,42 @@ namespace Kav } } + private void RenderShadows( + PerspectiveCamera camera, + IEnumerable<(Model, Matrix)> modelTransforms, + DirectionalLight directionalLight, + ShadowCascadeEffect effect + ) { + // 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)); + + // 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, effect, i); + + effect.CascadeFarPlanes[i] = farPlane; + previousFarPlane = farPlane; + } + } + private void RenderShadowMap( PerspectiveCamera camera, IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight, + ShadowCascadeEffect effect, int shadowCascadeIndex ) { GraphicsDevice.SetRenderTarget(ShadowRenderTargets[shadowCascadeIndex]); @@ -424,23 +454,21 @@ namespace Kav if (shadowCascadeIndex == 0) { - DeferredDirectionalLightEffect.LightSpaceMatrixOne = lightSpaceMatrix; + effect.LightSpaceMatrixOne = lightSpaceMatrix; } else if (shadowCascadeIndex == 1) { - DeferredDirectionalLightEffect.LightSpaceMatrixTwo = lightSpaceMatrix; + effect.LightSpaceMatrixTwo = lightSpaceMatrix; } else if (shadowCascadeIndex == 2) { - DeferredDirectionalLightEffect.LightSpaceMatrixThree = lightSpaceMatrix; + effect.LightSpaceMatrixThree = lightSpaceMatrix; } else if (shadowCascadeIndex == 3) { - DeferredDirectionalLightEffect.LightSpaceMatrixFour = lightSpaceMatrix; + effect.LightSpaceMatrixFour = lightSpaceMatrix; } - DeferredDirectionalLightEffect.CascadeFarPlanes[shadowCascadeIndex] = camera.FarPlane; - foreach (var (model, transform) in modelTransforms) { foreach (var modelMesh in model.Meshes) -- 2.25.1 From a914c586b2b01dc2d103551365a9025326e77e3e Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Fri, 2 Oct 2020 17:29:20 -0700 Subject: [PATCH 05/11] experiment with color banding --- Effects/Deferred_ToonEffect.cs | 8 ------- Effects/FXB/Deferred_ToonEffect.fxb | 4 ++-- Effects/HLSL/Deferred_ToonEffect.fx | 33 ++++++++++++++++++----------- Renderer.cs | 2 -- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/Effects/Deferred_ToonEffect.cs b/Effects/Deferred_ToonEffect.cs index a55b678..f5d2bfe 100644 --- a/Effects/Deferred_ToonEffect.cs +++ b/Effects/Deferred_ToonEffect.cs @@ -18,8 +18,6 @@ namespace Kav EffectParameter directionalLightDirectionParam; EffectParameter directionalLightColorParam; - EffectParameter softnessParam; - EffectParameter cascadeFarPlanesParam; EffectParameter shadowMapSizeParam; @@ -43,8 +41,6 @@ namespace Kav public Vector3 DirectionalLightDirection { get; set; } public Vector3 DirectionalLightColor { get; set; } - public float Softness { get; set; } - public float[] CascadeFarPlanes { get; } public float ShadowMapSize { get; set; } @@ -76,8 +72,6 @@ namespace Kav directionalLightDirectionParam.SetValue(DirectionalLightDirection); directionalLightColorParam.SetValue(DirectionalLightColor); - softnessParam.SetValue(Softness); - cascadeFarPlanesParam.SetValue(CascadeFarPlanes); shadowMapSizeParam.SetValue(ShadowMapSize); @@ -104,8 +98,6 @@ namespace Kav directionalLightDirectionParam = Parameters["DirectionalLightDirection"]; directionalLightColorParam = Parameters["DirectionalLightColor"]; - softnessParam = Parameters["Softness"]; - cascadeFarPlanesParam = Parameters["CascadeFarPlanes"]; shadowMapSizeParam = Parameters["ShadowMapSize"]; diff --git a/Effects/FXB/Deferred_ToonEffect.fxb b/Effects/FXB/Deferred_ToonEffect.fxb index f40c541..bcd420f 100644 --- a/Effects/FXB/Deferred_ToonEffect.fxb +++ b/Effects/FXB/Deferred_ToonEffect.fxb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f1008f0dd9ab943b47e548d9f61d122e44785f7fbb9ea47b7d73448af681016 -size 21180 +oid sha256:725d85ea8bb4ad3016dadae76f6da73792b3f46f99facdd5fe91a5666cc3a13a +size 21072 diff --git a/Effects/HLSL/Deferred_ToonEffect.fx b/Effects/HLSL/Deferred_ToonEffect.fx index 9da8035..6806710 100644 --- a/Effects/HLSL/Deferred_ToonEffect.fx +++ b/Effects/HLSL/Deferred_ToonEffect.fx @@ -18,8 +18,6 @@ float3 EyePosition _ps(c0) _cb(c0); float3 DirectionalLightDirection _ps(c1) _cb(c1); float3 DirectionalLightColor _ps(c2) _cb(c2); -float Softness _ps(c3) _cb(c3); - float CascadeFarPlanes[NUM_SHADOW_CASCADES] _ps(c4) _cb(c4); float ShadowMapSize _ps(c8) _cb(c8); @@ -138,6 +136,26 @@ float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L) } } +float IntensityBanding(float NdotL) +{ + if (NdotL > 0.5) + { + return 1.0; + } + else if (NdotL > 0.25) + { + return 0.5; + } + else if (NdotL > 0.0) + { + return 0.25; + } + else + { + return 0.0; + } +} + // FIXME: organize this float4 main_ps(PixelInput input) : SV_TARGET0 { @@ -153,16 +171,7 @@ float4 main_ps(PixelInput input) : SV_TARGET0 float NdotL = dot(N, L); float NdotH = max(dot(N, H), 0.0); - float lightIntensity; - if (Softness > 0.0) - { - lightIntensity = smoothstep(0, Softness, NdotL); - } - else - { - lightIntensity = (NdotL > 0.0) ? 1.0 : 0.0; - } - + float lightIntensity = IntensityBanding(NdotL); float3 light = lightIntensity * DirectionalLightColor; float specularIntensity = pow(NdotH * lightIntensity, 32 * 32); diff --git a/Renderer.cs b/Renderer.cs index a67e3b8..e8b2bf8 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -352,8 +352,6 @@ namespace Kav Deferred_ToonEffect.DirectionalLightColor = directionalLight.Color.ToVector3() * directionalLight.Intensity; - Deferred_ToonEffect.Softness = 0.01f; - Deferred_ToonEffect.ShadowMapOne = ShadowRenderTargets[0]; if (NumShadowCascades > 1) { -- 2.25.1 From 1f10698811a57b11d343dee62a47ec1b5610b626 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Mon, 5 Oct 2020 15:45:10 -0700 Subject: [PATCH 06/11] more toon shader control + dithered shading --- Effects/Deferred_ToonEffect.cs | 4 ++ Effects/FXB/Deferred_ToonEffect.fxb | 4 +- Effects/HLSL/Conversion.fxh | 34 +++++++++++ Effects/HLSL/Deferred_ToonEffect.fx | 68 +++++++++++++++------- Effects/HLSL/Dither.fxh | 89 +++++++++++++++++++++++++++++ Effects/HLSL/Shadow.fxh | 35 ++++++++++++ Geometry/Model.cs | 31 +++++++++- Renderer.cs | 1 + 8 files changed, 240 insertions(+), 26 deletions(-) create mode 100644 Effects/HLSL/Conversion.fxh create mode 100644 Effects/HLSL/Dither.fxh diff --git a/Effects/Deferred_ToonEffect.cs b/Effects/Deferred_ToonEffect.cs index f5d2bfe..1b1deea 100644 --- a/Effects/Deferred_ToonEffect.cs +++ b/Effects/Deferred_ToonEffect.cs @@ -8,6 +8,7 @@ namespace Kav EffectParameter gPositionParam; EffectParameter gAlbedoParam; EffectParameter gNormalParam; + EffectParameter gMetallicRoughnessParam; EffectParameter shadowMapOneParam; EffectParameter shadowMapTwoParam; @@ -31,6 +32,7 @@ namespace Kav 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; } @@ -62,6 +64,7 @@ namespace Kav gPositionParam.SetValue(GPosition); gAlbedoParam.SetValue(GAlbedo); gNormalParam.SetValue(GNormal); + gMetallicRoughnessParam.SetValue(GMetallicRoughness); shadowMapOneParam.SetValue(ShadowMapOne); shadowMapTwoParam.SetValue(ShadowMapTwo); @@ -88,6 +91,7 @@ namespace Kav gPositionParam = Parameters["gPosition"]; gAlbedoParam = Parameters["gAlbedo"]; gNormalParam = Parameters["gNormal"]; + gMetallicRoughnessParam = Parameters["gMetallicRoughness"]; shadowMapOneParam = Parameters["shadowMapOne"]; shadowMapTwoParam = Parameters["shadowMapTwo"]; diff --git a/Effects/FXB/Deferred_ToonEffect.fxb b/Effects/FXB/Deferred_ToonEffect.fxb index bcd420f..685bdea 100644 --- a/Effects/FXB/Deferred_ToonEffect.fxb +++ b/Effects/FXB/Deferred_ToonEffect.fxb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:725d85ea8bb4ad3016dadae76f6da73792b3f46f99facdd5fe91a5666cc3a13a -size 21072 +oid sha256:1d94e2b3520ef29a3de94389bb42b949f07af8e19cbac1fdd170e6ae116f4142 +size 7344 diff --git a/Effects/HLSL/Conversion.fxh b/Effects/HLSL/Conversion.fxh new file mode 100644 index 0000000..3d828bc --- /dev/null +++ b/Effects/HLSL/Conversion.fxh @@ -0,0 +1,34 @@ +float3 HUEtoRGB(in float H) +{ + float R = abs(H * 6 - 3) - 1; + float G = 2 - abs(H * 6 - 2); + float B = 2 - abs(H * 6 - 4); + return saturate(float3(R,G,B)); +} + +float Epsilon = 1e-10; + +float3 RGBtoHCV(in float3 RGB) +{ + // Based on work by Sam Hocevar and Emil Persson + float4 P = (RGB.g < RGB.b) ? float4(RGB.bg, -1.0, 2.0/3.0) : float4(RGB.gb, 0.0, -1.0/3.0); + float4 Q = (RGB.r < P.x) ? float4(P.xyw, RGB.r) : float4(RGB.r, P.yzx); + float C = Q.x - min(Q.w, Q.y); + float H = abs((Q.w - Q.y) / (6 * C + Epsilon) + Q.z); + return float3(H, C, Q.x); +} + +float3 RGBtoHSL(float3 RGB) +{ + float3 HCV = RGBtoHCV(RGB); + float L = HCV.z - HCV.y * 0.5; + float S = HCV.y / (1 - abs(L * 2 - 1) + Epsilon); + return float3(HCV.x, S, L); +} + +float3 HSLtoRGB(float3 HSL) +{ + float3 RGB = HUEtoRGB(HSL.x); + float C = (1 - abs(2 * HSL.z - 1)) * HSL.y; + return (RGB - 0.5) * C + HSL.z; +} diff --git a/Effects/HLSL/Deferred_ToonEffect.fx b/Effects/HLSL/Deferred_ToonEffect.fx index 6806710..3d8ac3f 100644 --- a/Effects/HLSL/Deferred_ToonEffect.fx +++ b/Effects/HLSL/Deferred_ToonEffect.fx @@ -1,11 +1,13 @@ #include "Macros.fxh" #include "Shadow.fxh" +#include "Dither.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); @@ -88,11 +90,9 @@ float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L) lightSpaceMatrix = LightSpaceMatrixFour; } - // PCF + Poisson soft shadows - if (shadowCascadeIndex == 0) { - return PoissonShadow( + return HardShadow( positionWorldSpace, N, L, @@ -103,7 +103,7 @@ float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L) } else if (shadowCascadeIndex == 1) { - return PoissonShadow( + return HardShadow( positionWorldSpace, N, L, @@ -114,7 +114,7 @@ float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L) } else if (shadowCascadeIndex == 2) { - return PoissonShadow( + return HardShadow( positionWorldSpace, N, L, @@ -125,7 +125,7 @@ float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L) } else { - return PoissonShadow( + return HardShadow( positionWorldSpace, N, L, @@ -138,30 +138,43 @@ float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L) float IntensityBanding(float NdotL) { - if (NdotL > 0.5) + // if (NdotL > 0.5) + // { + // return 1.0; + // } + // else if (NdotL > 0.25) + // { + // return 0.5; + // } + // else if (NdotL > 0.0) + // { + // return 0.25; + // } + // else + // { + // return 0.0; + // } + if (NdotL > 0) { return 1.0; } - else if (NdotL > 0.25) - { - return 0.5; - } - else if (NdotL > 0.0) - { - return 0.25; - } else { - return 0.0; + return 0.25; } } // FIXME: organize this float4 main_ps(PixelInput input) : SV_TARGET0 { + float2 screenPosition = input.Position.xy; 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; + + // the lower the glossiness, the sharper the specular highlight + float glossiness = lerp(64, 16, 1.0 - metallicRoughness.r); float3 V = normalize(EyePosition - worldPosition); float3 L = normalize(DirectionalLightDirection); @@ -172,23 +185,36 @@ float4 main_ps(PixelInput input) : SV_TARGET0 float NdotH = max(dot(N, H), 0.0); float lightIntensity = IntensityBanding(NdotL); - float3 light = lightIntensity * DirectionalLightColor; + //float3 light = lightIntensity * DirectionalLightColor; + float3 light = DirectionalLightColor; - float specularIntensity = pow(NdotH * lightIntensity, 32 * 32); + if (lightIntensity < 1) + { + light *= dither(lightIntensity, screenPosition); + } + + float specularIntensity = pow(NdotH * lightIntensity, glossiness * glossiness); float specularSmooth = smoothstep(0.005, 0.01, specularIntensity); float3 specular = specularSmooth * float3(1.0, 1.0, 1.0); - float rimColor = float3(1.0, 1.0, 1.0); + if (metallicRoughness.r == 0.0) { specular = float3(0.0, 0.0, 0.0); } + + float3 rimColor = float3(1.0, 1.0, 1.0); float rimThreshold = 0.1; - float rimAmount = 0.76; + float rimAmount = 1 - metallicRoughness.g; float rimDot = 1 - dot(V, N); float rimIntensity = rimDot * pow(max(NdotL, 0.0), rimThreshold); rimIntensity = smoothstep(rimAmount - 0.01, rimAmount + 0.01, rimIntensity); float3 rim = rimIntensity * rimColor; float shadow = ComputeShadow(worldPosition, N, L); - float3 color = albedo * (light + specular + rim) * shadow; + float3 color = albedo * (light + specular + rim); // * shadow; + + if (shadow < 1) + { + color *= dither(shadow, screenPosition); + } return float4(color, 1.0); } diff --git a/Effects/HLSL/Dither.fxh b/Effects/HLSL/Dither.fxh new file mode 100644 index 0000000..e0cf119 --- /dev/null +++ b/Effects/HLSL/Dither.fxh @@ -0,0 +1,89 @@ +#include "Conversion.fxh" + +// technique from http://alex-charlton.com/posts/Dithering_on_the_GPU/ + +uniform float3 palette[8]; +static const int paletteSize = 8; + +static const int indexMatrix4x4[16] = +{ + 0, 8, 2, 10, + 12, 4, 14, 6, + 3, 11, 1, 9, + 15, 7, 13, 5 +}; + +float indexValue(float2 screenCoords) +{ + int x = int(screenCoords.x % 4); + int y = int(screenCoords.y % 4); + + return indexMatrix4x4[(x + y * 4)] / 16.0; +} + +float hueDistance(float h1, float h2) +{ + float diff = abs(h1 - h2); + return min(abs(1.0 - diff), diff); +} + +void closestColors(float hue, out float3 ret[2]) +{ + float3 closest = float3(-2, 0, 0); + float3 secondClosest = float3(-2, 0, 0); + float3 temp; + + for (int i = 0; i < paletteSize; i++) + { + temp = palette[i]; + float tempDistance = hueDistance(temp.x, hue); + if (tempDistance < hueDistance(closest.x, hue)) + { + secondClosest = closest; + closest = temp; + } + else + { + if (tempDistance < hueDistance(secondClosest.x, hue)) + { + secondClosest = temp; + } + } + } + + ret[0] = closest; + ret[1] = secondClosest; +} + +float3 dither(float3 color, float2 screenCoords) +{ + float3 colors[2]; + float3 hsl = RGBtoHSL(color); + closestColors(hsl.x, colors); + float3 closestColor = colors[0]; + float3 secondClosestColor = colors[1]; + float d = indexValue(screenCoords); + float hueDiff = hueDistance(hsl.x, closestColor.x) / hueDistance(secondClosestColor.x, secondClosestColor.x); + return HSLtoRGB(hueDiff < d ? closestColor : secondClosestColor); +} + +// brightColor refers to undithered max color +// float3 dither(float3 color, float3 brightColor, float2 screenCoords) +// { +// float brightHue = RGBtoHSL(brightColor.x); +// float colorHue = RGBtoHSL(color.x); +// float halfDistance = hueDistance(0.0, brightHue) / 2.0; +// float3 closestColor = (colorHue < halfDistance) ? float3(0.0, 0.0, 0.0) : brightColor; +// float3 secondClosestColor = brightColor - closestColor; +// float d = indexValue(screenCoords); +// float distance = abs(closestColor - color); +// return (distance < d) ? closestColor : secondClosestColor; +// } + +float3 dither(float color, float2 screenCoords) { + float closestColor = (color < 0.5) ? 0 : 1; + float secondClosestColor = 1 - closestColor; + float d = indexValue(screenCoords); + float distance = abs(closestColor - color); + return (distance < d) ? closestColor : secondClosestColor; +} diff --git a/Effects/HLSL/Shadow.fxh b/Effects/HLSL/Shadow.fxh index 38efc3e..47af839 100644 --- a/Effects/HLSL/Shadow.fxh +++ b/Effects/HLSL/Shadow.fxh @@ -85,3 +85,38 @@ float PoissonShadow( return visibility; } + +float HardShadow( + float3 positionWorldSpace, + float3 N, + float3 L, + float4x4 lightSpaceMatrix, + sampler shadowMap, + int shadowMapSize +) { + // float bias = 0.005 * tan(acos(dot(N, L))); + // bias = clamp(bias, 0, 0.01); + + float bias = max(0.05 * (1.0 - dot(N, L)), 0.005); + + float4 positionLightSpace = mul(float4(positionWorldSpace, 1.0), lightSpaceMatrix); + + // maps to [-1, 1] + float3 projectionCoords = positionLightSpace.xyz / positionLightSpace.w; + + // maps to [0, 1] + // NOTE: In XNA, clip space z is [0, 1] range + projectionCoords.x = (projectionCoords.x * 0.5) + 0.5; + projectionCoords.y = (projectionCoords.y * 0.5) + 0.5; + projectionCoords.y *= -1; + + if (projectionCoords.z > 1.0) + { + return 1.0; + } + + float closestDepth = tex2D(shadowMap, projectionCoords.xy); + float currentDepth = projectionCoords.z; + + return (currentDepth - bias > closestDepth ? 0.25 : 1.0); +} diff --git a/Geometry/Model.cs b/Geometry/Model.cs index e9ad690..9ab1dc0 100644 --- a/Geometry/Model.cs +++ b/Geometry/Model.cs @@ -6,11 +6,8 @@ namespace Kav { public Mesh[] Meshes { get; } - private Color albedoValue; - public Color Albedo { - get { return albedoValue; } set { foreach (var mesh in Meshes) @@ -23,6 +20,34 @@ namespace Kav } } + public float Metallic + { + set + { + foreach (var mesh in Meshes) + { + foreach (var meshPart in mesh.MeshParts) + { + meshPart.Metallic = value; + } + } + } + } + + public float Roughness + { + set + { + foreach (var mesh in Meshes) + { + foreach (var meshPart in mesh.MeshParts) + { + meshPart.Roughness = value; + } + } + } + } + public Model(Mesh[] meshes) { Meshes = meshes; diff --git a/Renderer.cs b/Renderer.cs index e8b2bf8..747d88b 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -346,6 +346,7 @@ namespace Kav Deferred_ToonEffect.GPosition = gPosition; Deferred_ToonEffect.GAlbedo = gAlbedo; Deferred_ToonEffect.GNormal = gNormal; + Deferred_ToonEffect.GMetallicRoughness = gMetallicRoughness; Deferred_ToonEffect.EyePosition = camera.Position; Deferred_ToonEffect.DirectionalLightDirection = directionalLight.Direction; -- 2.25.1 From 19a61985ca25a7562a7f62e03186f0ccfdc1c8ef Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Thu, 15 Oct 2020 18:19:43 -0700 Subject: [PATCH 07/11] support dithered shadow toggle --- Effects/Deferred_ToonEffect.cs | 31 +++++++++++++++ Effects/FXB/Deferred_ToonEffect.fxb | 4 +- Effects/HLSL/Deferred_ToonEffect.fx | 60 ++++++++++++++++++++++++++++- Renderer.cs | 2 + 4 files changed, 93 insertions(+), 4 deletions(-) diff --git a/Effects/Deferred_ToonEffect.cs b/Effects/Deferred_ToonEffect.cs index 1b1deea..031f8e7 100644 --- a/Effects/Deferred_ToonEffect.cs +++ b/Effects/Deferred_ToonEffect.cs @@ -29,6 +29,8 @@ namespace Kav EffectParameter viewMatrixParam; + EffectParameter shaderIndexParam; + public Texture2D GPosition { get; set; } public Texture2D GAlbedo { get; set; } public Texture2D GNormal { get; set; } @@ -53,6 +55,19 @@ namespace Kav public Matrix ViewMatrix { get; set; } + private bool ditheredShadowValue = false; + public bool DitheredShadows + { + get { return ditheredShadowValue; } + set + { + ditheredShadowValue = value; + CalculateShaderIndex(); + } + } + + private int shaderIndex = 0; + public Deferred_ToonEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.Deferred_ToonEffect) { CascadeFarPlanes = new float[4]; @@ -84,6 +99,8 @@ namespace Kav lightSpaceMatrixFourParam.SetValue(LightSpaceMatrixFour); viewMatrixParam.SetValue(ViewMatrix); + + shaderIndexParam.SetValue(shaderIndex); } void CacheEffectParameters() @@ -111,6 +128,20 @@ namespace Kav lightSpaceMatrixFourParam = Parameters["LightSpaceMatrixFour"]; viewMatrixParam = Parameters["ViewMatrix"]; + + shaderIndexParam = Parameters["ShaderIndex"]; + } + + private void CalculateShaderIndex() + { + if (ditheredShadowValue) + { + shaderIndex = 1; + } + else + { + shaderIndex = 0; + } } } } diff --git a/Effects/FXB/Deferred_ToonEffect.fxb b/Effects/FXB/Deferred_ToonEffect.fxb index 685bdea..529d086 100644 --- a/Effects/FXB/Deferred_ToonEffect.fxb +++ b/Effects/FXB/Deferred_ToonEffect.fxb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1d94e2b3520ef29a3de94389bb42b949f07af8e19cbac1fdd170e6ae116f4142 -size 7344 +oid sha256:78552dce830d09b83694863355e51d4b2eaa31fafb11a16a207f8b42a6d004e5 +size 10964 diff --git a/Effects/HLSL/Deferred_ToonEffect.fx b/Effects/HLSL/Deferred_ToonEffect.fx index 3d8ac3f..62fbcad 100644 --- a/Effects/HLSL/Deferred_ToonEffect.fx +++ b/Effects/HLSL/Deferred_ToonEffect.fx @@ -164,8 +164,51 @@ float IntensityBanding(float NdotL) } } +float4 FlatShadow(PixelInput input) : SV_TARGET0 +{ + float2 screenPosition = input.Position.xy; + 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; + + // the lower the glossiness, the sharper the specular highlight + float glossiness = lerp(64, 16, 1.0 - metallicRoughness.r); + + float3 V = normalize(EyePosition - worldPosition); + float3 L = normalize(DirectionalLightDirection); + float3 N = normalize(normal); + float3 H = normalize(V + L); + + float NdotL = dot(N, L); + float NdotH = max(dot(N, H), 0.0); + + float lightIntensity = IntensityBanding(NdotL); + float3 light = lightIntensity * DirectionalLightColor; + + float specularIntensity = pow(NdotH * lightIntensity, glossiness * glossiness); + float specularSmooth = smoothstep(0.005, 0.01, specularIntensity); + + float3 specular = specularSmooth * float3(1.0, 1.0, 1.0); + + if (metallicRoughness.r == 0.0) { specular = float3(0.0, 0.0, 0.0); } + + float3 rimColor = float3(1.0, 1.0, 1.0); + float rimThreshold = 0.1; + float rimAmount = 1 - metallicRoughness.g; + float rimDot = 1 - dot(V, N); + float rimIntensity = rimDot * pow(max(NdotL, 0.0), rimThreshold); + rimIntensity = smoothstep(rimAmount - 0.01, rimAmount + 0.01, rimIntensity); + float3 rim = rimIntensity * rimColor; + + float shadow = ComputeShadow(worldPosition, N, L); + float3 color = albedo * (light + specular + rim) * shadow; + + return float4(color, 1.0); +} + // FIXME: organize this -float4 main_ps(PixelInput input) : SV_TARGET0 +float4 DitheredShadow(PixelInput input) : SV_TARGET0 { float2 screenPosition = input.Position.xy; float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb; @@ -219,11 +262,24 @@ float4 main_ps(PixelInput input) : SV_TARGET0 return float4(color, 1.0); } +PixelShader PSArray[2] = +{ + compile ps_3_0 FlatShadow(), + compile ps_3_0 DitheredShadow() +}; + +int PSIndices[2] = +{ + 0, 1 +}; + +int ShaderIndex = 0; + Technique Deferred_Toon { Pass { VertexShader = compile vs_3_0 main_vs(); - PixelShader = compile ps_3_0 main_ps(); + PixelShader = (PSArray[PSIndices[ShaderIndex]]); } } diff --git a/Renderer.cs b/Renderer.cs index 747d88b..d48a36a 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -348,6 +348,8 @@ namespace Kav Deferred_ToonEffect.GNormal = gNormal; Deferred_ToonEffect.GMetallicRoughness = gMetallicRoughness; + Deferred_ToonEffect.DitheredShadows = true; + Deferred_ToonEffect.EyePosition = camera.Position; Deferred_ToonEffect.DirectionalLightDirection = directionalLight.Direction; Deferred_ToonEffect.DirectionalLightColor = -- 2.25.1 From 06e552399600be4b706249bab54740eae4fd8e6b Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Sat, 17 Oct 2020 13:53:26 -0700 Subject: [PATCH 08/11] deferred skybox --- Effects/FXB/SkyboxEffect.fxb | 3 ++ Effects/HLSL/SkyboxEffect.fx | 45 ++++++++++++++++ Effects/SkyboxEffect.cs | 77 +++++++++++++++++++++++++++ Kav.Core.csproj | 6 +++ Kav.Framework.csproj | 6 +++ Models/UnitCube.glb | Bin 0 -> 1684 bytes Renderer.cs | 98 ++++++++++++++++++++++++++++++++--- Resources.cs | 47 +++++++++++++---- 8 files changed, 264 insertions(+), 18 deletions(-) create mode 100644 Effects/FXB/SkyboxEffect.fxb create mode 100644 Effects/HLSL/SkyboxEffect.fx create mode 100644 Effects/SkyboxEffect.cs create mode 100644 Models/UnitCube.glb diff --git a/Effects/FXB/SkyboxEffect.fxb b/Effects/FXB/SkyboxEffect.fxb new file mode 100644 index 0000000..10d5b22 --- /dev/null +++ b/Effects/FXB/SkyboxEffect.fxb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:847ffdb1472a2c6ca32b7bbf0f41e2b972d2435d8fe4d72e82d97bc8c88b4b59 +size 1072 diff --git a/Effects/HLSL/SkyboxEffect.fx b/Effects/HLSL/SkyboxEffect.fx new file mode 100644 index 0000000..88ac7f3 --- /dev/null +++ b/Effects/HLSL/SkyboxEffect.fx @@ -0,0 +1,45 @@ +#include "Macros.fxh" + +DECLARE_CUBEMAP(skybox, 0); + +BEGIN_CONSTANTS + + float4x4 ViewProjection _vs(c0) _cb(c0); + +END_CONSTANTS + +struct VertexShaderInput +{ + float3 Position : POSITION; +}; + +struct VertexShaderOutput +{ + float4 Position : SV_POSITION; + float3 TexCoord : TEXCOORD; +}; + +VertexShaderOutput main_vs(VertexShaderInput input) +{ + VertexShaderOutput output; + + output.Position = mul(float4(input.Position, 1.0), ViewProjection); + output.Position = output.Position.xyww; + output.TexCoord = input.Position; + + return output; +} + +float4 main_ps(VertexShaderOutput input) : SV_TARGET0 +{ + return SAMPLE_CUBEMAP(skybox, input.TexCoord); +} + +Technique Skybox +{ + Pass + { + VertexShader = compile vs_3_0 main_vs(); + PixelShader = compile ps_3_0 main_ps(); + } +} diff --git a/Effects/SkyboxEffect.cs b/Effects/SkyboxEffect.cs new file mode 100644 index 0000000..b4f4427 --- /dev/null +++ b/Effects/SkyboxEffect.cs @@ -0,0 +1,77 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + public class SkyboxEffect : Effect + { + EffectParameter viewProjectionParam; + EffectParameter skyboxParam; + + Matrix view; + Matrix projection; + TextureCube skybox; + + EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All; + + public Matrix View + { + get { return view; } + set + { + view = value; + dirtyFlags |= EffectDirtyFlags.WorldViewProj; + } + } + + public Matrix Projection + { + get { return projection; } + set + { + projection = value; + dirtyFlags |= EffectDirtyFlags.WorldViewProj; + } + } + + public TextureCube Skybox + { + get { return skybox; } + set + { + skybox = value; + dirtyFlags |= EffectDirtyFlags.World; // hack flag but whatever + } + } + + public SkyboxEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.SkyboxEffect) + { + CacheEffectParameters(); + } + + protected override void OnApply() + { + if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0) + { + Matrix.Multiply(ref view, ref projection, out Matrix viewProjection); + + viewProjectionParam.SetValue(viewProjection); + + dirtyFlags &= ~EffectDirtyFlags.WorldViewProj; + } + + if ((dirtyFlags & EffectDirtyFlags.World) != 0) + { + skyboxParam.SetValue(skybox); + + dirtyFlags &= ~EffectDirtyFlags.World; + } + } + + private void CacheEffectParameters() + { + viewProjectionParam = Parameters["ViewProjection"]; + skyboxParam = Parameters["skybox"]; + } + } +} diff --git a/Kav.Core.csproj b/Kav.Core.csproj index b018e72..fc78de4 100644 --- a/Kav.Core.csproj +++ b/Kav.Core.csproj @@ -42,6 +42,12 @@ Kav.Resources.SimpleDepthEffect.fxb + + Kav.Resources.SkyboxEffect.fxb + + + Kav.Resources.UnitCube.glb + diff --git a/Kav.Framework.csproj b/Kav.Framework.csproj index c2d9079..4b72a58 100644 --- a/Kav.Framework.csproj +++ b/Kav.Framework.csproj @@ -42,6 +42,12 @@ Kav.Resources.SimpleDepthEffect.fxb + + Kav.Resources.SkyboxEffect.fxb + + + Kav.Resources.UnitCube.glb + diff --git a/Models/UnitCube.glb b/Models/UnitCube.glb new file mode 100644 index 0000000000000000000000000000000000000000..aa53f03fbf6f8808cb355c0302381eb28e1ee265 GIT binary patch literal 1684 zcmb7FZEq4m5FT5tTD5A``jyP*6OW_3cptPBYebPVKpK;#(E}EGNx36;2ig!4euV!= zf0{GPE?lsoaLMd5v(GcLGkZtqT{b^#0l@Da0COAQQ^To^(TySzqdmr+@I~mxL5Mbf zz6b+9h*CXDNgZDbzazp_<+YO8>` zX%8EcGtdt@f(i3{a=wVh*cZ`-OjY{wuqBWsOvsFRDW=ki`w@5j@Rat>_j%@wke8&aMpio z*zJn&)Du5top#V41itX&=GB1a%4Mv)f$d=E(+ygO)UNxBr%UGyWjumMmvr`uH|7g% z$Fc&Ro|Ln6dt*$VL~Y)5(yDZ8lXUC9)3ev5%S}0FmPS^mH_D}IsV-mCqW$E>$xCZp z#iA1PIK%po8}mk#yTCdziF&N@`O!M&Sr00Yk@d`LWM1nSdH+WOnZE`6U$O<;a2xKx4y`+I7w*A* k*rjzB9>7C*1bejZ!DDy=Php?deRu}X;RPJfdH^rsABk>Wv;Y7A literal 0 HcmV?d00001 diff --git a/Renderer.cs b/Renderer.cs index d48a36a..2e11a73 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.IO; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; @@ -29,6 +30,7 @@ namespace Kav private Deferred_ToonEffect Deferred_ToonEffect { get; } private SimpleDepthEffect SimpleDepthEffect { get; } private Effect ToneMapEffect { get; } + private SkyboxEffect SkyboxEffect { get; } private RenderTarget2D gPosition { get; } private RenderTarget2D gNormal { get; } @@ -37,6 +39,8 @@ namespace Kav private RenderTargetBinding[] GBuffer { get; } + private Kav.Model UnitCube { get; } + private SpriteBatch SpriteBatch { get; } public Renderer( @@ -73,7 +77,7 @@ namespace Kav renderDimensionsY, false, SurfaceFormat.Color, - DepthFormat.None, + DepthFormat.Depth24, 0, RenderTargetUsage.PreserveContents ); @@ -142,6 +146,7 @@ namespace Kav DeferredDirectionalLightEffect.ShadowMapSize = ShadowMapSize; ToneMapEffect = new Effect(graphicsDevice, Resources.ToneMapEffect); Deferred_ToonEffect = new Deferred_ToonEffect(GraphicsDevice); + SkyboxEffect = new SkyboxEffect(GraphicsDevice); FullscreenTriangle = new VertexBuffer(GraphicsDevice, typeof(VertexPositionTexture), 3, BufferUsage.WriteOnly); FullscreenTriangle.SetData(new VertexPositionTexture[3] { @@ -150,6 +155,11 @@ namespace Kav new VertexPositionTexture(new Vector3(3, 1, 0), new Vector2(2, 0)) }); + UnitCube = Kav.ModelLoader.Load( + GraphicsDevice, + Smuggler.Importer.ImportGLB(GraphicsDevice, new MemoryStream(Resources.UnitCubeModel)) + ); + SpriteBatch = new SpriteBatch(graphicsDevice); } @@ -187,23 +197,95 @@ namespace Kav PerspectiveCamera camera, IEnumerable<(Model, Matrix)> modelTransforms, AmbientLight ambientLight, - DirectionalLight directionalLight - ) { + DirectionalLight directionalLight, + TextureCube skybox + ) { GBufferRender(camera, modelTransforms); - + GraphicsDevice.SetRenderTarget(ColorRenderTarget); - GraphicsDevice.Clear(Color.Black); + GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0); + GraphicsDevice.DepthStencilState = DepthStencilState.Default; + + DepthRender(camera, modelTransforms); + GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; AmbientLightRender(ambientLight); DirectionalLightToonRender(camera, modelTransforms, directionalLight); + SkyboxRender(camera, skybox); GraphicsDevice.SetRenderTarget(null); - GraphicsDevice.Clear(Color.Black); SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, null, null, null, null); SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White); SpriteBatch.End(); } + private void DepthRender( + PerspectiveCamera camera, + IEnumerable<(Model, Matrix)> modelTransforms + ) { + foreach (var (model, transform) in modelTransforms) + { + foreach (var modelMesh in model.Meshes) + { + foreach (var meshPart in modelMesh.MeshParts) + { + SimpleDepthEffect.Model = transform; + SimpleDepthEffect.View = camera.View; + SimpleDepthEffect.Projection = camera.Projection; + + GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer); + GraphicsDevice.Indices = meshPart.IndexBuffer; + + foreach (var pass in SimpleDepthEffect.CurrentTechnique.Passes) + { + pass.Apply(); + + GraphicsDevice.DrawIndexedPrimitives( + PrimitiveType.TriangleList, + 0, + 0, + meshPart.VertexBuffer.VertexCount, + 0, + meshPart.Triangles.Length + ); + } + } + } + } + } + + private void SkyboxRender( + PerspectiveCamera camera, + TextureCube skybox + ) { + GraphicsDevice.RasterizerState.CullMode = CullMode.CullClockwiseFace; + SkyboxEffect.Skybox = skybox; + + var view = camera.View; + view.Translation = Vector3.Zero; + SkyboxEffect.View = view; + + SkyboxEffect.Projection = camera.Projection; + + GraphicsDevice.SetVertexBuffer(UnitCube.Meshes[0].MeshParts[0].VertexBuffer); + GraphicsDevice.Indices = UnitCube.Meshes[0].MeshParts[0].IndexBuffer; + + foreach (var pass in SkyboxEffect.CurrentTechnique.Passes) + { + pass.Apply(); + + GraphicsDevice.DrawIndexedPrimitives( + PrimitiveType.TriangleList, + 0, + 0, + UnitCube.Meshes[0].MeshParts[0].VertexBuffer.VertexCount, + 0, + UnitCube.Meshes[0].MeshParts[0].Triangles.Length + ); + } + GraphicsDevice.RasterizerState.CullMode = CullMode.CullCounterClockwiseFace; + } + private void GBufferRender( PerspectiveCamera camera, IEnumerable<(Model, Matrix)> modelTransforms @@ -255,8 +337,7 @@ namespace Kav private void AmbientLightRender(AmbientLight ambientLight) { GraphicsDevice.SetRenderTarget(ColorRenderTarget); - GraphicsDevice.BlendState = BlendState.Additive; - GraphicsDevice.DepthStencilState = DepthStencilState.None; + GraphicsDevice.BlendState = BlendState.Opaque; DeferredAmbientLightEffect.GPosition = gPosition; DeferredAmbientLightEffect.GAlbedo = gAlbedo; @@ -341,6 +422,7 @@ namespace Kav RenderShadows(camera, modelTransforms, directionalLight, Deferred_ToonEffect); GraphicsDevice.SetRenderTarget(ColorRenderTarget); + GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; GraphicsDevice.BlendState = BlendState.Additive; Deferred_ToonEffect.GPosition = gPosition; diff --git a/Resources.cs b/Resources.cs index 9badc01..251b820 100644 --- a/Resources.cs +++ b/Resources.cs @@ -10,7 +10,7 @@ namespace Kav { if (ambientLightEffect == null) { - ambientLightEffect = GetResource("DeferredPBR_AmbientLightEffect"); + ambientLightEffect = GetResource("DeferredPBR_AmbientLightEffect.fxb"); } return ambientLightEffect; } @@ -21,7 +21,7 @@ namespace Kav { if (pointLightEffect == null) { - pointLightEffect = GetResource("DeferredPBR_PointLightEffect"); + pointLightEffect = GetResource("DeferredPBR_PointLightEffect.fxb"); } return pointLightEffect; } @@ -33,7 +33,7 @@ namespace Kav { if (directionalLightEffect == null) { - directionalLightEffect = GetResource("DeferredPBR_DirectionalLightEffect"); + directionalLightEffect = GetResource("DeferredPBR_DirectionalLightEffect.fxb"); } return directionalLightEffect; } @@ -45,7 +45,7 @@ namespace Kav { if (gBufferEffect == null) { - gBufferEffect = GetResource("DeferredPBR_GBufferEffect"); + gBufferEffect = GetResource("DeferredPBR_GBufferEffect.fxb"); } return gBufferEffect; } @@ -57,7 +57,7 @@ namespace Kav { if (toneMapEffect == null) { - toneMapEffect = GetResource("ToneMapEffect"); + toneMapEffect = GetResource("ToneMapEffect.fxb"); } return toneMapEffect; } @@ -69,7 +69,7 @@ namespace Kav { if (deferredToonEffect == null) { - deferredToonEffect = GetResource("Deferred_ToonEffect"); + deferredToonEffect = GetResource("Deferred_ToonEffect.fxb"); } return deferredToonEffect; } @@ -81,7 +81,7 @@ namespace Kav { if (deferredPBREffect == null) { - deferredPBREffect = GetResource("DeferredPBREffect"); + deferredPBREffect = GetResource("DeferredPBREffect.fxb"); } return deferredPBREffect; } @@ -93,7 +93,7 @@ namespace Kav { if (pbrEffect == null) { - pbrEffect = GetResource("PBREffect"); + pbrEffect = GetResource("PBREffect.fxb"); } return pbrEffect; } @@ -105,12 +105,36 @@ namespace Kav { if (simpleDepthEffect == null) { - simpleDepthEffect = GetResource("SimpleDepthEffect"); + simpleDepthEffect = GetResource("SimpleDepthEffect.fxb"); } return simpleDepthEffect; } } + public static byte[] SkyboxEffect + { + get + { + if (skyboxEffect == null) + { + skyboxEffect = GetResource("SkyboxEffect.fxb"); + } + return skyboxEffect; + } + } + + public static byte[] UnitCubeModel + { + get + { + if (unitCubeModel == null) + { + unitCubeModel = GetResource("UnitCube.glb"); + } + return unitCubeModel; + } + } + private static byte[] ambientLightEffect; private static byte[] pointLightEffect; private static byte[] directionalLightEffect; @@ -120,11 +144,14 @@ namespace Kav private static byte[] deferredPBREffect; private static byte[] pbrEffect; private static byte[] simpleDepthEffect; + private static byte[] skyboxEffect; + + private static byte[] unitCubeModel; private static byte[] GetResource(string name) { Stream stream = typeof(Resources).Assembly.GetManifestResourceStream( - "Kav.Resources." + name + ".fxb" + "Kav.Resources." + name ); using (MemoryStream ms = new MemoryStream()) { -- 2.25.1 From 0a7698c315655bcabc3ef6d805bd1a4aee944e13 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Mon, 19 Oct 2020 03:01:37 -0700 Subject: [PATCH 09/11] starting on point shadows --- Effects/DeferredPBR_PointLightEffect.cs | 17 +++ Effects/FXB/DeferredPBR_PointLightEffect.fxb | 4 +- Effects/FXB/LinearDepthEffect.fxb | 3 + Effects/FXB/SimpleDepthEffect.fxb | 4 +- Effects/HLSL/DeferredPBR_PointLightEffect.fx | 8 +- Effects/HLSL/LinearDepthEffect.fx | 46 ++++++ Effects/HLSL/Shadow.fxh | 20 +++ Effects/HLSL/SimpleDepthEffect.fx | 2 +- Effects/LinearDepthEffect.cs | 90 ++++++++++++ Kav.Core.csproj | 3 + Kav.Framework.csproj | 15 +- README.md | 2 +- Renderer.cs | 146 +++++++++++++++++-- Resources.cs | 87 ++++++----- 14 files changed, 387 insertions(+), 60 deletions(-) create mode 100644 Effects/FXB/LinearDepthEffect.fxb create mode 100644 Effects/HLSL/LinearDepthEffect.fx create mode 100644 Effects/LinearDepthEffect.cs diff --git a/Effects/DeferredPBR_PointLightEffect.cs b/Effects/DeferredPBR_PointLightEffect.cs index 6f959ea..82a73ea 100644 --- a/Effects/DeferredPBR_PointLightEffect.cs +++ b/Effects/DeferredPBR_PointLightEffect.cs @@ -9,22 +9,28 @@ namespace Kav EffectParameter gAlbedoParam; EffectParameter gNormalParam; EffectParameter gMetallicRoughnessParam; + EffectParameter shadowMapParam; EffectParameter eyePositionParam; EffectParameter pointLightColorParam; EffectParameter pointLightPositionParam; + 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 PointLightPosition { get; set; } public Vector3 PointLightColor { get; set; } + public float FarPlane { get; set; } + public DeferredPBR_PointLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_PointLightEffect) { CacheEffectParameters(); @@ -32,15 +38,20 @@ namespace Kav public DeferredPBR_PointLightEffect(DeferredPBR_PointLightEffect cloneSource) : base(cloneSource) { + CacheEffectParameters(); + GPosition = cloneSource.GPosition; GAlbedo = cloneSource.GAlbedo; GNormal = cloneSource.GNormal; GMetallicRoughness = cloneSource.GMetallicRoughness; + ShadowMap = cloneSource.ShadowMap; EyePosition = cloneSource.EyePosition; PointLightPosition = cloneSource.PointLightPosition; PointLightColor = cloneSource.PointLightColor; + + FarPlane = cloneSource.FarPlane; } public override Effect Clone() @@ -54,11 +65,14 @@ namespace Kav gAlbedoParam.SetValue(GAlbedo); gNormalParam.SetValue(GNormal); gMetallicRoughnessParam.SetValue(GMetallicRoughness); + shadowMapParam.SetValue(ShadowMap); eyePositionParam.SetValue(EyePosition); pointLightPositionParam.SetValue(PointLightPosition); pointLightColorParam.SetValue(PointLightColor); + + farPlaneParam.SetValue(FarPlane); } void CacheEffectParameters() @@ -67,11 +81,14 @@ namespace Kav gAlbedoParam = Parameters["gAlbedo"]; gNormalParam = Parameters["gNormal"]; gMetallicRoughnessParam = Parameters["gMetallicRoughness"]; + shadowMapParam = Parameters["shadowMap"]; eyePositionParam = Parameters["EyePosition"]; pointLightPositionParam = Parameters["PointLightPosition"]; pointLightColorParam = Parameters["PointLightColor"]; + + farPlaneParam = Parameters["FarPlane"]; } } } diff --git a/Effects/FXB/DeferredPBR_PointLightEffect.fxb b/Effects/FXB/DeferredPBR_PointLightEffect.fxb index 398f66b..330086e 100644 --- a/Effects/FXB/DeferredPBR_PointLightEffect.fxb +++ b/Effects/FXB/DeferredPBR_PointLightEffect.fxb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e51395e0f0dd1e1b86272dde1e4fb5aea1fdf9b86399416015ad33cfb7260691 -size 3108 +oid sha256:0331c52bcbc31d3639743c487ff1b48e6e90c5c4f9134f6b2154736a9dd8d6d0 +size 3740 diff --git a/Effects/FXB/LinearDepthEffect.fxb b/Effects/FXB/LinearDepthEffect.fxb new file mode 100644 index 0000000..f805053 --- /dev/null +++ b/Effects/FXB/LinearDepthEffect.fxb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9509896b7532c6ecf77aef4e575a7bceb8a106f4ad0c1a9c11a1608c9807cc10 +size 1332 diff --git a/Effects/FXB/SimpleDepthEffect.fxb b/Effects/FXB/SimpleDepthEffect.fxb index c60bf15..8281157 100644 --- a/Effects/FXB/SimpleDepthEffect.fxb +++ b/Effects/FXB/SimpleDepthEffect.fxb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b7e52830f9c3e89d230f12b98bc42cbc3af7a488c584c9ff48cd571115d57d85 -size 832 +oid sha256:e0984ae92245afe3e2bda5fab0135293b5121c3ee7b5f146e7d4ddd1684ee74b +size 848 diff --git a/Effects/HLSL/DeferredPBR_PointLightEffect.fx b/Effects/HLSL/DeferredPBR_PointLightEffect.fx index fba3c6e..d6e1933 100644 --- a/Effects/HLSL/DeferredPBR_PointLightEffect.fx +++ b/Effects/HLSL/DeferredPBR_PointLightEffect.fx @@ -1,10 +1,12 @@ #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 @@ -13,6 +15,8 @@ BEGIN_CONSTANTS float3 PointLightPosition _ps(c1) _cb(c1); float3 PointLightColor _ps(c2) _cb(c2); + float FarPlane _ps(c3) _cb(c3); + MATRIX_CONSTANTS END_CONSTANTS @@ -60,9 +64,11 @@ float4 ComputeColor( float attenuation = 1.0 / (distance * distance); float3 radiance = PointLightColor * attenuation; - float3 color = ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, 1.0); + float shadow = HardPointShadow(worldPosition, N, L, PointLightPosition, SAMPLER(shadowMap), FarPlane); + 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/LinearDepthEffect.fx b/Effects/HLSL/LinearDepthEffect.fx new file mode 100644 index 0000000..164da5b --- /dev/null +++ b/Effects/HLSL/LinearDepthEffect.fx @@ -0,0 +1,46 @@ +#include "Macros.fxh" + +BEGIN_CONSTANTS + + float4x4 Model _vs(c0) _cb(c0); + float4x4 ModelViewProjection _vs(c4) _cb(c4); + + float3 LightPosition _ps(c0) _cb(c8); + float FarPlane _ps(c1) _cb(c9); + +END_CONSTANTS + +struct VertexShaderInput +{ + float4 Position : POSITION; +}; + +struct VertexShaderOutput +{ + float4 Position : SV_Position; + float3 PositionWorld : TEXCOORD0; +}; + +VertexShaderOutput main_vs(VertexShaderInput input) +{ + VertexShaderOutput output; + output.Position = mul(input.Position, ModelViewProjection); + output.PositionWorld = mul(input.Position, Model); + return output; +} + +float4 main_ps(VertexShaderOutput input) : SV_TARGET0 +{ + float lightDistance = length(input.PositionWorld - LightPosition); + lightDistance /= FarPlane; + return float4(lightDistance, 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/Shadow.fxh b/Effects/HLSL/Shadow.fxh index 47af839..c1bb9ba 100644 --- a/Effects/HLSL/Shadow.fxh +++ b/Effects/HLSL/Shadow.fxh @@ -120,3 +120,23 @@ float HardShadow( return (currentDepth - bias > closestDepth ? 0.25 : 1.0); } + +float HardPointShadow( + float3 positionWorldSpace, + float3 N, + float3 L, + float3 lightPosition, + sampler shadowMap, + float farPlane +) { + float3 lightToFrag = positionWorldSpace - lightPosition; + float closestDepth = texCUBE(shadowMap, lightToFrag).r; + closestDepth *= farPlane; + + float currentDepth = length(lightToFrag); + + float bias = max(0.05 * (1.0 - dot(N, L)), 0.005); + + return (currentDepth - bias > closestDepth ? 0.25 : 1.0); + //return closestDepth / farPlane; +} diff --git a/Effects/HLSL/SimpleDepthEffect.fx b/Effects/HLSL/SimpleDepthEffect.fx index 3598eb5..f813bd7 100644 --- a/Effects/HLSL/SimpleDepthEffect.fx +++ b/Effects/HLSL/SimpleDepthEffect.fx @@ -24,7 +24,7 @@ VertexShaderOutput main_vs(VertexShaderInput input) VertexShaderOutput output; output.Position = mul(input.Position, ModelViewProjection); - output.Depth = output.Position.z; + output.Depth = output.Position.z / output.Position.w; return output; } diff --git a/Effects/LinearDepthEffect.cs b/Effects/LinearDepthEffect.cs new file mode 100644 index 0000000..2a6a76a --- /dev/null +++ b/Effects/LinearDepthEffect.cs @@ -0,0 +1,90 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + public class LinearDepthEffect : Effect + { + EffectParameter modelParam; + EffectParameter modelViewProjectionParam; + EffectParameter lightPositionParam; + EffectParameter farPlaneParam; + + EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All; + + Matrix model; + Matrix view; + Matrix projection; + + public Vector3 LightPosition { get; set; } + public float FarPlane { get; set; } + + public Matrix Model + { + get { return model; } + set + { + model = value; + dirtyFlags |= EffectDirtyFlags.World; + dirtyFlags |= EffectDirtyFlags.WorldViewProj; + } + } + + public Matrix View + { + get { return view; } + set + { + view = value; + dirtyFlags |= EffectDirtyFlags.WorldViewProj; + } + } + + public Matrix Projection + { + get { return projection; } + set + { + projection = value; + dirtyFlags |= EffectDirtyFlags.WorldViewProj; + } + } + + public LinearDepthEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.LinearDepthEffect) + { + CacheEffectParameters(); + } + + protected override void OnApply() + { + if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0) + { + Matrix.Multiply(ref view, ref projection, out Matrix viewProjection); + Matrix.Multiply(ref model, ref viewProjection, out Matrix worldViewProj); + + modelViewProjectionParam.SetValue(worldViewProj); + + dirtyFlags &= ~EffectDirtyFlags.WorldViewProj; + } + + if ((dirtyFlags & EffectDirtyFlags.World) != 0) + { + modelParam.SetValue(model); + + dirtyFlags &= ~EffectDirtyFlags.World; + } + + lightPositionParam.SetValue(LightPosition); + farPlaneParam.SetValue(FarPlane); + } + + private void CacheEffectParameters() + { + modelParam = Parameters["Model"]; + modelViewProjectionParam = Parameters["ModelViewProjection"]; + + lightPositionParam = Parameters["LightPosition"]; + farPlaneParam = Parameters["FarPlane"]; + } + } +} diff --git a/Kav.Core.csproj b/Kav.Core.csproj index fc78de4..1a7954e 100644 --- a/Kav.Core.csproj +++ b/Kav.Core.csproj @@ -42,6 +42,9 @@ Kav.Resources.SimpleDepthEffect.fxb + + Kav.Resources.LinearDepthEffect.fxb + Kav.Resources.SkyboxEffect.fxb diff --git a/Kav.Framework.csproj b/Kav.Framework.csproj index 4b72a58..c60f949 100644 --- a/Kav.Framework.csproj +++ b/Kav.Framework.csproj @@ -42,12 +42,15 @@ Kav.Resources.SimpleDepthEffect.fxb - - Kav.Resources.SkyboxEffect.fxb - - - Kav.Resources.UnitCube.glb - + + Kav.Resources.LinearDepthEffect.fxb + + + Kav.Resources.SkyboxEffect.fxb + + + Kav.Resources.UnitCube.glb + diff --git a/README.md b/README.md index 8f3190b..9cfb2c3 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Essential - [ ] Frustum culling - [ ] Shadow-casting point lights - [ ] Parabolic lights -- [ ] Skyboxes +- [x] Skyboxes - [ ] Screen-space reflection Nice-To-Haves diff --git a/Renderer.cs b/Renderer.cs index 2e11a73..27b4b1b 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; @@ -29,6 +30,7 @@ namespace Kav private DeferredPBR_DirectionalLightEffect DeferredDirectionalLightEffect { get; } private Deferred_ToonEffect Deferred_ToonEffect { get; } private SimpleDepthEffect SimpleDepthEffect { get; } + private LinearDepthEffect LinearDepthEffect { get; } private Effect ToneMapEffect { get; } private SkyboxEffect SkyboxEffect { get; } @@ -36,6 +38,7 @@ namespace Kav private RenderTarget2D gNormal { get; } private RenderTarget2D gAlbedo { get; } private RenderTarget2D gMetallicRoughness { get; } + private RenderTargetCube PointShadowCubeMap { get; } private RenderTargetBinding[] GBuffer { get; } @@ -136,7 +139,16 @@ namespace Kav new RenderTargetBinding(gMetallicRoughness) }; + PointShadowCubeMap = new RenderTargetCube( + GraphicsDevice, + shadowMapSize, + false, + SurfaceFormat.Single, + DepthFormat.Depth24 + ); + SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice); + LinearDepthEffect = new LinearDepthEffect(GraphicsDevice); DeferredPBREffect = new DeferredPBREffect(GraphicsDevice); Deferred_GBufferEffect = new DeferredPBR_GBufferEffect(GraphicsDevice); @@ -181,7 +193,7 @@ namespace Kav foreach (var pointLight in pointLights) { - PointLightRender(pointLight); + PointLightRender(camera, modelTransforms, pointLight); } DirectionalLightRender(camera, modelTransforms, directionalLight); @@ -197,6 +209,7 @@ namespace Kav PerspectiveCamera camera, IEnumerable<(Model, Matrix)> modelTransforms, AmbientLight ambientLight, + IEnumerable pointLights, DirectionalLight directionalLight, TextureCube skybox ) { @@ -210,7 +223,11 @@ namespace Kav GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; AmbientLightRender(ambientLight); - DirectionalLightToonRender(camera, modelTransforms, directionalLight); + foreach (var pointLight in pointLights) + { + PointLightRender(camera, modelTransforms, pointLight); + } + //DirectionalLightToonRender(camera, modelTransforms, directionalLight); SkyboxRender(camera, skybox); GraphicsDevice.SetRenderTarget(null); @@ -351,17 +368,29 @@ namespace Kav } } - private void PointLightRender(PointLight pointLight) - { + private void PointLightRender( + PerspectiveCamera camera, + IEnumerable<(Model, Matrix)> modelTransforms, + PointLight pointLight + ) { + RenderPointShadows(camera, modelTransforms, pointLight); + + GraphicsDevice.SetRenderTarget(ColorRenderTarget); + GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; + GraphicsDevice.BlendState = BlendState.Additive; + DeferredPointLightEffect.GPosition = gPosition; DeferredPointLightEffect.GAlbedo = gAlbedo; DeferredPointLightEffect.GNormal = gNormal; DeferredPointLightEffect.GMetallicRoughness = gMetallicRoughness; + DeferredPointLightEffect.ShadowMap = PointShadowCubeMap; DeferredPointLightEffect.PointLightPosition = pointLight.Position; DeferredPointLightEffect.PointLightColor = pointLight.Color.ToVector3() * pointLight.Intensity; + DeferredPointLightEffect.FarPlane = 25f; // FIXME: magic value + foreach (var pass in DeferredPointLightEffect.CurrentTechnique.Passes) { pass.Apply(); @@ -375,7 +404,7 @@ namespace Kav IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight ) { - RenderShadows(camera, modelTransforms, directionalLight, DeferredDirectionalLightEffect); + RenderDirectionalShadows(camera, modelTransforms, directionalLight, DeferredDirectionalLightEffect); DeferredDirectionalLightEffect.GPosition = gPosition; DeferredDirectionalLightEffect.GAlbedo = gAlbedo; @@ -419,7 +448,7 @@ namespace Kav IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight ) { - RenderShadows(camera, modelTransforms, directionalLight, Deferred_ToonEffect); + RenderDirectionalShadows(camera, modelTransforms, directionalLight, Deferred_ToonEffect); GraphicsDevice.SetRenderTarget(ColorRenderTarget); GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; @@ -461,7 +490,7 @@ namespace Kav } } - private void RenderShadows( + private void RenderDirectionalShadows( PerspectiveCamera camera, IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight, @@ -485,14 +514,14 @@ namespace Kav ); // TODO: This is tightly coupled to the effect and it sucks - RenderShadowMap(shadowCamera, modelTransforms, directionalLight, effect, i); + RenderDirectionalShadowMap(shadowCamera, modelTransforms, directionalLight, effect, i); effect.CascadeFarPlanes[i] = farPlane; previousFarPlane = farPlane; } } - private void RenderShadowMap( + private void RenderDirectionalShadowMap( PerspectiveCamera camera, IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight, @@ -580,5 +609,102 @@ namespace Kav } } } + + private void RenderPointShadows( + PerspectiveCamera camera, + IEnumerable<(Model, Matrix)> modelTransforms, + PointLight pointLight + ) { + GraphicsDevice.DepthStencilState = DepthStencilState.Default; + GraphicsDevice.BlendState = BlendState.Opaque; + + LinearDepthEffect.Projection = Matrix.CreatePerspectiveFieldOfView( + MathHelper.PiOver2, + 1, + 0.1f, + 25f // FIXME: magic value + ); + LinearDepthEffect.FarPlane = 25f; + LinearDepthEffect.LightPosition = pointLight.Position; + + foreach (CubeMapFace face in Enum.GetValues(typeof(CubeMapFace))) + { + GraphicsDevice.SetRenderTarget(PointShadowCubeMap, face); + + Vector3 targetDirection; + Vector3 targetUpDirection; + switch(face) + { + case CubeMapFace.PositiveX: + targetDirection = Vector3.Right; + targetUpDirection = Vector3.Up; + break; + + case CubeMapFace.NegativeX: + targetDirection = Vector3.Left; + targetUpDirection = Vector3.Up; + break; + + case CubeMapFace.PositiveY: + targetDirection = Vector3.Up; + targetUpDirection = Vector3.Forward; + break; + + case CubeMapFace.NegativeY: + targetDirection = Vector3.Down; + targetUpDirection = Vector3.Backward; + break; + + case CubeMapFace.PositiveZ: + targetDirection = Vector3.Backward; + targetUpDirection = Vector3.Up; + break; + + case CubeMapFace.NegativeZ: + targetDirection = Vector3.Forward; + targetUpDirection = Vector3.Up; + break; + + default: + targetDirection = Vector3.Right; + targetUpDirection = Vector3.Up; + break; + } + + LinearDepthEffect.View = Matrix.CreateLookAt( + pointLight.Position, + pointLight.Position + targetDirection, + targetUpDirection + ); + + foreach (var (model, transform) in modelTransforms) + { + foreach (var modelMesh in model.Meshes) + { + foreach (var meshPart in modelMesh.MeshParts) + { + GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer); + GraphicsDevice.Indices = meshPart.IndexBuffer; + + LinearDepthEffect.Model = transform; + + foreach (var pass in LinearDepthEffect.CurrentTechnique.Passes) + { + pass.Apply(); + + GraphicsDevice.DrawIndexedPrimitives( + PrimitiveType.TriangleList, + 0, + 0, + meshPart.VertexBuffer.VertexCount, + 0, + meshPart.Triangles.Length + ); + } + } + } + } + } + } } } diff --git a/Resources.cs b/Resources.cs index 251b820..fd5daa5 100644 --- a/Resources.cs +++ b/Resources.cs @@ -10,7 +10,7 @@ namespace Kav { if (ambientLightEffect == null) { - ambientLightEffect = GetResource("DeferredPBR_AmbientLightEffect.fxb"); + ambientLightEffect = GetResource("DeferredPBR_AmbientLightEffect.fxb"); } return ambientLightEffect; } @@ -21,7 +21,7 @@ namespace Kav { if (pointLightEffect == null) { - pointLightEffect = GetResource("DeferredPBR_PointLightEffect.fxb"); + pointLightEffect = GetResource("DeferredPBR_PointLightEffect.fxb"); } return pointLightEffect; } @@ -33,7 +33,7 @@ namespace Kav { if (directionalLightEffect == null) { - directionalLightEffect = GetResource("DeferredPBR_DirectionalLightEffect.fxb"); + directionalLightEffect = GetResource("DeferredPBR_DirectionalLightEffect.fxb"); } return directionalLightEffect; } @@ -45,7 +45,7 @@ namespace Kav { if (gBufferEffect == null) { - gBufferEffect = GetResource("DeferredPBR_GBufferEffect.fxb"); + gBufferEffect = GetResource("DeferredPBR_GBufferEffect.fxb"); } return gBufferEffect; } @@ -57,7 +57,7 @@ namespace Kav { if (toneMapEffect == null) { - toneMapEffect = GetResource("ToneMapEffect.fxb"); + toneMapEffect = GetResource("ToneMapEffect.fxb"); } return toneMapEffect; } @@ -69,7 +69,7 @@ namespace Kav { if (deferredToonEffect == null) { - deferredToonEffect = GetResource("Deferred_ToonEffect.fxb"); + deferredToonEffect = GetResource("Deferred_ToonEffect.fxb"); } return deferredToonEffect; } @@ -81,7 +81,7 @@ namespace Kav { if (deferredPBREffect == null) { - deferredPBREffect = GetResource("DeferredPBREffect.fxb"); + deferredPBREffect = GetResource("DeferredPBREffect.fxb"); } return deferredPBREffect; } @@ -93,7 +93,7 @@ namespace Kav { if (pbrEffect == null) { - pbrEffect = GetResource("PBREffect.fxb"); + pbrEffect = GetResource("PBREffect.fxb"); } return pbrEffect; } @@ -105,36 +105,48 @@ namespace Kav { if (simpleDepthEffect == null) { - simpleDepthEffect = GetResource("SimpleDepthEffect.fxb"); + simpleDepthEffect = GetResource("SimpleDepthEffect.fxb"); } return simpleDepthEffect; } } - public static byte[] SkyboxEffect - { - get - { - if (skyboxEffect == null) - { - skyboxEffect = GetResource("SkyboxEffect.fxb"); - } - return skyboxEffect; - } - } - - public static byte[] UnitCubeModel - { - get - { - if (unitCubeModel == null) - { - unitCubeModel = GetResource("UnitCube.glb"); - } - return unitCubeModel; - } - } - + public static byte[] LinearDepthEffect + { + get + { + if (linearDepthEffect == null) + { + linearDepthEffect = GetResource("LinearDepthEffect.fxb"); + } + return linearDepthEffect; + } + } + + public static byte[] SkyboxEffect + { + get + { + if (skyboxEffect == null) + { + skyboxEffect = GetResource("SkyboxEffect.fxb"); + } + return skyboxEffect; + } + } + + public static byte[] UnitCubeModel + { + get + { + if (unitCubeModel == null) + { + unitCubeModel = GetResource("UnitCube.glb"); + } + return unitCubeModel; + } + } + private static byte[] ambientLightEffect; private static byte[] pointLightEffect; private static byte[] directionalLightEffect; @@ -144,14 +156,15 @@ namespace Kav private static byte[] deferredPBREffect; private static byte[] pbrEffect; private static byte[] simpleDepthEffect; - private static byte[] skyboxEffect; - - private static byte[] unitCubeModel; + private static byte[] linearDepthEffect; + private static byte[] skyboxEffect; + + private static byte[] unitCubeModel; private static byte[] GetResource(string name) { Stream stream = typeof(Resources).Assembly.GetManifestResourceStream( - "Kav.Resources." + name + "Kav.Resources." + name ); using (MemoryStream ms = new MemoryStream()) { -- 2.25.1 From 8407a34c37b5b8f3278d4e6e6beb20683c3d13e8 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Mon, 19 Oct 2020 12:48:32 -0700 Subject: [PATCH 10/11] flip horizontal UVs of cubemap render --- Effects/FXB/LinearDepthEffect.fxb | 4 ++-- Effects/HLSL/LinearDepthEffect.fx | 1 + Renderer.cs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Effects/FXB/LinearDepthEffect.fxb b/Effects/FXB/LinearDepthEffect.fxb index f805053..c92681f 100644 --- a/Effects/FXB/LinearDepthEffect.fxb +++ b/Effects/FXB/LinearDepthEffect.fxb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9509896b7532c6ecf77aef4e575a7bceb8a106f4ad0c1a9c11a1608c9807cc10 -size 1332 +oid sha256:2e91546d1db9b0bb2eb93ab2dcdeaaa260c5eb08d9acf6460c091f8fdf4c88ee +size 1344 diff --git a/Effects/HLSL/LinearDepthEffect.fx b/Effects/HLSL/LinearDepthEffect.fx index 164da5b..67db0e2 100644 --- a/Effects/HLSL/LinearDepthEffect.fx +++ b/Effects/HLSL/LinearDepthEffect.fx @@ -25,6 +25,7 @@ VertexShaderOutput main_vs(VertexShaderInput input) { VertexShaderOutput output; output.Position = mul(input.Position, ModelViewProjection); + output.Position.x *= -1; // otherwise cube map render will be horizontally flipped output.PositionWorld = mul(input.Position, Model); return output; } diff --git a/Renderer.cs b/Renderer.cs index 27b4b1b..67372c8 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -276,7 +276,7 @@ namespace Kav TextureCube skybox ) { GraphicsDevice.RasterizerState.CullMode = CullMode.CullClockwiseFace; - SkyboxEffect.Skybox = skybox; + SkyboxEffect.Skybox = PointShadowCubeMap; var view = camera.View; view.Translation = Vector3.Zero; -- 2.25.1 From cb0baf0bf0b2c8c830c239c1566ca7a2fcacced9 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Mon, 19 Oct 2020 14:00:11 -0700 Subject: [PATCH 11/11] integrate all shadows --- Effects/FXB/DeferredPBR_PointLightEffect.fxb | 2 +- Effects/HLSL/Shadow.fxh | 3 +-- README.md | 2 +- Renderer.cs | 6 +++--- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Effects/FXB/DeferredPBR_PointLightEffect.fxb b/Effects/FXB/DeferredPBR_PointLightEffect.fxb index 330086e..9511784 100644 --- a/Effects/FXB/DeferredPBR_PointLightEffect.fxb +++ b/Effects/FXB/DeferredPBR_PointLightEffect.fxb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0331c52bcbc31d3639743c487ff1b48e6e90c5c4f9134f6b2154736a9dd8d6d0 +oid sha256:033598dc2f22c2766a8b0d46215e5a9764b5445a520d67bc68f1566dbdd15035 size 3740 diff --git a/Effects/HLSL/Shadow.fxh b/Effects/HLSL/Shadow.fxh index c1bb9ba..5f8351f 100644 --- a/Effects/HLSL/Shadow.fxh +++ b/Effects/HLSL/Shadow.fxh @@ -137,6 +137,5 @@ float HardPointShadow( float bias = max(0.05 * (1.0 - dot(N, L)), 0.005); - return (currentDepth - bias > closestDepth ? 0.25 : 1.0); - //return closestDepth / farPlane; + return (currentDepth - bias > closestDepth ? 0 : 1.0); } diff --git a/README.md b/README.md index 9cfb2c3..29c7615 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Essential - [x] Tone map shader - [x] Poisson soft shadowing - [ ] Frustum culling -- [ ] Shadow-casting point lights +- [x] Shadow-casting point lights - [ ] Parabolic lights - [x] Skyboxes - [ ] Screen-space reflection diff --git a/Renderer.cs b/Renderer.cs index 67372c8..3eb2440 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -227,7 +227,7 @@ namespace Kav { PointLightRender(camera, modelTransforms, pointLight); } - //DirectionalLightToonRender(camera, modelTransforms, directionalLight); + DirectionalLightToonRender(camera, modelTransforms, directionalLight); SkyboxRender(camera, skybox); GraphicsDevice.SetRenderTarget(null); @@ -276,7 +276,7 @@ namespace Kav TextureCube skybox ) { GraphicsDevice.RasterizerState.CullMode = CullMode.CullClockwiseFace; - SkyboxEffect.Skybox = PointShadowCubeMap; + SkyboxEffect.Skybox = skybox; var view = camera.View; view.Translation = Vector3.Zero; @@ -459,7 +459,7 @@ namespace Kav Deferred_ToonEffect.GNormal = gNormal; Deferred_ToonEffect.GMetallicRoughness = gMetallicRoughness; - Deferred_ToonEffect.DitheredShadows = true; + Deferred_ToonEffect.DitheredShadows = false; Deferred_ToonEffect.EyePosition = camera.Position; Deferred_ToonEffect.DirectionalLightDirection = directionalLight.Direction; -- 2.25.1