supporting directional lights

shadows
cosmonaut 2020-08-07 01:12:46 -07:00
parent 86cc23b0ad
commit 393b8bcb03
10 changed files with 198 additions and 54 deletions

View File

@ -0,0 +1,8 @@
namespace Kav
{
public interface DirectionalLightEffect
{
int MaxDirectionalLights { get; }
DirectionalLightCollection DirectionalLights { get; }
}
}

Binary file not shown.

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
}
}
}
}
}

View File

@ -18,6 +18,9 @@
<EmbeddedResource Include="Effects\FXB\PBREffect.fxb">
<LogicalName>Kav.Resources.PBREffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\SimpleDepthEffect.fxb">
<LogicalName>Kav.Resources.SimpleDepthEffect.fxb</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@ -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;
}
}
}

View File

@ -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)
{

View File

@ -31,19 +31,23 @@ namespace Kav
SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice);
}
public void Render(Camera camera, IEnumerable<Model> models, IEnumerable<PointLight> pointLights)
{
Render(camera.View, camera.Projection, models, pointLights);
public void Render(
Camera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
IEnumerable<PointLight> pointLights,
IEnumerable<DirectionalLight> directionalLights
) {
Render(camera.View, camera.Projection, modelTransforms, pointLights, directionalLights);
}
// for shadow mapping
public void DepthRender(Matrix view, Matrix projection, IEnumerable<(Model, Matrix)> modelTransforms, IEnumerable<PointLight> 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<Model> models, IEnumerable<PointLight> pointLights)
{
foreach (var model in models)
private void Render(
Matrix view,
Matrix projection,
IEnumerable<(Model, Matrix)> modelTransforms,
IEnumerable<PointLight> pointLights,
IEnumerable<DirectionalLight> 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();

View File

@ -22,7 +22,7 @@ namespace Kav
{
if (simpleDepthEffect == null)
{
simpleDepthEffect = GetResource("SimpleDepthEffecT");
simpleDepthEffect = GetResource("SimpleDepthEffect");
}
return simpleDepthEffect;
}