diff --git a/EffectInterfaces/DirectionalLightEffect.cs b/EffectInterfaces/DirectionalLightEffect.cs new file mode 100644 index 0000000..53ba894 --- /dev/null +++ b/EffectInterfaces/DirectionalLightEffect.cs @@ -0,0 +1,8 @@ +namespace Kav +{ + public interface DirectionalLightEffect + { + int MaxDirectionalLights { get; } + DirectionalLightCollection DirectionalLights { get; } + } +} diff --git a/Effects/FXB/PBREffect.fxb b/Effects/FXB/PBREffect.fxb index 9763762..1cac37b 100644 Binary files a/Effects/FXB/PBREffect.fxb and b/Effects/FXB/PBREffect.fxb differ diff --git a/Effects/HLSL/PBREffect.fx b/Effects/HLSL/PBREffect.fx index ee5ecb7..34ce0ec 100644 --- a/Effects/HLSL/PBREffect.fx +++ b/Effects/HLSL/PBREffect.fx @@ -23,12 +23,15 @@ BEGIN_CONSTANTS // Light Info float3 LightPositions[4] _ps(c4) _cb(c4); - float3 LightColors[4] _ps(c8) _cb(c8); + float3 PositionLightColors[4] _ps(c8) _cb(c8); - float3 EyePosition _ps(c12) _cb(c12); + float3 LightDirections[4] _ps(c12) _cb(c12); + float3 DirectionLightColors[4] _ps(c16) _cb(c16); - float4x4 World _vs(c0) _cb(c16); - float4x4 WorldInverseTranspose _vs(c4) _cb(c20); + float3 EyePosition _ps(c20) _cb(c20); + + float4x4 World _vs(c0) _cb(c21); + float4x4 WorldInverseTranspose _vs(c4) _cb(c25); MATRIX_CONSTANTS @@ -121,6 +124,36 @@ float3 GetNormalFromMap(float3 worldPos, float2 texCoords, float3 normal) return normalize(mul(tangentNormal, TBN)); } +float3 ComputeLight( + float3 lightDir, + float3 radiance, + float3 F0, + float3 V, + float3 N, + float3 albedo, + float metallic, + float roughness +) { + float3 L = normalize(lightDir); + float3 H = normalize(V + L); + + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + float3 F = FresnelSchlick(max(dot(H, V), 0.0), F0); + + float3 numerator = NDF * G * F; + float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0); + float3 specular = numerator / max(denominator, 0.001); + + float3 kS = F; + float3 kD = float3(1.0, 1.0, 1.0) - kS; + + kD *= 1.0 - metallic; + + float NdotL = max(dot(N, L), 0.0); + return (kD * albedo / PI + specular) * radiance * NdotL; +} + float4 ComputeColor( float3 worldPosition, float3 worldNormal, @@ -136,31 +169,24 @@ float4 ComputeColor( float3 Lo = float3(0.0, 0.0, 0.0); + // point light for (int i = 0; i < 4; i++) { float3 lightDir = LightPositions[i] - worldPosition; - float3 L = normalize(lightDir); - float3 H = normalize(V + L); - float distance = length(lightDir); float attenuation = 1.0 / (distance * distance); - float3 radiance = LightColors[i] * attenuation; + float3 radiance = PositionLightColors[i] * attenuation; - float NDF = DistributionGGX(N, H, roughness); - float G = GeometrySmith(N, V, L, roughness); - float3 F = FresnelSchlick(max(dot(H, V), 0.0), F0); + Lo += ComputeLight(lightDir, radiance, F0, V, N, albedo, metallic, roughness); + } - float3 numerator = NDF * G * F; - float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0); - float3 specular = numerator / max(denominator, 0.001); + // directional light + for (int i = 0; i < 4; i++) + { + float3 lightDir = LightDirections[i]; + float3 radiance = DirectionLightColors[i]; - float3 kS = F; - float3 kD = float3(1.0, 1.0, 1.0) - kS; - - kD *= 1.0 - metallic; - - float NdotL = max(dot(N, L), 0.0); - Lo += (kD * albedo / PI + specular) * radiance * NdotL; + Lo += ComputeLight(lightDir, radiance, F0, V, N, albedo, metallic, roughness); } float3 ambient = float3(0.03, 0.03, 0.03) * albedo * AO; diff --git a/Effects/PBREffect.cs b/Effects/PBREffect.cs index f7927b4..09e313e 100644 --- a/Effects/PBREffect.cs +++ b/Effects/PBREffect.cs @@ -45,7 +45,49 @@ namespace Kav } } - public class PBREffect : Effect, TransformEffect, PointLightEffect + public class DirectionalLightCollection + { + private readonly Vector3[] directions = new Vector3[4]; + private readonly Vector3[] colors = new Vector3[4]; + private readonly float[] intensities = new float[4]; + + readonly EffectParameter lightPositionsParam; + readonly EffectParameter lightColorsParam; + + public DirectionalLightCollection(EffectParameter lightPositionsParam, EffectParameter lightColorsParam) + { + this.lightPositionsParam = lightPositionsParam; + this.lightColorsParam = lightColorsParam; + } + + public DirectionalLight this[int i] + { + get + { + var color = colors[i] / intensities[i]; + return new DirectionalLight( + directions[i], + new Color( + color.X, + color.Y, + color.Z, + 1f + ), + intensities[i] + ); + } + set + { + directions[i] = value.Direction; + colors[i] = value.Color.ToVector3() * value.Intensity; + intensities[i] = value.Intensity; + lightPositionsParam.SetValue(directions); + lightColorsParam.SetValue(colors); + } + } + } + + public class PBREffect : Effect, TransformEffect, PointLightEffect, DirectionalLightEffect { EffectParameter worldParam; EffectParameter worldViewProjectionParam; @@ -73,6 +115,7 @@ namespace Kav Matrix view = Matrix.Identity; Matrix projection = Matrix.Identity; PointLightCollection pointLightCollection; + DirectionalLightCollection directionalLightCollection; Vector3 albedo; float metallic; @@ -122,7 +165,15 @@ namespace Kav public PointLightCollection PointLights { get { return pointLightCollection; } - internal set { pointLightCollection = value; } + private set { pointLightCollection = value; } + } + + public int MaxDirectionalLights { get; } = 4; + + public DirectionalLightCollection DirectionalLights + { + get { return directionalLightCollection; } + private set { directionalLightCollection = value; } } public Vector3 Albedo @@ -234,7 +285,12 @@ namespace Kav pointLightCollection = new PointLightCollection( Parameters["LightPositions"], - Parameters["LightColors"] + Parameters["PositionLightColors"] + ); + + directionalLightCollection = new DirectionalLightCollection( + Parameters["LightDirections"], + Parameters["DirectionLightColors"] ); } @@ -248,14 +304,24 @@ namespace Kav PointLights = new PointLightCollection( Parameters["LightPositions"], - Parameters["LightColors"] + Parameters["PositionLightColors"] ); - for (int i = 0; i < 4; i++) + for (int i = 0; i < MaxPointLights; i++) { PointLights[i] = cloneSource.PointLights[i]; } + DirectionalLights = new DirectionalLightCollection( + Parameters["LightDirections"], + Parameters["DirectionLightColors"] + ); + + for (int i = 0; i < MaxDirectionalLights; i++) + { + DirectionalLights[i] = cloneSource.DirectionalLights[i]; + } + AlbedoTexture = cloneSource.AlbedoTexture; NormalTexture = cloneSource.NormalTexture; EmissionTexture = cloneSource.EmissionTexture; diff --git a/Geometry/Model.cs b/Geometry/Model.cs index 049532f..1d83410 100644 --- a/Geometry/Model.cs +++ b/Geometry/Model.cs @@ -10,19 +10,5 @@ namespace Kav { Meshes = meshes; } - - public void ApplyTransform(Matrix transform) - { - foreach (var mesh in Meshes) - { - foreach (var meshPart in mesh.MeshParts) - { - if (meshPart.Effect is TransformEffect transformEffect) - { - transformEffect.World = transform; - } - } - } - } } } diff --git a/Kav.csproj b/Kav.csproj index 41937d0..4497042 100644 --- a/Kav.csproj +++ b/Kav.csproj @@ -18,6 +18,9 @@ Kav.Resources.PBREffect.fxb + + Kav.Resources.SimpleDepthEffect.fxb + diff --git a/Lights/DirectionalLight.cs b/Lights/DirectionalLight.cs new file mode 100644 index 0000000..31ca4fd --- /dev/null +++ b/Lights/DirectionalLight.cs @@ -0,0 +1,34 @@ +using Microsoft.Xna.Framework; + +namespace Kav +{ + public struct DirectionalLight + { + public Vector3 Direction { get; set; } + public Color Color { get; set; } + public float Intensity { get; set; } + + public Matrix View + { + get + { + return Matrix.CreateLookAt(-Direction * 100f, Vector3.Zero, Vector3.Up); + } + } + + public Matrix Projection + { + get + { + return Matrix.CreateOrthographic(20f, 20f, 1f, 101f); + } + } + + public DirectionalLight(Vector3 direction, Color color, float intensity = 1f) + { + Direction = direction; + Color = color; + Intensity = intensity; + } + } +} diff --git a/Lights/PointLight.cs b/Lights/PointLight.cs index 657f7ea..6d3fb90 100644 --- a/Lights/PointLight.cs +++ b/Lights/PointLight.cs @@ -2,11 +2,11 @@ using Microsoft.Xna.Framework; namespace Kav { - public class PointLight + public struct PointLight { - public Vector3 Position { get; set; } - public Color Color { get; set; } - public float Intensity { get; set; } + public Vector3 Position { get; } + public Color Color { get; } + public float Intensity { get; } public PointLight(Vector3 position, Color color, float intensity = 1f) { diff --git a/Renderer.cs b/Renderer.cs index 79b078d..77037b3 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -31,19 +31,23 @@ namespace Kav SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice); } - public void Render(Camera camera, IEnumerable models, IEnumerable pointLights) - { - Render(camera.View, camera.Projection, models, pointLights); + public void Render( + Camera camera, + IEnumerable<(Model, Matrix)> modelTransforms, + IEnumerable pointLights, + IEnumerable directionalLights + ) { + Render(camera.View, camera.Projection, modelTransforms, pointLights, directionalLights); } // for shadow mapping - public void DepthRender(Matrix view, Matrix projection, IEnumerable<(Model, Matrix)> modelTransforms, IEnumerable pointLights) + public void DepthRender(IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight) { GraphicsDevice.SetRenderTarget(DepthRenderTarget); GraphicsDevice.Clear(ClearOptions.DepthBuffer, Color.Black, 1, 0); - SimpleDepthEffect.View = view; - SimpleDepthEffect.Projection = projection; + SimpleDepthEffect.View = directionalLight.View; + SimpleDepthEffect.Projection = directionalLight.Projection; foreach (var (model, transform) in modelTransforms) { @@ -74,9 +78,14 @@ namespace Kav } } - private void Render(Matrix view, Matrix projection, IEnumerable models, IEnumerable pointLights) - { - foreach (var model in models) + 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) { @@ -87,6 +96,7 @@ namespace Kav if (meshPart.Effect is TransformEffect transformEffect) { + transformEffect.World = transform; transformEffect.View = view; transformEffect.Projection = projection; } @@ -102,6 +112,17 @@ namespace Kav } } + if (meshPart.Effect is DirectionalLightEffect directionalLightEffect) + { + int i = 0; + foreach (var directionalLight in directionalLights) + { + if (i > directionalLightEffect.MaxDirectionalLights) { break; } + directionalLightEffect.DirectionalLights[i] = directionalLight; + i++; + } + } + foreach (var pass in meshPart.Effect.CurrentTechnique.Passes) { pass.Apply(); diff --git a/Resources.cs b/Resources.cs index 4964b4a..6ff3806 100644 --- a/Resources.cs +++ b/Resources.cs @@ -22,7 +22,7 @@ namespace Kav { if (simpleDepthEffect == null) { - simpleDepthEffect = GetResource("SimpleDepthEffecT"); + simpleDepthEffect = GetResource("SimpleDepthEffect"); } return simpleDepthEffect; }