From 47242e4f52d11b16ec4bc5b630d5d58869dedd92 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Sat, 5 Dec 2020 19:47:01 -0800 Subject: [PATCH] initial diffuse lit sprite effect --- Effects/DiffuseLitSpriteEffect.cs | 154 +++++++++++++++++++++++++ Effects/FXB/DiffuseLitSpriteEffect.fxb | 3 + Effects/HLSL/DiffuseLitSprite.fx | 90 +++++++++++++++ Kav.Core.csproj | 3 + Kav.Framework.csproj | 3 + Renderer.cs | 38 ++++-- Resources.cs | 13 +++ 7 files changed, 293 insertions(+), 11 deletions(-) create mode 100644 Effects/DiffuseLitSpriteEffect.cs create mode 100644 Effects/FXB/DiffuseLitSpriteEffect.fxb create mode 100644 Effects/HLSL/DiffuseLitSprite.fx diff --git a/Effects/DiffuseLitSpriteEffect.cs b/Effects/DiffuseLitSpriteEffect.cs new file mode 100644 index 0000000..35385b6 --- /dev/null +++ b/Effects/DiffuseLitSpriteEffect.cs @@ -0,0 +1,154 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + public class DiffuseLitSpriteEffect : Effect + { + EffectParameter textureParam; + + EffectParameter ambientColorParam; + + EffectParameter directionalLightDirectionParam; + EffectParameter directionalLightColorParam; + + EffectParameter worldParam; + EffectParameter worldViewProjectionParam; + EffectParameter worldInverseTransposeParam; + + Texture2D texture; + + Vector3 ambientColor; + + Vector3 directionalLightDirection; + Vector3 directionalLightColor; + + Matrix world = Matrix.Identity; + Matrix view = Matrix.Identity; + Matrix projection = Matrix.Identity; + + EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All; + + public Texture2D Texture + { + get { return texture; } + set + { + texture = value; + textureParam.SetValue(texture); + } + } + + public Matrix World + { + get { return world; } + set + { + world = value; + dirtyFlags |= EffectDirtyFlags.World | EffectDirtyFlags.WorldViewProj; + } + } + + public Matrix View + { + get { return view; } + set + { + view = value; + dirtyFlags |= EffectDirtyFlags.WorldViewProj | EffectDirtyFlags.EyePosition; + } + } + + public Matrix Projection + { + get { return projection; } + set + { + projection = value; + dirtyFlags |= EffectDirtyFlags.WorldViewProj; + } + } + + public int MaxPointLights { get; } = 8; + + public Vector3 AmbientColor + { + get { return ambientColor; } + set + { + ambientColor = value; + ambientColorParam.SetValue(ambientColor); + } + } + + public PointLightCollection PointLights { get; private set; } + + public Vector3 DirectionalLightDirection + { + get { return directionalLightDirection; } + set + { + directionalLightDirection = value; + directionalLightDirectionParam.SetValue(directionalLightDirection); + } + } + + public Vector3 DirectionalLightColor + { + get { return directionalLightColor; } + set + { + directionalLightColor = value; + directionalLightColorParam.SetValue(directionalLightColor); + } + } + + public DiffuseLitSpriteEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DiffuseLitSpriteEffect) + { + CacheEffectParameters(); + + PointLights = new PointLightCollection( + Parameters["PointLightPositions"], + Parameters["PointLightColors"], + MaxPointLights + ); + } + + protected override void OnApply() + { + if ((dirtyFlags & EffectDirtyFlags.World) != 0) + { + worldParam.SetValue(world); + + Matrix.Invert(ref world, out Matrix worldInverse); + Matrix.Transpose(ref worldInverse, out Matrix worldInverseTranspose); + worldInverseTransposeParam.SetValue(worldInverseTranspose); + + dirtyFlags &= ~EffectDirtyFlags.World; + } + + if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0) + { + Matrix.Multiply(ref world, ref view, out Matrix worldView); + Matrix.Multiply(ref worldView, ref projection, out Matrix worldViewProj); + worldViewProjectionParam.SetValue(worldViewProj); + + dirtyFlags &= ~EffectDirtyFlags.WorldViewProj; + } + } + + private void CacheEffectParameters() + { + textureParam = Parameters["Texture"]; + + worldParam = Parameters["World"]; + worldViewProjectionParam = Parameters["WorldViewProjection"]; + worldInverseTransposeParam = Parameters["WorldInverseTranspose"]; + + ambientColorParam = Parameters["AmbientColor"]; + + directionalLightDirectionParam = Parameters["DirectionalLightDirection"]; + directionalLightColorParam = Parameters["DirectionalLightColor"]; + } + } +} diff --git a/Effects/FXB/DiffuseLitSpriteEffect.fxb b/Effects/FXB/DiffuseLitSpriteEffect.fxb new file mode 100644 index 0000000..7c6af31 --- /dev/null +++ b/Effects/FXB/DiffuseLitSpriteEffect.fxb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc6de319b583aabd741b9d1012854fd318ee66d28cb4abeaa04ea78058645d25 +size 4288 diff --git a/Effects/HLSL/DiffuseLitSprite.fx b/Effects/HLSL/DiffuseLitSprite.fx new file mode 100644 index 0000000..163af05 --- /dev/null +++ b/Effects/HLSL/DiffuseLitSprite.fx @@ -0,0 +1,90 @@ +#include "Macros.fxh" //from FNA + +// Effect applies normalmapped lighting to a 2D sprite. + +DECLARE_TEXTURE(Texture, 0); +DECLARE_TEXTURE(Normal, 1); + +BEGIN_CONSTANTS + + float AmbientColor _ps(c0) _cb(c0); + + float3 PointLightPositions[8] _ps(c1) _cb(c1); + float3 PointLightColors[8] _ps(c9) _cb(c9); + + float DirectionalLightDirection _ps(c17) _cb(c17); + float DirectionalLightColor _ps(c18) _cb(c18); + +MATRIX_CONSTANTS + + float4x4 WorldInverseTranspose _ps(c19) _cb(c19); + float4x4 World _vs(c0) _cb(c23); + float4x4 WorldViewProjection _vs(c4) _cb(c27); + +END_CONSTANTS + +struct VertexShaderInput +{ + float4 Position : POSITION; + float2 TexCoord : TEXCOORD0; +}; + +struct PixelShaderInput +{ + float4 Position : SV_Position; + float2 TexCoord : TEXCOORD0; + float3 PositionWS : TEXCOORD2; +}; + +PixelShaderInput main_vs(VertexShaderInput input) +{ + PixelShaderInput output; + + output.TexCoord = input.TexCoord; + output.PositionWS = mul(input.Position, World).xyz; + output.Position = mul(input.Position, WorldViewProjection); + + return output; +} + +float4 main_ps(PixelShaderInput input) : COLOR0 +{ + // Look up the texture and normalmap values. + float4 tex = tex2D(TextureSampler, input.TexCoord); + float3 normal = tex2D(NormalSampler, input.TexCoord).xyz; + float3 normalWS = mul(normal, (float3x3)WorldInverseTranspose).xyz; + + float3 lightColor = float3(0.0, 0.0, 0.0); + + // point lights + for (int i = 0; i < 8; i++) + { + float3 lightVec = PointLightPositions[i] - input.PositionWS; + float distance = length(lightVec); + + float3 lightDir = normalize(lightVec); + float diffuse = max(dot(normalWS, lightDir), 0.0); + float3 attenuation = 1.0 / (distance * distance); + + lightColor += diffuse * attenuation * PointLightColors[i]; + } + + // directional light + float directionalDiffuse = max(dot(normalWS, DirectionalLightDirection), 0.0); + lightColor += directionalDiffuse * DirectionalLightColor; + + // ambient light + lightColor += AmbientColor; + + // blend with sample + return tex * float4(lightColor, 1.0); +} + +Technique DiffuseLitSprite +{ + 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 b52de06..9cb9564 100644 --- a/Kav.Core.csproj +++ b/Kav.Core.csproj @@ -49,6 +49,9 @@ Kav.Resources.SkyboxEffect.fxb + + Kav.Resources.DiffuseLitSpriteEffect.fxb + Kav.Resources.UnitCube.glb diff --git a/Kav.Framework.csproj b/Kav.Framework.csproj index 64058b4..d5c6c57 100644 --- a/Kav.Framework.csproj +++ b/Kav.Framework.csproj @@ -49,6 +49,9 @@ Kav.Resources.SkyboxEffect.fxb + + Kav.Resources.DiffuseLitSpriteEffect.fxb + Kav.Resources.UnitCube.glb diff --git a/Renderer.cs b/Renderer.cs index 047c386..23561be 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -33,7 +33,7 @@ namespace Kav private LinearDepthEffect LinearDepthEffect { get; } private Effect ToneMapEffect { get; } private SkyboxEffect SkyboxEffect { get; } - private BasicEffect BasicEffect { get; } + private DiffuseLitSpriteEffect DiffuseLitSpriteEffect { get; } private RenderTarget2D gPosition { get; } private RenderTarget2D gNormal { get; } @@ -160,7 +160,7 @@ namespace Kav ToneMapEffect = new Effect(graphicsDevice, Resources.ToneMapEffect); Deferred_ToonEffect = new Deferred_ToonEffect(GraphicsDevice); SkyboxEffect = new SkyboxEffect(GraphicsDevice); - BasicEffect = new BasicEffect(GraphicsDevice); + DiffuseLitSpriteEffect = new DiffuseLitSpriteEffect(GraphicsDevice); FullscreenTriangle = new VertexBuffer(GraphicsDevice, typeof(VertexPositionTexture), 3, BufferUsage.WriteOnly); FullscreenTriangle.SetData(new VertexPositionTexture[3] { @@ -245,7 +245,10 @@ namespace Kav RenderTarget2D renderTarget, PerspectiveCamera camera, IEnumerable<(Model, Matrix)> modelTransforms, - IEnumerable sprites + IEnumerable sprites, + AmbientLight ambientLight, + IEnumerable pointLights, + DirectionalLight directionalLight ) { GraphicsDevice.SetRenderTarget(ColorRenderTarget); GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0); @@ -254,20 +257,31 @@ namespace Kav DepthRender(camera, modelTransforms); GraphicsDevice.Clear(ClearOptions.Target, new Color(0, 0, 0, 0), 1f, 0); - BasicEffect.View = camera.View; - BasicEffect.Projection = camera.Projection; - BasicEffect.TextureEnabled = true; - BasicEffect.VertexColorEnabled = true; + DiffuseLitSpriteEffect.View = camera.View; + DiffuseLitSpriteEffect.Projection = camera.Projection; + + DiffuseLitSpriteEffect.DirectionalLightDirection = + directionalLight.Direction; + DiffuseLitSpriteEffect.DirectionalLightColor = + directionalLight.Color.ToVector3() * directionalLight.Intensity; + + var i = 0; + foreach (var pointLight in pointLights) + { + if (i > DiffuseLitSpriteEffect.MaxPointLights) { break; } + DiffuseLitSpriteEffect.PointLights[i] = pointLight; + i += 1; + } foreach (var sprite in sprites) { if (sprite.BillboardConstraint == SpriteBillboardConstraint.None) { - BasicEffect.World = sprite.TransformMatrix; + DiffuseLitSpriteEffect.World = sprite.TransformMatrix; } else if (sprite.BillboardConstraint == SpriteBillboardConstraint.Horizontal) { - BasicEffect.World = Matrix.CreateConstrainedBillboard( + DiffuseLitSpriteEffect.World = Matrix.CreateConstrainedBillboard( sprite.Position, camera.Position, Vector3.Up, @@ -277,7 +291,7 @@ namespace Kav } else { - BasicEffect.World = Matrix.CreateConstrainedBillboard( + DiffuseLitSpriteEffect.World = Matrix.CreateConstrainedBillboard( sprite.Position, camera.Position, Vector3.Up, @@ -286,7 +300,9 @@ namespace Kav ); } - SpriteBatch.Begin(0, null, null, DepthStencilState.DepthRead, RasterizerState.CullNone, BasicEffect); + GraphicsDevice.Textures[1] = sprite.Texture; + + SpriteBatch.Begin(0, null, null, DepthStencilState.DepthRead, RasterizerState.CullNone, DiffuseLitSpriteEffect); SpriteBatch.Draw( sprite.Texture, Vector2.Zero, diff --git a/Resources.cs b/Resources.cs index fd5daa5..44c28f7 100644 --- a/Resources.cs +++ b/Resources.cs @@ -135,6 +135,18 @@ namespace Kav } } + public static byte[] DiffuseLitSpriteEffect + { + get + { + if (diffuseLitSpriteEffect == null) + { + diffuseLitSpriteEffect = GetResource("DiffuseLitSpriteEffect.fxb"); + } + return diffuseLitSpriteEffect; + } + } + public static byte[] UnitCubeModel { get @@ -158,6 +170,7 @@ namespace Kav private static byte[] simpleDepthEffect; private static byte[] linearDepthEffect; private static byte[] skyboxEffect; + private static byte[] diffuseLitSpriteEffect; private static byte[] unitCubeModel;