Directional Light + Shadows + One Pass Per Light #2

Merged
cosmonaut merged 15 commits from directional_light into main 2020-10-01 19:46:26 +00:00
34 changed files with 1399 additions and 228 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.fxb filter=lfs diff=lfs merge=lfs -text

View File

@ -2,22 +2,32 @@ using Microsoft.Xna.Framework;
namespace Kav namespace Kav
{ {
public struct Camera public struct PerspectiveCamera
{ {
public Matrix Transform { get; } public Matrix View { get; }
public Matrix View
{
get
{
return Matrix.CreateLookAt(Transform.Translation, Transform.Translation + Transform.Forward, Transform.Up);
}
}
public Matrix Projection { get; } public Matrix Projection { get; }
public Camera(Matrix transform, Matrix projection) public Vector3 Position { get; }
public Vector3 Forward { get; }
public Vector3 Up { get; }
public float FieldOfView { get; }
public float AspectRatio { get; }
public float NearPlane { get; }
public float FarPlane { get; }
public PerspectiveCamera(Vector3 position, Vector3 forward, Vector3 up, float fieldOfView, float aspectRatio, float nearPlane, float farPlane)
{ {
Transform = transform; Position = position;
Projection = projection; Forward = forward;
Up = up;
View = Matrix.CreateLookAt(Position, Position + Forward, Up);
FieldOfView = fieldOfView;
AspectRatio = aspectRatio;
NearPlane = nearPlane;
FarPlane = farPlane;
Projection = Matrix.CreatePerspectiveFieldOfView(FieldOfView, AspectRatio, NearPlane, FarPlane);
} }
} }
} }

View File

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

View File

@ -10,15 +10,47 @@ namespace Kav
EffectParameter gNormalParam; EffectParameter gNormalParam;
EffectParameter gMetallicRoughnessParam; EffectParameter gMetallicRoughnessParam;
EffectParameter shadowMapOneParam;
EffectParameter shadowMapTwoParam;
EffectParameter shadowMapThreeParam;
EffectParameter shadowMapFourParam;
EffectParameter lightSpaceMatrixOneParam;
EffectParameter lightSpaceMatrixTwoParam;
EffectParameter lightSpaceMatrixThreeParam;
EffectParameter lightSpaceMatrixFourParam;
EffectParameter viewMatrixParam;
EffectParameter cascadeFarPlanesParam;
EffectParameter directionalLightColorParam;
EffectParameter directionalLightDirectionParam;
EffectParameter eyePositionParam; EffectParameter eyePositionParam;
PointLightCollection pointLightCollection; PointLightCollection pointLightCollection;
DirectionalLightCollection directionalLightCollection;
public Texture2D GPosition { get; set; } public Texture2D GPosition { get; set; }
public Texture2D GAlbedo { get; set; } public Texture2D GAlbedo { get; set; }
public Texture2D GNormal { get; set; } public Texture2D GNormal { get; set; }
public Texture2D GMetallicRoughness { get; set; } public Texture2D GMetallicRoughness { get; set; }
public Texture2D ShadowMapOne { get; set; }
public Texture2D ShadowMapTwo { get; set; }
public Texture2D ShadowMapThree { get; set; }
public Texture2D ShadowMapFour { 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 readonly float[] CascadeFarPlanes;
public Vector3 DirectionalLightColor { get; set; }
public Vector3 DirectionalLightDirection { get; set; }
public Vector3 EyePosition { get; set; } public Vector3 EyePosition { get; set; }
public int MaxPointLights { get; } = 64; public int MaxPointLights { get; } = 64;
@ -29,16 +61,10 @@ namespace Kav
private set { pointLightCollection = value; } private set { pointLightCollection = value; }
} }
public int MaxDirectionalLights { get; } = 4;
public DirectionalLightCollection DirectionalLights
{
get { return directionalLightCollection; }
private set { directionalLightCollection = value; }
}
public DeferredPBREffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBREffect) public DeferredPBREffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBREffect)
{ {
CascadeFarPlanes = new float[4];
CacheEffectParameters(); CacheEffectParameters();
pointLightCollection = new PointLightCollection( pointLightCollection = new PointLightCollection(
@ -46,11 +72,6 @@ namespace Kav
Parameters["PointLightColors"], Parameters["PointLightColors"],
MaxPointLights MaxPointLights
); );
DirectionalLights = new DirectionalLightCollection(
Parameters["DirectionalLightDirections"],
Parameters["DirectionalLightColors"]
);
} }
protected DeferredPBREffect(DeferredPBREffect cloneSource) : base(cloneSource) protected DeferredPBREffect(DeferredPBREffect cloneSource) : base(cloneSource)
@ -72,16 +93,6 @@ namespace Kav
{ {
PointLights[i] = cloneSource.PointLights[i]; PointLights[i] = cloneSource.PointLights[i];
} }
DirectionalLights = new DirectionalLightCollection(
Parameters["DirectionalLightDirections"],
Parameters["DirectionalLightColors"]
);
for (int i = 0; i < MaxDirectionalLights; i++)
{
DirectionalLights[i] = cloneSource.DirectionalLights[i];
}
} }
public override Effect Clone() public override Effect Clone()
@ -96,17 +107,49 @@ namespace Kav
gNormalParam.SetValue(GNormal); gNormalParam.SetValue(GNormal);
gMetallicRoughnessParam.SetValue(GMetallicRoughness); gMetallicRoughnessParam.SetValue(GMetallicRoughness);
shadowMapOneParam.SetValue(ShadowMapOne);
shadowMapTwoParam.SetValue(ShadowMapTwo);
shadowMapThreeParam.SetValue(ShadowMapThree);
shadowMapFourParam.SetValue(ShadowMapFour);
lightSpaceMatrixOneParam.SetValue(LightSpaceMatrixOne);
lightSpaceMatrixTwoParam.SetValue(LightSpaceMatrixTwo);
lightSpaceMatrixThreeParam.SetValue(LightSpaceMatrixThree);
lightSpaceMatrixFourParam.SetValue(LightSpaceMatrixFour);
viewMatrixParam.SetValue(ViewMatrix);
cascadeFarPlanesParam.SetValue(CascadeFarPlanes);
directionalLightColorParam.SetValue(DirectionalLightColor);
directionalLightDirectionParam.SetValue(DirectionalLightDirection);
eyePositionParam.SetValue(EyePosition); eyePositionParam.SetValue(EyePosition);
} }
void CacheEffectParameters() void CacheEffectParameters()
{ {
gPositionParam = Parameters["gPosition"]; gPositionParam = Parameters["gPosition"];
gAlbedoParam = Parameters["gAlbedo"]; gAlbedoParam = Parameters["gAlbedo"];
gNormalParam = Parameters["gNormal"]; gNormalParam = Parameters["gNormal"];
gMetallicRoughnessParam = Parameters["gMetallicRoughness"]; gMetallicRoughnessParam = Parameters["gMetallicRoughness"];
eyePositionParam = Parameters["EyePosition"]; shadowMapOneParam = Parameters["shadowMapOne"];
shadowMapTwoParam = Parameters["shadowMapTwo"];
shadowMapThreeParam = Parameters["shadowMapThree"];
shadowMapFourParam = Parameters["shadowMapFour"];
lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"];
lightSpaceMatrixTwoParam = Parameters["LightSpaceMatrixTwo"];
lightSpaceMatrixThreeParam = Parameters["LightSpaceMatrixThree"];
lightSpaceMatrixFourParam = Parameters["LightSpaceMatrixFour"];
viewMatrixParam = Parameters["ViewMatrix"];
cascadeFarPlanesParam = Parameters["CascadeFarPlanes"];
directionalLightDirectionParam = Parameters["DirectionalLightDirection"];
directionalLightColorParam = Parameters["DirectionalLightColor"];
eyePositionParam = Parameters["EyePosition"];
} }
} }
} }

View File

@ -0,0 +1,31 @@
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class DeferredPBR_AmbientLightEffect : Effect
{
EffectParameter gPositionParam;
EffectParameter gAlbedoParam;
public Texture2D GPosition { get; set; }
public Texture2D GAlbedo { get; set; }
public DeferredPBR_AmbientLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_AmbientLightEffect)
{
CacheEffectParameters();
}
protected override void OnApply()
{
gPositionParam.SetValue(GPosition);
gAlbedoParam.SetValue(GAlbedo);
}
void CacheEffectParameters()
{
gPositionParam = Parameters["gPosition"];
gAlbedoParam = Parameters["gAlbedo"];
}
}
}

View File

@ -0,0 +1,161 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class DeferredPBR_DirectionalLightEffect : Effect
{
EffectParameter gPositionParam;
EffectParameter gAlbedoParam;
EffectParameter gNormalParam;
EffectParameter gMetallicRoughnessParam;
EffectParameter shadowMapOneParam;
EffectParameter shadowMapTwoParam;
EffectParameter shadowMapThreeParam;
EffectParameter shadowMapFourParam;
EffectParameter eyePositionParam;
EffectParameter directionalLightColorParam;
EffectParameter directionalLightDirectionParam;
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 GMetallicRoughness { 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 readonly float[] CascadeFarPlanes;
public int 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 DeferredPBR_DirectionalLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_DirectionalLightEffect)
{
CascadeFarPlanes = new float[4];
CacheEffectParameters();
}
public DeferredPBR_DirectionalLightEffect(DeferredPBR_DirectionalLightEffect cloneSource) : base(cloneSource)
{
GPosition = cloneSource.GPosition;
GAlbedo = cloneSource.GAlbedo;
GNormal = cloneSource.GNormal;
GMetallicRoughness = cloneSource.GMetallicRoughness;
ShadowMapOne = cloneSource.ShadowMapOne;
ShadowMapTwo = cloneSource.ShadowMapTwo;
ShadowMapThree = cloneSource.ShadowMapThree;
ShadowMapFour = cloneSource.ShadowMapFour;
EyePosition = cloneSource.EyePosition;
DirectionalLightDirection = cloneSource.DirectionalLightDirection;
DirectionalLightColor = cloneSource.DirectionalLightColor;
CascadeFarPlanes = new float[4];
for (int i = 0 ; i < 4; i++)
{
CascadeFarPlanes[i] = cloneSource.CascadeFarPlanes[i];
}
ShadowMapSize = cloneSource.ShadowMapSize;
LightSpaceMatrixOne = cloneSource.LightSpaceMatrixOne;
LightSpaceMatrixTwo = cloneSource.LightSpaceMatrixTwo;
LightSpaceMatrixThree = cloneSource.LightSpaceMatrixThree;
LightSpaceMatrixFour = cloneSource.LightSpaceMatrixFour;
ViewMatrix = cloneSource.ViewMatrix;
}
public override Effect Clone()
{
return new DeferredPBR_DirectionalLightEffect(this);
}
protected override void OnApply()
{
gPositionParam.SetValue(GPosition);
gAlbedoParam.SetValue(GAlbedo);
gNormalParam.SetValue(GNormal);
gMetallicRoughnessParam.SetValue(GMetallicRoughness);
shadowMapOneParam.SetValue(ShadowMapOne);
shadowMapTwoParam.SetValue(ShadowMapTwo);
shadowMapThreeParam.SetValue(ShadowMapThree);
shadowMapFourParam.SetValue(ShadowMapFour);
eyePositionParam.SetValue(EyePosition);
directionalLightDirectionParam.SetValue(DirectionalLightDirection);
directionalLightColorParam.SetValue(DirectionalLightColor);
cascadeFarPlanesParam.SetValue(CascadeFarPlanes);
shadowMapSizeParam.SetValue(ShadowMapSize);
lightSpaceMatrixOneParam.SetValue(LightSpaceMatrixOne);
lightSpaceMatrixTwoParam.SetValue(LightSpaceMatrixTwo);
lightSpaceMatrixThreeParam.SetValue(LightSpaceMatrixThree);
lightSpaceMatrixFourParam.SetValue(LightSpaceMatrixFour);
viewMatrixParam.SetValue(ViewMatrix);
}
void CacheEffectParameters()
{
gPositionParam = Parameters["gPosition"];
gAlbedoParam = Parameters["gAlbedo"];
gNormalParam = Parameters["gNormal"];
gMetallicRoughnessParam = Parameters["gMetallicRoughness"];
shadowMapOneParam = Parameters["shadowMapOne"];
shadowMapTwoParam = Parameters["shadowMapTwo"];
shadowMapThreeParam = Parameters["shadowMapThree"];
shadowMapFourParam = Parameters["shadowMapFour"];
eyePositionParam = Parameters["EyePosition"];
directionalLightDirectionParam = Parameters["DirectionalLightDirection"];
directionalLightColorParam = Parameters["DirectionalLightColor"];
cascadeFarPlanesParam = Parameters["CascadeFarPlanes"];
shadowMapSizeParam = Parameters["ShadowMapSize"];
lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"];
lightSpaceMatrixTwoParam = Parameters["LightSpaceMatrixTwo"];
lightSpaceMatrixThreeParam = Parameters["LightSpaceMatrixThree"];
lightSpaceMatrixFourParam = Parameters["LightSpaceMatrixFour"];
viewMatrixParam = Parameters["ViewMatrix"];
}
}
}

View File

@ -0,0 +1,77 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class DeferredPBR_PointLightEffect : Effect
{
EffectParameter gPositionParam;
EffectParameter gAlbedoParam;
EffectParameter gNormalParam;
EffectParameter gMetallicRoughnessParam;
EffectParameter eyePositionParam;
EffectParameter pointLightColorParam;
EffectParameter pointLightPositionParam;
public Texture2D GPosition { get; set; }
public Texture2D GAlbedo { get; set; }
public Texture2D GNormal { get; set; }
public Texture2D GMetallicRoughness { get; set; }
public Vector3 EyePosition { get; set; }
public Vector3 PointLightPosition { get; set; }
public Vector3 PointLightColor { get; set; }
public DeferredPBR_PointLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_PointLightEffect)
{
CacheEffectParameters();
}
public DeferredPBR_PointLightEffect(DeferredPBR_PointLightEffect cloneSource) : base(cloneSource)
{
GPosition = cloneSource.GPosition;
GAlbedo = cloneSource.GAlbedo;
GNormal = cloneSource.GNormal;
GMetallicRoughness = cloneSource.GMetallicRoughness;
EyePosition = cloneSource.EyePosition;
PointLightPosition = cloneSource.PointLightPosition;
PointLightColor = cloneSource.PointLightColor;
}
public override Effect Clone()
{
return new DeferredPBR_PointLightEffect(this);
}
protected override void OnApply()
{
gPositionParam.SetValue(GPosition);
gAlbedoParam.SetValue(GAlbedo);
gNormalParam.SetValue(GNormal);
gMetallicRoughnessParam.SetValue(GMetallicRoughness);
eyePositionParam.SetValue(EyePosition);
pointLightPositionParam.SetValue(PointLightPosition);
pointLightColorParam.SetValue(PointLightColor);
}
void CacheEffectParameters()
{
gPositionParam = Parameters["gPosition"];
gAlbedoParam = Parameters["gAlbedo"];
gNormalParam = Parameters["gNormal"];
gMetallicRoughnessParam = Parameters["gMetallicRoughness"];
eyePositionParam = Parameters["EyePosition"];
pointLightPositionParam = Parameters["PointLightPosition"];
pointLightColorParam = Parameters["PointLightColor"];
}
}
}

View File

@ -1,47 +0,0 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
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 lightDirectionsParam;
readonly EffectParameter lightColorsParam;
public DirectionalLightCollection(EffectParameter lightDirectionsParam, EffectParameter lightColorsParam)
{
this.lightDirectionsParam = lightDirectionsParam;
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;
lightDirectionsParam.SetValue(directions);
lightColorsParam.SetValue(colors);
}
}
}
}

Binary file not shown.

BIN
Effects/FXB/DeferredPBR_AmbientLightEffect.fxb (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Effects/FXB/DeferredPBR_PointLightEffect.fxb (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Effects/FXB/ToneMapEffect.fxb (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -2,12 +2,16 @@
static const float PI = 3.141592653589793; static const float PI = 3.141592653589793;
static const int MAX_POINT_LIGHTS = 64; static const int MAX_POINT_LIGHTS = 64;
static const int MAX_DIRECTIONAL_LIGHTS = 4; static const int NUM_SHADOW_CASCADES = 4;
DECLARE_TEXTURE(gPosition, 0); DECLARE_TEXTURE(gPosition, 0);
DECLARE_TEXTURE(gAlbedo, 1); DECLARE_TEXTURE(gAlbedo, 1);
DECLARE_TEXTURE(gNormal, 2); DECLARE_TEXTURE(gNormal, 2);
DECLARE_TEXTURE(gMetallicRoughness, 3); DECLARE_TEXTURE(gMetallicRoughness, 3);
DECLARE_TEXTURE(shadowMapOne, 4);
DECLARE_TEXTURE(shadowMapTwo, 5);
DECLARE_TEXTURE(shadowMapThree, 6);
DECLARE_TEXTURE(shadowMapFour, 7);
BEGIN_CONSTANTS BEGIN_CONSTANTS
@ -16,19 +20,45 @@ BEGIN_CONSTANTS
float3 PointLightPositions[MAX_POINT_LIGHTS] _ps(c1) _cb(c1); float3 PointLightPositions[MAX_POINT_LIGHTS] _ps(c1) _cb(c1);
float3 PointLightColors[MAX_POINT_LIGHTS] _ps(c65) _cb(c65); float3 PointLightColors[MAX_POINT_LIGHTS] _ps(c65) _cb(c65);
float3 DirectionalLightDirections[MAX_DIRECTIONAL_LIGHTS] _ps(c129) _cb(c129); float3 DirectionalLightDirection _ps(c129) _cb(c129);
float3 DirectionalLightColors[MAX_DIRECTIONAL_LIGHTS] _ps(c133) _cb(c133); float3 DirectionalLightColor _ps(c130) _cb(c130);
float CascadeFarPlanes[NUM_SHADOW_CASCADES] _ps(c131) _cb(c131);
MATRIX_CONSTANTS MATRIX_CONSTANTS
float4x4 LightSpaceMatrixOne _ps(c135) _cb(c135);
float4x4 LightSpaceMatrixTwo _ps(c139) _cb(c139);
float4x4 LightSpaceMatrixThree _ps(c143) _cb(c143);
float4x4 LightSpaceMatrixFour _ps(c147) _cb(c147);
// used to select shadow cascade
float4x4 ViewMatrix _ps(c151) _cb(c151);
END_CONSTANTS END_CONSTANTS
struct VertexInput
{
float4 Position : POSITION;
float2 TexCoord : TEXCOORD;
};
struct PixelInput struct PixelInput
{ {
float4 Position : SV_POSITION; float4 Position : SV_POSITION;
float2 TexCoord : TEXCOORD0; float2 TexCoord : TEXCOORD0;
}; };
PixelInput main_vs(VertexInput input)
{
PixelInput output;
output.Position = input.Position;
output.TexCoord = input.TexCoord;
return output;
}
// Pixel Shader // Pixel Shader
float3 FresnelSchlick(float cosTheta, float3 F0) float3 FresnelSchlick(float cosTheta, float3 F0)
@ -71,17 +101,119 @@ float GeometrySmith(float3 N, float3 V, float3 L, float roughness)
return ggx1 * ggx2; return ggx1 * ggx2;
} }
float ComputeShadow(float3 positionWorldSpace, float3 N, float 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
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;
}
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
float inc = 1.0 / 1024.0;
float shadowFactor = 0;
for (int row = -1; row <= 1; row++)
{
for (int col = -1; col <= 1; col++)
{
float closestDepth;
if (shadowCascadeIndex == 0)
{
closestDepth = SAMPLE_TEXTURE(shadowMapOne, projectionCoords.xy + float2(row, col) * inc).r;
}
else if (shadowCascadeIndex == 1)
{
closestDepth = SAMPLE_TEXTURE(shadowMapTwo, projectionCoords.xy + float2(row, col) * inc).r;
}
else if (shadowCascadeIndex == 2)
{
closestDepth = SAMPLE_TEXTURE(shadowMapThree, projectionCoords.xy + float2(row, col) * inc).r;
}
else
{
closestDepth = SAMPLE_TEXTURE(shadowMapFour, projectionCoords.xy + float2(row, col) * inc).r;
}
shadowFactor += projectionCoords.z - bias > closestDepth ? 1.0 : 0.0;
}
}
shadowFactor /= 9.0;
if (projectionCoords.z > 1.0)
{
shadowFactor = 1.0;
}
return shadowFactor;
// float currentDepth = projectionCoords.z;
// if (currentDepth > 1.0)
// {
// return 0.0;
// }
// if (currentDepth - bias > closestDepth)
// {
// return 1.0;
// }
// else
// {
// return 0.0;
// }
}
float3 ComputeLight( float3 ComputeLight(
float3 lightDir, float3 L,
float3 radiance, float3 radiance,
float3 F0, float3 F0,
float3 V, float3 V,
float3 N, float3 N,
float3 albedo, float3 albedo,
float metallic, float metallic,
float roughness float roughness,
float shadow
) { ) {
float3 L = normalize(lightDir);
float3 H = normalize(V + L); float3 H = normalize(V + L);
float NDF = DistributionGGX(N, H, roughness); float NDF = DistributionGGX(N, H, roughness);
@ -98,7 +230,7 @@ float3 ComputeLight(
kD *= 1.0 - metallic; kD *= 1.0 - metallic;
float NdotL = max(dot(N, L), 0.0); float NdotL = max(dot(N, L), 0.0);
return (kD * albedo / PI + specular) * radiance * NdotL; return (kD * albedo / PI + specular) * radiance * NdotL * shadow;
} }
float4 ComputeColor( float4 ComputeColor(
@ -120,21 +252,20 @@ float4 ComputeColor(
for (int i = 0; i < MAX_POINT_LIGHTS; i++) for (int i = 0; i < MAX_POINT_LIGHTS; i++)
{ {
float3 lightDir = PointLightPositions[i] - worldPosition; float3 lightDir = PointLightPositions[i] - worldPosition;
float3 L = normalize(lightDir);
float distance = length(lightDir); float distance = length(lightDir);
float attenuation = 1.0 / (distance * distance); float attenuation = 1.0 / (distance * distance);
float3 radiance = PointLightColors[i] * attenuation; float3 radiance = PointLightColors[i] * attenuation;
Lo += ComputeLight(lightDir, radiance, F0, V, N, albedo, metallic, roughness); Lo += ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, 1.0);
} }
// directional light // directional light
for (int i = 0; i < MAX_DIRECTIONAL_LIGHTS; i++) float3 L = normalize(DirectionalLightDirection);
{ float3 radiance = DirectionalLightColor;
float3 lightDir = DirectionalLightDirections[i];
float3 radiance = DirectionalLightColors[i];
Lo += ComputeLight(lightDir, radiance, F0, V, N, albedo, metallic, roughness); float shadow = ComputeShadow(worldPosition, N, L);
} Lo += ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, (1.0 - shadow));
float3 ambient = float3(0.03, 0.03, 0.03) * albedo; // * AO; float3 ambient = float3(0.03, 0.03, 0.03) * albedo; // * AO;
float3 color = ambient + Lo; float3 color = ambient + Lo;
@ -148,13 +279,13 @@ float4 ComputeColor(
float4 main_ps(PixelInput input) : SV_TARGET0 float4 main_ps(PixelInput input) : SV_TARGET0
{ {
float3 fragPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb; float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb;
float3 normal = SAMPLE_TEXTURE(gNormal, input.TexCoord).xyz; float3 normal = SAMPLE_TEXTURE(gNormal, input.TexCoord).xyz;
float3 albedo = SAMPLE_TEXTURE(gAlbedo, input.TexCoord).rgb; float3 albedo = SAMPLE_TEXTURE(gAlbedo, input.TexCoord).rgb;
float2 metallicRoughness = SAMPLE_TEXTURE(gMetallicRoughness, input.TexCoord).rg; float2 metallicRoughness = SAMPLE_TEXTURE(gMetallicRoughness, input.TexCoord).rg;
return ComputeColor( return ComputeColor(
fragPosition, worldPosition,
normal, normal,
albedo, albedo,
metallicRoughness.r, metallicRoughness.r,
@ -166,6 +297,7 @@ Technique DeferredPBR
{ {
Pass Pass
{ {
VertexShader = compile vs_3_0 main_vs();
PixelShader = compile ps_3_0 main_ps(); PixelShader = compile ps_3_0 main_ps();
} }
} }

View File

@ -0,0 +1,54 @@
#include "Macros.fxh" // from FNA
DECLARE_TEXTURE(gPosition, 0);
DECLARE_TEXTURE(gAlbedo, 1);
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 ComputeColor(
float3 worldPosition,
float3 albedo
) {
float3 color = float3(0.03, 0.03, 0.03) * albedo;
return float4(color, 1.0);
}
float4 main_ps(PixelInput input) : SV_TARGET0
{
float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb;
float3 albedo = SAMPLE_TEXTURE(gAlbedo, input.TexCoord).rgb;
return ComputeColor(
worldPosition,
albedo
);
}
Technique DeferredPBR_Ambient
{
Pass
{
VertexShader = compile vs_3_0 main_vs();
PixelShader = compile ps_3_0 main_ps();
}
}

View File

@ -0,0 +1,252 @@
#include "Macros.fxh" //from FNA
#include "Lighting.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);
DECLARE_TEXTURE(shadowMapFour, 7);
BEGIN_CONSTANTS
float3 EyePosition _ps(c0) _cb(c0);
float3 DirectionalLightDirection _ps(c1) _cb(c1);
float3 DirectionalLightColor _ps(c2) _cb(c2);
float CascadeFarPlanes[NUM_SHADOW_CASCADES] _ps(c3) _cb(c3);
float ShadowMapSize _ps(c7) _cb(c7);
MATRIX_CONSTANTS
float4x4 LightSpaceMatrixOne _ps(c8) _cb(c8);
float4x4 LightSpaceMatrixTwo _ps(c12) _cb(c12);
float4x4 LightSpaceMatrixThree _ps(c16) _cb(c16);
float4x4 LightSpaceMatrixFour _ps(c20) _cb(c20);
// used to select shadow cascade
float4x4 ViewMatrix _ps(c24) _cb(c24);
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;
float2 TexCoord : TEXCOORD;
};
struct PixelInput
{
float4 Position : SV_POSITION;
float2 TexCoord : TEXCOORD0;
};
PixelInput main_vs(VertexInput input)
{
PixelInput output;
output.Position = input.Position;
output.TexCoord = input.TexCoord;
return output;
}
// Pixel Shader
// 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
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;
}
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);
}
else if (shadowCascadeIndex == 1)
{
visibility = PoissonCoord(SAMPLER(shadowMapTwo), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias);
}
else if (shadowCascadeIndex == 2)
{
visibility = PoissonCoord(SAMPLER(shadowMapThree), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias);
}
else
{
visibility = PoissonCoord(SAMPLER(shadowMapFour), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias);
}
return visibility;
}
float4 ComputeColor(
float3 worldPosition,
float3 worldNormal,
float3 albedo,
float metallic,
float roughness
) {
float3 V = normalize(EyePosition - worldPosition);
float3 N = normalize(worldNormal);
float3 F0 = float3(0.04, 0.04, 0.04);
F0 = lerp(F0, albedo, metallic);
float3 L = normalize(DirectionalLightDirection);
float3 radiance = DirectionalLightColor;
float shadow = ComputeShadow(worldPosition, N, L);
float3 color = ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, shadow);
return float4(color, 1.0);
}
float4 main_ps(PixelInput input) : SV_TARGET0
{
float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb;
float3 normal = SAMPLE_TEXTURE(gNormal, input.TexCoord).xyz;
float3 albedo = SAMPLE_TEXTURE(gAlbedo, input.TexCoord).rgb;
float2 metallicRoughness = SAMPLE_TEXTURE(gMetallicRoughness, input.TexCoord).rg;
return ComputeColor(
worldPosition,
normal,
albedo,
metallicRoughness.r,
metallicRoughness.g
);
}
Technique DeferredPBR_Directional
{
Pass
{
VertexShader = compile vs_3_0 main_vs();
PixelShader = compile ps_3_0 main_ps();
}
}

View File

@ -79,10 +79,10 @@ PixelOutput NonePS(PixelInput input)
{ {
PixelOutput output; PixelOutput output;
output.gPosition = float4(input.PositionWorld, 0.0); output.gPosition = float4(input.PositionWorld, 1.0);
output.gNormal = float4(normalize(input.NormalWorld), 0.0); output.gNormal = float4(normalize(input.NormalWorld), 1.0);
output.gAlbedo = float4(AlbedoValue, 1.0); output.gAlbedo = float4(AlbedoValue, 1.0);
output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0); output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0);
return output; return output;
} }
@ -91,10 +91,10 @@ PixelOutput AlbedoPS(PixelInput input)
{ {
PixelOutput output; PixelOutput output;
output.gPosition = float4(input.PositionWorld, 0.0); output.gPosition = float4(input.PositionWorld, 1.0);
output.gNormal = float4(normalize(input.NormalWorld), 0.0); output.gNormal = float4(normalize(input.NormalWorld), 1.0);
output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord); output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord);
output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0); output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0);
return output; return output;
} }
@ -103,8 +103,8 @@ PixelOutput MetallicRoughnessPS(PixelInput input)
{ {
PixelOutput output; PixelOutput output;
output.gPosition = float4(input.PositionWorld, 0.0); output.gPosition = float4(input.PositionWorld, 1.0);
output.gNormal = float4(normalize(input.NormalWorld), 0.0); output.gNormal = float4(normalize(input.NormalWorld), 1.0);
output.gAlbedo = float4(AlbedoValue, 1.0); output.gAlbedo = float4(AlbedoValue, 1.0);
output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord); output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord);
@ -115,10 +115,10 @@ PixelOutput NormalPS(PixelInput input)
{ {
PixelOutput output; PixelOutput output;
output.gPosition = float4(input.PositionWorld, 0.0); output.gPosition = float4(input.PositionWorld, 1.0);
output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0); output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0);
output.gAlbedo = float4(AlbedoValue, 1.0); output.gAlbedo = float4(AlbedoValue, 1.0);
output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0); output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0);
return output; return output;
} }
@ -127,8 +127,8 @@ PixelOutput AlbedoMetallicRoughnessPS(PixelInput input)
{ {
PixelOutput output; PixelOutput output;
output.gPosition = float4(input.PositionWorld, 0.0); output.gPosition = float4(input.PositionWorld, 1.0);
output.gNormal = float4(normalize(input.NormalWorld), 0.0); output.gNormal = float4(normalize(input.NormalWorld), 1.0);
output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord); output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord);
output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord); output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord);
@ -139,10 +139,10 @@ PixelOutput AlbedoNormalPS(PixelInput input)
{ {
PixelOutput output; PixelOutput output;
output.gPosition = float4(input.PositionWorld, 0.0); output.gPosition = float4(input.PositionWorld, 1.0);
output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0); output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0);
output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord); output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord);
output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0); output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0);
return output; return output;
} }
@ -151,8 +151,8 @@ PixelOutput MetallicRoughnessNormalPS(PixelInput input)
{ {
PixelOutput output; PixelOutput output;
output.gPosition = float4(input.PositionWorld, 0.0); output.gPosition = float4(input.PositionWorld, 1.0);
output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0); output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0);
output.gAlbedo = float4(AlbedoValue, 1.0); output.gAlbedo = float4(AlbedoValue, 1.0);
output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord); output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord);
@ -163,8 +163,8 @@ PixelOutput AlbedoMetallicRoughnessNormalMapPS(PixelInput input)
{ {
PixelOutput output; PixelOutput output;
output.gPosition = float4(input.PositionWorld, 0.0); output.gPosition = float4(input.PositionWorld, 1.0);
output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0); output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0);
output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord); output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord);
output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord); output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord);

View File

@ -0,0 +1,91 @@
#include "Macros.fxh" //from FNA
#include "Lighting.fxh"
DECLARE_TEXTURE(gPosition, 0);
DECLARE_TEXTURE(gAlbedo, 1);
DECLARE_TEXTURE(gNormal, 2);
DECLARE_TEXTURE(gMetallicRoughness, 3);
BEGIN_CONSTANTS
float3 EyePosition _ps(c0) _cb(c0);
float3 PointLightPosition _ps(c1) _cb(c1);
float3 PointLightColor _ps(c2) _cb(c2);
MATRIX_CONSTANTS
END_CONSTANTS
struct VertexInput
{
float4 Position : POSITION;
float2 TexCoord : TEXCOORD;
};
struct PixelInput
{
float4 Position : SV_POSITION;
float2 TexCoord : TEXCOORD0;
};
PixelInput main_vs(VertexInput input)
{
PixelInput output;
output.Position = input.Position;
output.TexCoord = input.TexCoord;
return output;
}
// Pixel Shader
float4 ComputeColor(
float3 worldPosition,
float3 worldNormal,
float3 albedo,
float metallic,
float roughness
) {
float3 V = normalize(EyePosition - worldPosition);
float3 N = normalize(worldNormal);
float3 F0 = float3(0.04, 0.04, 0.04);
F0 = lerp(F0, albedo, metallic);
float3 lightDir = PointLightPosition - worldPosition;
float3 L = normalize(lightDir);
float distance = length(lightDir);
float attenuation = 1.0 / (distance * distance);
float3 radiance = PointLightColor * attenuation;
float3 color = ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, 1.0);
return float4(color, 1.0);
}
float4 main_ps(PixelInput input) : SV_TARGET0
{
float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb;
float3 normal = SAMPLE_TEXTURE(gNormal, input.TexCoord).xyz;
float3 albedo = SAMPLE_TEXTURE(gAlbedo, input.TexCoord).rgb;
float2 metallicRoughness = SAMPLE_TEXTURE(gMetallicRoughness, input.TexCoord).rg;
return ComputeColor(
worldPosition,
normal,
albedo,
metallicRoughness.r,
metallicRoughness.g
);
}
Technique DeferredPBR_Point
{
Pass
{
VertexShader = compile vs_3_0 main_vs();
PixelShader = compile ps_3_0 main_ps();
}
}

71
Effects/HLSL/Lighting.fxh Normal file
View File

@ -0,0 +1,71 @@
static const float PI = 3.141592653589793;
float3 FresnelSchlick(float cosTheta, float3 F0)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
float DistributionGGX(float3 N, float3 H, float roughness)
{
float a = roughness * roughness;
float a2 = a * a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH * NdotH;
float num = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return num / denom;
}
float GeometrySchlickGGX(float NdotV, float roughness)
{
float r = (roughness + 1.0);
float k = (r * r) / 8.0;
float num = NdotV;
float denom = NdotV * (1.0 - k) + k;
return num / denom;
}
float GeometrySmith(float3 N, float3 V, float3 L, float roughness)
{
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
float3 ComputeLight(
float3 L,
float3 radiance,
float3 F0,
float3 V,
float3 N,
float3 albedo,
float metallic,
float roughness,
float visibility
) {
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 * visibility;
}

View File

@ -54,4 +54,5 @@
#define SAMPLE_TEXTURE(Name, texCoord) tex2D(Name##Sampler, texCoord) #define SAMPLE_TEXTURE(Name, texCoord) tex2D(Name##Sampler, texCoord)
#define SAMPLE_CUBEMAP(Name, texCoord) texCUBE(Name##Sampler, texCoord) #define SAMPLE_CUBEMAP(Name, texCoord) texCUBE(Name##Sampler, texCoord)
#define SAMPLE_CUBEMAP_LOD(Name, texCoord) texCUBElod(Name##Sampler, texCoord) #define SAMPLE_CUBEMAP_LOD(Name, texCoord) texCUBElod(Name##Sampler, texCoord)
#define SAMPLER(Name) Name##Sampler
#endif #endif

View File

@ -16,21 +16,29 @@ struct VertexShaderInput
struct VertexShaderOutput struct VertexShaderOutput
{ {
float4 Position : SV_Position; float4 Position : SV_Position;
float Depth : TEXCOORD0;
}; };
VertexShaderOutput main_vs(VertexShaderInput input) VertexShaderOutput main_vs(VertexShaderInput input)
{ {
VertexShaderOutput output; VertexShaderOutput output;
output.Position = mul(float4(input.Position.xyz, 1.0), ModelViewProjection); output.Position = mul(input.Position, ModelViewProjection);
output.Depth = output.Position.z;
return output; return output;
} }
float4 main_ps(VertexShaderOutput input) : SV_TARGET0
{
return float4(input.Depth, 0.0, 0.0, 0.0);
}
Technique SimpleDepth Technique SimpleDepth
{ {
Pass Pass
{ {
VertexShader = compile vs_3_0 main_vs(); VertexShader = compile vs_3_0 main_vs();
PixelShader = compile ps_3_0 main_ps();
} }
} }

View File

@ -0,0 +1,20 @@
sampler TextureSampler : register(s0);
float4 main_ps(float2 texCoord : TEXCOORD0) : COLOR0
{
float3 color = tex2D(TextureSampler, texCoord).xyz;
color = color / (color + float3(1.0, 1.0, 1.0));
float exposureConstant = 1.0 / 2.2;
color = pow(color, float3(exposureConstant, exposureConstant, exposureConstant));
return float4(color, 1.0);
}
Technique DeferredPBR
{
Pass
{
PixelShader = compile ps_3_0 main_ps();
}
}

View File

@ -3,7 +3,7 @@ using Microsoft.Xna.Framework.Graphics;
namespace Kav namespace Kav
{ {
public class PBREffect : Effect, TransformEffect, PointLightEffect, DirectionalLightEffect public class PBREffect : Effect, TransformEffect, PointLightEffect
{ {
EffectParameter worldParam; EffectParameter worldParam;
EffectParameter worldViewProjectionParam; EffectParameter worldViewProjectionParam;
@ -31,7 +31,6 @@ namespace Kav
Matrix view = Matrix.Identity; Matrix view = Matrix.Identity;
Matrix projection = Matrix.Identity; Matrix projection = Matrix.Identity;
PointLightCollection pointLightCollection; PointLightCollection pointLightCollection;
DirectionalLightCollection directionalLightCollection;
Vector3 albedo; Vector3 albedo;
float metallic; float metallic;
@ -84,14 +83,6 @@ namespace Kav
private 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 public Vector3 Albedo
{ {
get { return albedo; } get { return albedo; }
@ -204,11 +195,6 @@ namespace Kav
Parameters["PositionLightColors"], Parameters["PositionLightColors"],
MaxPointLights MaxPointLights
); );
directionalLightCollection = new DirectionalLightCollection(
Parameters["LightDirections"],
Parameters["DirectionLightColors"]
);
} }
protected PBREffect(PBREffect cloneSource) : base(cloneSource) protected PBREffect(PBREffect cloneSource) : base(cloneSource)
@ -230,16 +216,6 @@ namespace Kav
PointLights[i] = cloneSource.PointLights[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; AlbedoTexture = cloneSource.AlbedoTexture;
NormalTexture = cloneSource.NormalTexture; NormalTexture = cloneSource.NormalTexture;
EmissionTexture = cloneSource.EmissionTexture; EmissionTexture = cloneSource.EmissionTexture;

View File

@ -42,6 +42,7 @@ namespace Kav
dirtyFlags |= EffectDirtyFlags.WorldViewProj; dirtyFlags |= EffectDirtyFlags.WorldViewProj;
} }
} }
public SimpleDepthEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.SimpleDepthEffect) public SimpleDepthEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.SimpleDepthEffect)
{ {
CacheEffectParameters(); CacheEffectParameters();
@ -51,8 +52,8 @@ namespace Kav
{ {
if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0) if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0)
{ {
Matrix.Multiply(ref model, ref view, out Matrix modelView); Matrix.Multiply(ref view, ref projection, out Matrix viewProjection);
Matrix.Multiply(ref modelView, ref projection, out Matrix worldViewProj); Matrix.Multiply(ref model, ref viewProjection, out Matrix worldViewProj);
modelViewProjectionParam.SetValue(worldViewProj); modelViewProjectionParam.SetValue(worldViewProj);

View File

@ -15,9 +15,21 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Effects\FXB\DeferredPBR_AmbientLightEffect.fxb">
<LogicalName>Kav.Resources.DeferredPBR_AmbientLightEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\DeferredPBR_PointLightEffect.fxb">
<LogicalName>Kav.Resources.DeferredPBR_PointLightEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\DeferredPBR_DirectionalLightEffect.fxb">
<LogicalName>Kav.Resources.DeferredPBR_DirectionalLightEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\DeferredPBR_GBufferEffect.fxb"> <EmbeddedResource Include="Effects\FXB\DeferredPBR_GBufferEffect.fxb">
<LogicalName>Kav.Resources.DeferredPBR_GBufferEffect.fxb</LogicalName> <LogicalName>Kav.Resources.DeferredPBR_GBufferEffect.fxb</LogicalName>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\ToneMapEffect.fxb">
<LogicalName>Kav.Resources.ToneMapEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\DeferredPBREffect.fxb"> <EmbeddedResource Include="Effects\FXB\DeferredPBREffect.fxb">
<LogicalName>Kav.Resources.DeferredPBREffect.fxb</LogicalName> <LogicalName>Kav.Resources.DeferredPBREffect.fxb</LogicalName>
</EmbeddedResource> </EmbeddedResource>

View File

@ -15,9 +15,21 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Effects\FXB\DeferredPBR_AmbientLightEffect.fxb">
<LogicalName>Kav.Resources.DeferredPBR_AmbientLightEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\DeferredPBR_PointLightEffect.fxb">
<LogicalName>Kav.Resources.DeferredPBR_PointLightEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\DeferredPBR_DirectionalLightEffect.fxb">
<LogicalName>Kav.Resources.DeferredPBR_DirectionalLightEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\DeferredPBR_GBufferEffect.fxb"> <EmbeddedResource Include="Effects\FXB\DeferredPBR_GBufferEffect.fxb">
<LogicalName>Kav.Resources.DeferredPBR_GBufferEffect.fxb</LogicalName> <LogicalName>Kav.Resources.DeferredPBR_GBufferEffect.fxb</LogicalName>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\ToneMapEffect.fxb">
<LogicalName>Kav.Resources.ToneMapEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\DeferredPBREffect.fxb"> <EmbeddedResource Include="Effects\FXB\DeferredPBREffect.fxb">
<LogicalName>Kav.Resources.DeferredPBREffect.fxb</LogicalName> <LogicalName>Kav.Resources.DeferredPBREffect.fxb</LogicalName>
</EmbeddedResource> </EmbeddedResource>

View File

@ -12,7 +12,7 @@ namespace Kav
{ {
get get
{ {
return Matrix.CreateLookAt(-Direction * 100f, Vector3.Zero, Vector3.Up); return Matrix.CreateLookAt(Direction * 100f, Vector3.Zero, Vector3.Up);
} }
} }

29
README.md Normal file
View File

@ -0,0 +1,29 @@
# Kav
A 3D renderer built on top of FNA.
## Roadmap
Essential
- [x] PBR shading
- [x] Deferred rendering
- [x] Point lighting
- [x] Directional lighting
- [x] Directional shadow maps
- [x] Cascading shadow maps
- [x] Tone map shader
- [x] Poisson soft shadowing
- [ ] Frustum culling
- [ ] Shadow-casting point lights
- [ ] Parabolic lights
- [ ] Skyboxes
- [ ] Screen-space reflection
Nice-To-Haves
- [ ] Anti-aliasing
- [ ] Image-based lighting
- [ ] Volumetric lighting
- [ ] Volumetric smoke
- [ ]

View File

@ -6,38 +6,84 @@ namespace Kav
{ {
public class Renderer public class Renderer
{ {
private const int MAX_SHADOW_CASCADES = 4;
private int ShadowMapSize { get; }
private GraphicsDevice GraphicsDevice { get; } private GraphicsDevice GraphicsDevice { get; }
private int RenderDimensionsX { get; } private int RenderDimensionsX { get; }
private int RenderDimensionsY { get; } private int RenderDimensionsY { get; }
private RenderTarget2D DepthRenderTarget { get; } private VertexBuffer FullscreenTriangle { get; }
private int NumShadowCascades { get; }
private RenderTarget2D ColorRenderTarget { get; }
private RenderTarget2D DirectionalRenderTarget { get; }
private RenderTarget2D[] ShadowRenderTargets { get; }
private DeferredPBREffect DeferredPBREffect { get; }
private DeferredPBR_AmbientLightEffect DeferredAmbientLightEffect { get; }
private DeferredPBR_PointLightEffect DeferredPointLightEffect { get; }
private DeferredPBR_DirectionalLightEffect DeferredDirectionalLightEffect { get; }
private SimpleDepthEffect SimpleDepthEffect { get; } private SimpleDepthEffect SimpleDepthEffect { get; }
private Effect ToneMapEffect { get; }
private RenderTarget2D gPosition { get; } private RenderTarget2D gPosition { get; }
private RenderTarget2D gNormal { get; } private RenderTarget2D gNormal { get; }
private RenderTarget2D gAlbedo { get; } private RenderTarget2D gAlbedo { get; }
private RenderTarget2D gMetallicRoughness { get; } private RenderTarget2D gMetallicRoughness { get; }
private RenderTarget2D deferredRenderTarget { get; }
private RenderTargetBinding[] GBuffer { get; } private RenderTargetBinding[] GBuffer { get; }
private DeferredPBREffect DeferredPBREffect { get; }
private SpriteBatch SpriteBatch { get; } private SpriteBatch SpriteBatch { get; }
public Renderer(GraphicsDevice graphicsDevice, int renderDimensionsX, int renderDimensionsY) public Renderer(
{ GraphicsDevice graphicsDevice,
int renderDimensionsX,
int renderDimensionsY,
int numShadowCascades,
int shadowMapSize
) {
GraphicsDevice = graphicsDevice; GraphicsDevice = graphicsDevice;
RenderDimensionsX = renderDimensionsX; RenderDimensionsX = renderDimensionsX;
RenderDimensionsY = renderDimensionsY; RenderDimensionsY = renderDimensionsY;
DepthRenderTarget = new RenderTarget2D( ShadowMapSize = shadowMapSize;
GraphicsDevice,
NumShadowCascades = (int)MathHelper.Clamp(numShadowCascades, 1, MAX_SHADOW_CASCADES);
ShadowRenderTargets = new RenderTarget2D[numShadowCascades];
for (var i = 0; i < numShadowCascades; i++)
{
ShadowRenderTargets[i] = new RenderTarget2D(
GraphicsDevice,
ShadowMapSize,
ShadowMapSize,
false,
SurfaceFormat.Single,
DepthFormat.Depth24
);
}
ColorRenderTarget = new RenderTarget2D(
graphicsDevice,
renderDimensionsX, renderDimensionsX,
renderDimensionsY, renderDimensionsY,
false, false,
SurfaceFormat.HalfSingle, // unused SurfaceFormat.Color,
DepthFormat.Depth24 DepthFormat.None,
0,
RenderTargetUsage.PreserveContents
);
DirectionalRenderTarget = new RenderTarget2D(
graphicsDevice,
renderDimensionsX,
renderDimensionsY,
false,
SurfaceFormat.Color,
DepthFormat.None,
0,
RenderTargetUsage.PreserveContents
); );
gPosition = new RenderTarget2D( gPosition = new RenderTarget2D(
@ -83,28 +129,32 @@ namespace Kav
new RenderTargetBinding(gMetallicRoughness) new RenderTargetBinding(gMetallicRoughness)
}; };
deferredRenderTarget = new RenderTarget2D(
GraphicsDevice,
renderDimensionsX,
renderDimensionsY
);
SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice); SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice);
DeferredPBREffect = new DeferredPBREffect(GraphicsDevice); DeferredPBREffect = new DeferredPBREffect(GraphicsDevice);
DeferredAmbientLightEffect = new DeferredPBR_AmbientLightEffect(GraphicsDevice);
DeferredPointLightEffect = new DeferredPBR_PointLightEffect(GraphicsDevice);
DeferredDirectionalLightEffect = new DeferredPBR_DirectionalLightEffect(GraphicsDevice);
DeferredDirectionalLightEffect.ShadowMapSize = ShadowMapSize;
ToneMapEffect = new Effect(graphicsDevice, Resources.ToneMapEffect);
SpriteBatch = new SpriteBatch(GraphicsDevice); FullscreenTriangle = new VertexBuffer(GraphicsDevice, typeof(VertexPositionTexture), 3, BufferUsage.WriteOnly);
FullscreenTriangle.SetData(new VertexPositionTexture[3] {
new VertexPositionTexture(new Vector3(-1, -3, 0), new Vector2(0, 2)),
new VertexPositionTexture(new Vector3(-1, 1, 0), new Vector2(0, 0)),
new VertexPositionTexture(new Vector3(3, 1, 0), new Vector2(2, 0))
});
GraphicsDevice.SetRenderTarget(deferredRenderTarget); SpriteBatch = new SpriteBatch(graphicsDevice);
graphicsDevice.Clear(Color.White);
GraphicsDevice.SetRenderTarget(null);
} }
public void DeferredRender( public void DeferredRender(
Camera camera, PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms, IEnumerable<(Model, Matrix)> modelTransforms,
IEnumerable<PointLight> pointLights, IEnumerable<PointLight> pointLights,
IEnumerable<DirectionalLight> directionalLights DirectionalLight directionalLight
) { ) {
// g-buffer pass
GraphicsDevice.SetRenderTargets(GBuffer); GraphicsDevice.SetRenderTargets(GBuffer);
GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0); GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0);
GraphicsDevice.DepthStencilState = DepthStencilState.Default; GraphicsDevice.DepthStencilState = DepthStencilState.Default;
@ -143,53 +193,186 @@ namespace Kav
} }
} }
GraphicsDevice.SetRenderTarget(null); GraphicsDevice.SetRenderTarget(ColorRenderTarget);
GraphicsDevice.Clear(Color.CornflowerBlue); GraphicsDevice.Clear(Color.Black);
GraphicsDevice.BlendState = BlendState.Additive;
GraphicsDevice.DepthStencilState = DepthStencilState.None;
DeferredPBREffect.GPosition = gPosition; DeferredAmbientLightEffect.GPosition = gPosition;
DeferredPBREffect.GAlbedo = gAlbedo; DeferredAmbientLightEffect.GAlbedo = gAlbedo;
DeferredPBREffect.GNormal = gNormal;
DeferredPBREffect.GMetallicRoughness = gMetallicRoughness; foreach (var pass in DeferredAmbientLightEffect.CurrentTechnique.Passes)
DeferredPBREffect.EyePosition = Matrix.Invert(camera.View).Translation; {
pass.Apply();
GraphicsDevice.SetVertexBuffer(FullscreenTriangle);
GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
}
DeferredPointLightEffect.EyePosition = camera.Position;
int i = 0;
foreach (var pointLight in pointLights) foreach (var pointLight in pointLights)
{ {
if (i > DeferredPBREffect.MaxPointLights) { break; } PointLightRender(pointLight);
DeferredPBREffect.PointLights[i] = pointLight;
i++;
} }
i = 0; DirectionalLightRender(camera, modelTransforms, directionalLight);
foreach (var directionalLight in directionalLights) // return;
{ // GraphicsDevice.SetRenderTarget(null);
if (i > DeferredPBREffect.MaxDirectionalLights) { break; } // SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied);
DeferredPBREffect.DirectionalLights[i] = directionalLight; // SpriteBatch.Draw(DirectionalRenderTarget, Vector2.Zero, Color.White);
i++; // SpriteBatch.End();
}
SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, null, null, DeferredPBREffect); GraphicsDevice.SetRenderTarget(null);
SpriteBatch.Draw(deferredRenderTarget, Vector2.Zero, Color.White); GraphicsDevice.Clear(Color.Black);
SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, null, null, null, ToneMapEffect);
SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White);
SpriteBatch.End(); SpriteBatch.End();
} }
public void Render( private void PointLightRender(PointLight pointLight)
Camera camera, {
IEnumerable<(Model, Matrix)> modelTransforms, DeferredPointLightEffect.GPosition = gPosition;
IEnumerable<PointLight> pointLights, DeferredPointLightEffect.GAlbedo = gAlbedo;
IEnumerable<DirectionalLight> directionalLights DeferredPointLightEffect.GNormal = gNormal;
) { DeferredPointLightEffect.GMetallicRoughness = gMetallicRoughness;
Render(camera.View, camera.Projection, modelTransforms, pointLights, directionalLights);
DeferredPointLightEffect.PointLightPosition = pointLight.Position;
DeferredPointLightEffect.PointLightColor =
pointLight.Color.ToVector3() * pointLight.Intensity;
foreach (var pass in DeferredPointLightEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.SetVertexBuffer(FullscreenTriangle);
GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
}
} }
// for shadow mapping private void DirectionalLightRender(
public void DepthRender(IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight) PerspectiveCamera camera,
{ IEnumerable<(Model, Matrix)> modelTransforms,
GraphicsDevice.SetRenderTarget(DepthRenderTarget); DirectionalLight directionalLight
GraphicsDevice.Clear(ClearOptions.DepthBuffer, Color.Black, 1, 0); ) {
// 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));
SimpleDepthEffect.View = directionalLight.View; // divide the view frustum
SimpleDepthEffect.Projection = directionalLight.Projection; 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;
}
DeferredDirectionalLightEffect.GPosition = gPosition;
DeferredDirectionalLightEffect.GAlbedo = gAlbedo;
DeferredDirectionalLightEffect.GNormal = gNormal;
DeferredDirectionalLightEffect.GMetallicRoughness = gMetallicRoughness;
DeferredDirectionalLightEffect.ShadowMapOne = ShadowRenderTargets[0];
if (NumShadowCascades > 1)
{
DeferredDirectionalLightEffect.ShadowMapTwo = ShadowRenderTargets[1];
}
if (NumShadowCascades > 2)
{
DeferredDirectionalLightEffect.ShadowMapThree = ShadowRenderTargets[2];
}
if (NumShadowCascades > 3)
{
DeferredDirectionalLightEffect.ShadowMapFour = ShadowRenderTargets[3];
}
DeferredDirectionalLightEffect.DirectionalLightDirection = directionalLight.Direction;
DeferredDirectionalLightEffect.DirectionalLightColor =
directionalLight.Color.ToVector3() * directionalLight.Intensity;
DeferredDirectionalLightEffect.ViewMatrix = camera.View;
DeferredDirectionalLightEffect.EyePosition = Matrix.Invert(camera.View).Translation;
GraphicsDevice.SetRenderTarget(ColorRenderTarget);
GraphicsDevice.BlendState = BlendState.Additive;
foreach (EffectPass pass in DeferredDirectionalLightEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.SetVertexBuffer(FullscreenTriangle);
GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
}
}
private void RenderShadowMap(
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
DirectionalLight directionalLight,
int shadowCascadeIndex
) {
GraphicsDevice.SetRenderTarget(ShadowRenderTargets[shadowCascadeIndex]);
GraphicsDevice.Clear(Color.White);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.BlendState = BlendState.Opaque;
var cameraBoundingFrustum = new BoundingFrustum(camera.View * camera.Projection);
Vector3[] frustumCorners = cameraBoundingFrustum.GetCorners();
Vector3 frustumCenter = Vector3.Zero;
for (var i = 0; i < frustumCorners.Length; i++)
{
frustumCenter += frustumCorners[i];
}
frustumCenter /= 8f;
var lightView = Matrix.CreateLookAt(frustumCenter + directionalLight.Direction, frustumCenter, Vector3.Backward);
for (var i = 0; i < frustumCorners.Length; i++)
{
frustumCorners[i] = Vector3.Transform(frustumCorners[i], lightView);
}
BoundingBox lightBox = BoundingBox.CreateFromPoints(frustumCorners);
SimpleDepthEffect.View = lightView;
SimpleDepthEffect.Projection = Matrix.CreateOrthographicOffCenter(
lightBox.Min.X,
lightBox.Max.X,
lightBox.Min.Y,
lightBox.Max.Y,
-lightBox.Max.Z - 10f, // TODO: near clip plane needs scene AABB info to get rid of this magic value
-lightBox.Min.Z
);
var lightSpaceMatrix = SimpleDepthEffect.View * SimpleDepthEffect.Projection;
if (shadowCascadeIndex == 0)
{
DeferredDirectionalLightEffect.LightSpaceMatrixOne = lightSpaceMatrix;
}
else if (shadowCascadeIndex == 1)
{
DeferredDirectionalLightEffect.LightSpaceMatrixTwo = lightSpaceMatrix;
}
else if (shadowCascadeIndex == 2)
{
DeferredDirectionalLightEffect.LightSpaceMatrixThree = lightSpaceMatrix;
}
else if (shadowCascadeIndex == 3)
{
DeferredDirectionalLightEffect.LightSpaceMatrixFour = lightSpaceMatrix;
}
DeferredDirectionalLightEffect.CascadeFarPlanes[shadowCascadeIndex] = camera.FarPlane;
foreach (var (model, transform) in modelTransforms) foreach (var (model, transform) in modelTransforms)
{ {
@ -220,6 +403,15 @@ namespace Kav
} }
} }
public void Render(
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
IEnumerable<PointLight> pointLights,
IEnumerable<DirectionalLight> directionalLights
) {
Render(camera.View, camera.Projection, modelTransforms, pointLights, directionalLights);
}
private void Render( private void Render(
Matrix view, Matrix view,
Matrix projection, Matrix projection,
@ -254,17 +446,6 @@ 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) foreach (var pass in meshPart.Effect.CurrentTechnique.Passes)
{ {
pass.Apply(); pass.Apply();

View File

@ -4,6 +4,41 @@ namespace Kav
{ {
internal class Resources internal class Resources
{ {
public static byte[] DeferredPBR_AmbientLightEffect
{
get
{
if (ambientLightEffect == null)
{
ambientLightEffect = GetResource("DeferredPBR_AmbientLightEffect");
}
return ambientLightEffect;
}
}
public static byte[] DeferredPBR_PointLightEffect
{
get
{
if (pointLightEffect == null)
{
pointLightEffect = GetResource("DeferredPBR_PointLightEffect");
}
return pointLightEffect;
}
}
public static byte[] DeferredPBR_DirectionalLightEffect
{
get
{
if (directionalLightEffect == null)
{
directionalLightEffect = GetResource("DeferredPBR_DirectionalLightEffect");
}
return directionalLightEffect;
}
}
public static byte[] DeferredPBR_GBufferEffect public static byte[] DeferredPBR_GBufferEffect
{ {
get get
@ -16,6 +51,18 @@ namespace Kav
} }
} }
public static byte[] ToneMapEffect
{
get
{
if (toneMapEffect == null)
{
toneMapEffect = GetResource("ToneMapEffect");
}
return toneMapEffect;
}
}
public static byte[] DeferredPBREffect public static byte[] DeferredPBREffect
{ {
get get
@ -52,7 +99,11 @@ namespace Kav
} }
} }
private static byte[] ambientLightEffect;
private static byte[] pointLightEffect;
private static byte[] directionalLightEffect;
private static byte[] gBufferEffect; private static byte[] gBufferEffect;
private static byte[] toneMapEffect;
private static byte[] deferredPBREffect; private static byte[] deferredPBREffect;
private static byte[] pbrEffect; private static byte[] pbrEffect;
private static byte[] simpleDepthEffect; private static byte[] simpleDepthEffect;