starting on point shadows

pull/3/head
cosmonaut 2020-10-19 03:01:37 -07:00
parent 06e5523996
commit 0a7698c315
14 changed files with 387 additions and 60 deletions

View File

@ -9,22 +9,28 @@ namespace Kav
EffectParameter gAlbedoParam;
EffectParameter gNormalParam;
EffectParameter gMetallicRoughnessParam;
EffectParameter shadowMapParam;
EffectParameter eyePositionParam;
EffectParameter pointLightColorParam;
EffectParameter pointLightPositionParam;
EffectParameter farPlaneParam;
public Texture2D GPosition { get; set; }
public Texture2D GAlbedo { get; set; }
public Texture2D GNormal { get; set; }
public Texture2D GMetallicRoughness { get; set; }
public TextureCube ShadowMap { get; set; }
public Vector3 EyePosition { get; set; }
public Vector3 PointLightPosition { get; set; }
public Vector3 PointLightColor { get; set; }
public float FarPlane { get; set; }
public DeferredPBR_PointLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_PointLightEffect)
{
CacheEffectParameters();
@ -32,15 +38,20 @@ namespace Kav
public DeferredPBR_PointLightEffect(DeferredPBR_PointLightEffect cloneSource) : base(cloneSource)
{
CacheEffectParameters();
GPosition = cloneSource.GPosition;
GAlbedo = cloneSource.GAlbedo;
GNormal = cloneSource.GNormal;
GMetallicRoughness = cloneSource.GMetallicRoughness;
ShadowMap = cloneSource.ShadowMap;
EyePosition = cloneSource.EyePosition;
PointLightPosition = cloneSource.PointLightPosition;
PointLightColor = cloneSource.PointLightColor;
FarPlane = cloneSource.FarPlane;
}
public override Effect Clone()
@ -54,11 +65,14 @@ namespace Kav
gAlbedoParam.SetValue(GAlbedo);
gNormalParam.SetValue(GNormal);
gMetallicRoughnessParam.SetValue(GMetallicRoughness);
shadowMapParam.SetValue(ShadowMap);
eyePositionParam.SetValue(EyePosition);
pointLightPositionParam.SetValue(PointLightPosition);
pointLightColorParam.SetValue(PointLightColor);
farPlaneParam.SetValue(FarPlane);
}
void CacheEffectParameters()
@ -67,11 +81,14 @@ namespace Kav
gAlbedoParam = Parameters["gAlbedo"];
gNormalParam = Parameters["gNormal"];
gMetallicRoughnessParam = Parameters["gMetallicRoughness"];
shadowMapParam = Parameters["shadowMap"];
eyePositionParam = Parameters["EyePosition"];
pointLightPositionParam = Parameters["PointLightPosition"];
pointLightColorParam = Parameters["PointLightColor"];
farPlaneParam = Parameters["FarPlane"];
}
}
}

Binary file not shown.

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

Binary file not shown.

BIN
Effects/FXB/SimpleDepthEffect.fxb (Stored with Git LFS)

Binary file not shown.

View File

@ -1,10 +1,12 @@
#include "Macros.fxh" //from FNA
#include "Lighting.fxh"
#include "Shadow.fxh"
DECLARE_TEXTURE(gPosition, 0);
DECLARE_TEXTURE(gAlbedo, 1);
DECLARE_TEXTURE(gNormal, 2);
DECLARE_TEXTURE(gMetallicRoughness, 3);
DECLARE_CUBEMAP(shadowMap, 4);
BEGIN_CONSTANTS
@ -13,6 +15,8 @@ BEGIN_CONSTANTS
float3 PointLightPosition _ps(c1) _cb(c1);
float3 PointLightColor _ps(c2) _cb(c2);
float FarPlane _ps(c3) _cb(c3);
MATRIX_CONSTANTS
END_CONSTANTS
@ -60,9 +64,11 @@ float4 ComputeColor(
float attenuation = 1.0 / (distance * distance);
float3 radiance = PointLightColor * attenuation;
float3 color = ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, 1.0);
float shadow = HardPointShadow(worldPosition, N, L, PointLightPosition, SAMPLER(shadowMap), FarPlane);
float3 color = ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, shadow);
return float4(color, 1.0);
//return float4(shadow, shadow, shadow, 1.0);
}
float4 main_ps(PixelInput input) : SV_TARGET0

View File

@ -0,0 +1,46 @@
#include "Macros.fxh"
BEGIN_CONSTANTS
float4x4 Model _vs(c0) _cb(c0);
float4x4 ModelViewProjection _vs(c4) _cb(c4);
float3 LightPosition _ps(c0) _cb(c8);
float FarPlane _ps(c1) _cb(c9);
END_CONSTANTS
struct VertexShaderInput
{
float4 Position : POSITION;
};
struct VertexShaderOutput
{
float4 Position : SV_Position;
float3 PositionWorld : TEXCOORD0;
};
VertexShaderOutput main_vs(VertexShaderInput input)
{
VertexShaderOutput output;
output.Position = mul(input.Position, ModelViewProjection);
output.PositionWorld = mul(input.Position, Model);
return output;
}
float4 main_ps(VertexShaderOutput input) : SV_TARGET0
{
float lightDistance = length(input.PositionWorld - LightPosition);
lightDistance /= FarPlane;
return float4(lightDistance, 0.0, 0.0, 0.0);
}
Technique SimpleDepth
{
Pass
{
VertexShader = compile vs_3_0 main_vs();
PixelShader = compile ps_3_0 main_ps();
}
}

View File

@ -120,3 +120,23 @@ float HardShadow(
return (currentDepth - bias > closestDepth ? 0.25 : 1.0);
}
float HardPointShadow(
float3 positionWorldSpace,
float3 N,
float3 L,
float3 lightPosition,
sampler shadowMap,
float farPlane
) {
float3 lightToFrag = positionWorldSpace - lightPosition;
float closestDepth = texCUBE(shadowMap, lightToFrag).r;
closestDepth *= farPlane;
float currentDepth = length(lightToFrag);
float bias = max(0.05 * (1.0 - dot(N, L)), 0.005);
return (currentDepth - bias > closestDepth ? 0.25 : 1.0);
//return closestDepth / farPlane;
}

View File

@ -24,7 +24,7 @@ VertexShaderOutput main_vs(VertexShaderInput input)
VertexShaderOutput output;
output.Position = mul(input.Position, ModelViewProjection);
output.Depth = output.Position.z;
output.Depth = output.Position.z / output.Position.w;
return output;
}

View File

@ -0,0 +1,90 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class LinearDepthEffect : Effect
{
EffectParameter modelParam;
EffectParameter modelViewProjectionParam;
EffectParameter lightPositionParam;
EffectParameter farPlaneParam;
EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
Matrix model;
Matrix view;
Matrix projection;
public Vector3 LightPosition { get; set; }
public float FarPlane { get; set; }
public Matrix Model
{
get { return model; }
set
{
model = value;
dirtyFlags |= EffectDirtyFlags.World;
dirtyFlags |= EffectDirtyFlags.WorldViewProj;
}
}
public Matrix View
{
get { return view; }
set
{
view = value;
dirtyFlags |= EffectDirtyFlags.WorldViewProj;
}
}
public Matrix Projection
{
get { return projection; }
set
{
projection = value;
dirtyFlags |= EffectDirtyFlags.WorldViewProj;
}
}
public LinearDepthEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.LinearDepthEffect)
{
CacheEffectParameters();
}
protected override void OnApply()
{
if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0)
{
Matrix.Multiply(ref view, ref projection, out Matrix viewProjection);
Matrix.Multiply(ref model, ref viewProjection, out Matrix worldViewProj);
modelViewProjectionParam.SetValue(worldViewProj);
dirtyFlags &= ~EffectDirtyFlags.WorldViewProj;
}
if ((dirtyFlags & EffectDirtyFlags.World) != 0)
{
modelParam.SetValue(model);
dirtyFlags &= ~EffectDirtyFlags.World;
}
lightPositionParam.SetValue(LightPosition);
farPlaneParam.SetValue(FarPlane);
}
private void CacheEffectParameters()
{
modelParam = Parameters["Model"];
modelViewProjectionParam = Parameters["ModelViewProjection"];
lightPositionParam = Parameters["LightPosition"];
farPlaneParam = Parameters["FarPlane"];
}
}
}

View File

@ -42,6 +42,9 @@
<EmbeddedResource Include="Effects\FXB\SimpleDepthEffect.fxb">
<LogicalName>Kav.Resources.SimpleDepthEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\LinearDepthEffect.fxb">
<LogicalName>Kav.Resources.LinearDepthEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\SkyboxEffect.fxb">
<LogicalName>Kav.Resources.SkyboxEffect.fxb</LogicalName>
</EmbeddedResource>

View File

@ -42,12 +42,15 @@
<EmbeddedResource Include="Effects\FXB\SimpleDepthEffect.fxb">
<LogicalName>Kav.Resources.SimpleDepthEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\SkyboxEffect.fxb">
<LogicalName>Kav.Resources.SkyboxEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Models\UnitCube.glb">
<LogicalName>Kav.Resources.UnitCube.glb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\LinearDepthEffect.fxb">
<LogicalName>Kav.Resources.LinearDepthEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\SkyboxEffect.fxb">
<LogicalName>Kav.Resources.SkyboxEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Models\UnitCube.glb">
<LogicalName>Kav.Resources.UnitCube.glb</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@ -17,7 +17,7 @@ Essential
- [ ] Frustum culling
- [ ] Shadow-casting point lights
- [ ] Parabolic lights
- [ ] Skyboxes
- [x] Skyboxes
- [ ] Screen-space reflection
Nice-To-Haves

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@ -29,6 +30,7 @@ namespace Kav
private DeferredPBR_DirectionalLightEffect DeferredDirectionalLightEffect { get; }
private Deferred_ToonEffect Deferred_ToonEffect { get; }
private SimpleDepthEffect SimpleDepthEffect { get; }
private LinearDepthEffect LinearDepthEffect { get; }
private Effect ToneMapEffect { get; }
private SkyboxEffect SkyboxEffect { get; }
@ -36,6 +38,7 @@ namespace Kav
private RenderTarget2D gNormal { get; }
private RenderTarget2D gAlbedo { get; }
private RenderTarget2D gMetallicRoughness { get; }
private RenderTargetCube PointShadowCubeMap { get; }
private RenderTargetBinding[] GBuffer { get; }
@ -136,7 +139,16 @@ namespace Kav
new RenderTargetBinding(gMetallicRoughness)
};
PointShadowCubeMap = new RenderTargetCube(
GraphicsDevice,
shadowMapSize,
false,
SurfaceFormat.Single,
DepthFormat.Depth24
);
SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice);
LinearDepthEffect = new LinearDepthEffect(GraphicsDevice);
DeferredPBREffect = new DeferredPBREffect(GraphicsDevice);
Deferred_GBufferEffect = new DeferredPBR_GBufferEffect(GraphicsDevice);
@ -181,7 +193,7 @@ namespace Kav
foreach (var pointLight in pointLights)
{
PointLightRender(pointLight);
PointLightRender(camera, modelTransforms, pointLight);
}
DirectionalLightRender(camera, modelTransforms, directionalLight);
@ -197,6 +209,7 @@ namespace Kav
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
AmbientLight ambientLight,
IEnumerable<PointLight> pointLights,
DirectionalLight directionalLight,
TextureCube skybox
) {
@ -210,7 +223,11 @@ namespace Kav
GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
AmbientLightRender(ambientLight);
DirectionalLightToonRender(camera, modelTransforms, directionalLight);
foreach (var pointLight in pointLights)
{
PointLightRender(camera, modelTransforms, pointLight);
}
//DirectionalLightToonRender(camera, modelTransforms, directionalLight);
SkyboxRender(camera, skybox);
GraphicsDevice.SetRenderTarget(null);
@ -351,17 +368,29 @@ namespace Kav
}
}
private void PointLightRender(PointLight pointLight)
{
private void PointLightRender(
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
PointLight pointLight
) {
RenderPointShadows(camera, modelTransforms, pointLight);
GraphicsDevice.SetRenderTarget(ColorRenderTarget);
GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
GraphicsDevice.BlendState = BlendState.Additive;
DeferredPointLightEffect.GPosition = gPosition;
DeferredPointLightEffect.GAlbedo = gAlbedo;
DeferredPointLightEffect.GNormal = gNormal;
DeferredPointLightEffect.GMetallicRoughness = gMetallicRoughness;
DeferredPointLightEffect.ShadowMap = PointShadowCubeMap;
DeferredPointLightEffect.PointLightPosition = pointLight.Position;
DeferredPointLightEffect.PointLightColor =
pointLight.Color.ToVector3() * pointLight.Intensity;
DeferredPointLightEffect.FarPlane = 25f; // FIXME: magic value
foreach (var pass in DeferredPointLightEffect.CurrentTechnique.Passes)
{
pass.Apply();
@ -375,7 +404,7 @@ namespace Kav
IEnumerable<(Model, Matrix)> modelTransforms,
DirectionalLight directionalLight
) {
RenderShadows(camera, modelTransforms, directionalLight, DeferredDirectionalLightEffect);
RenderDirectionalShadows(camera, modelTransforms, directionalLight, DeferredDirectionalLightEffect);
DeferredDirectionalLightEffect.GPosition = gPosition;
DeferredDirectionalLightEffect.GAlbedo = gAlbedo;
@ -419,7 +448,7 @@ namespace Kav
IEnumerable<(Model, Matrix)> modelTransforms,
DirectionalLight directionalLight
) {
RenderShadows(camera, modelTransforms, directionalLight, Deferred_ToonEffect);
RenderDirectionalShadows(camera, modelTransforms, directionalLight, Deferred_ToonEffect);
GraphicsDevice.SetRenderTarget(ColorRenderTarget);
GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
@ -461,7 +490,7 @@ namespace Kav
}
}
private void RenderShadows(
private void RenderDirectionalShadows(
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
DirectionalLight directionalLight,
@ -485,14 +514,14 @@ namespace Kav
);
// TODO: This is tightly coupled to the effect and it sucks
RenderShadowMap(shadowCamera, modelTransforms, directionalLight, effect, i);
RenderDirectionalShadowMap(shadowCamera, modelTransforms, directionalLight, effect, i);
effect.CascadeFarPlanes[i] = farPlane;
previousFarPlane = farPlane;
}
}
private void RenderShadowMap(
private void RenderDirectionalShadowMap(
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
DirectionalLight directionalLight,
@ -580,5 +609,102 @@ namespace Kav
}
}
}
private void RenderPointShadows(
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
PointLight pointLight
) {
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.BlendState = BlendState.Opaque;
LinearDepthEffect.Projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.PiOver2,
1,
0.1f,
25f // FIXME: magic value
);
LinearDepthEffect.FarPlane = 25f;
LinearDepthEffect.LightPosition = pointLight.Position;
foreach (CubeMapFace face in Enum.GetValues(typeof(CubeMapFace)))
{
GraphicsDevice.SetRenderTarget(PointShadowCubeMap, face);
Vector3 targetDirection;
Vector3 targetUpDirection;
switch(face)
{
case CubeMapFace.PositiveX:
targetDirection = Vector3.Right;
targetUpDirection = Vector3.Up;
break;
case CubeMapFace.NegativeX:
targetDirection = Vector3.Left;
targetUpDirection = Vector3.Up;
break;
case CubeMapFace.PositiveY:
targetDirection = Vector3.Up;
targetUpDirection = Vector3.Forward;
break;
case CubeMapFace.NegativeY:
targetDirection = Vector3.Down;
targetUpDirection = Vector3.Backward;
break;
case CubeMapFace.PositiveZ:
targetDirection = Vector3.Backward;
targetUpDirection = Vector3.Up;
break;
case CubeMapFace.NegativeZ:
targetDirection = Vector3.Forward;
targetUpDirection = Vector3.Up;
break;
default:
targetDirection = Vector3.Right;
targetUpDirection = Vector3.Up;
break;
}
LinearDepthEffect.View = Matrix.CreateLookAt(
pointLight.Position,
pointLight.Position + targetDirection,
targetUpDirection
);
foreach (var (model, transform) in modelTransforms)
{
foreach (var modelMesh in model.Meshes)
{
foreach (var meshPart in modelMesh.MeshParts)
{
GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer);
GraphicsDevice.Indices = meshPart.IndexBuffer;
LinearDepthEffect.Model = transform;
foreach (var pass in LinearDepthEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawIndexedPrimitives(
PrimitiveType.TriangleList,
0,
0,
meshPart.VertexBuffer.VertexCount,
0,
meshPart.Triangles.Length
);
}
}
}
}
}
}
}
}

View File

@ -10,7 +10,7 @@ namespace Kav
{
if (ambientLightEffect == null)
{
ambientLightEffect = GetResource("DeferredPBR_AmbientLightEffect.fxb");
ambientLightEffect = GetResource("DeferredPBR_AmbientLightEffect.fxb");
}
return ambientLightEffect;
}
@ -21,7 +21,7 @@ namespace Kav
{
if (pointLightEffect == null)
{
pointLightEffect = GetResource("DeferredPBR_PointLightEffect.fxb");
pointLightEffect = GetResource("DeferredPBR_PointLightEffect.fxb");
}
return pointLightEffect;
}
@ -33,7 +33,7 @@ namespace Kav
{
if (directionalLightEffect == null)
{
directionalLightEffect = GetResource("DeferredPBR_DirectionalLightEffect.fxb");
directionalLightEffect = GetResource("DeferredPBR_DirectionalLightEffect.fxb");
}
return directionalLightEffect;
}
@ -45,7 +45,7 @@ namespace Kav
{
if (gBufferEffect == null)
{
gBufferEffect = GetResource("DeferredPBR_GBufferEffect.fxb");
gBufferEffect = GetResource("DeferredPBR_GBufferEffect.fxb");
}
return gBufferEffect;
}
@ -57,7 +57,7 @@ namespace Kav
{
if (toneMapEffect == null)
{
toneMapEffect = GetResource("ToneMapEffect.fxb");
toneMapEffect = GetResource("ToneMapEffect.fxb");
}
return toneMapEffect;
}
@ -69,7 +69,7 @@ namespace Kav
{
if (deferredToonEffect == null)
{
deferredToonEffect = GetResource("Deferred_ToonEffect.fxb");
deferredToonEffect = GetResource("Deferred_ToonEffect.fxb");
}
return deferredToonEffect;
}
@ -81,7 +81,7 @@ namespace Kav
{
if (deferredPBREffect == null)
{
deferredPBREffect = GetResource("DeferredPBREffect.fxb");
deferredPBREffect = GetResource("DeferredPBREffect.fxb");
}
return deferredPBREffect;
}
@ -93,7 +93,7 @@ namespace Kav
{
if (pbrEffect == null)
{
pbrEffect = GetResource("PBREffect.fxb");
pbrEffect = GetResource("PBREffect.fxb");
}
return pbrEffect;
}
@ -105,36 +105,48 @@ namespace Kav
{
if (simpleDepthEffect == null)
{
simpleDepthEffect = GetResource("SimpleDepthEffect.fxb");
simpleDepthEffect = GetResource("SimpleDepthEffect.fxb");
}
return simpleDepthEffect;
}
}
public static byte[] SkyboxEffect
{
get
{
if (skyboxEffect == null)
{
skyboxEffect = GetResource("SkyboxEffect.fxb");
}
return skyboxEffect;
}
}
public static byte[] UnitCubeModel
{
get
{
if (unitCubeModel == null)
{
unitCubeModel = GetResource("UnitCube.glb");
}
return unitCubeModel;
}
}
public static byte[] LinearDepthEffect
{
get
{
if (linearDepthEffect == null)
{
linearDepthEffect = GetResource("LinearDepthEffect.fxb");
}
return linearDepthEffect;
}
}
public static byte[] SkyboxEffect
{
get
{
if (skyboxEffect == null)
{
skyboxEffect = GetResource("SkyboxEffect.fxb");
}
return skyboxEffect;
}
}
public static byte[] UnitCubeModel
{
get
{
if (unitCubeModel == null)
{
unitCubeModel = GetResource("UnitCube.glb");
}
return unitCubeModel;
}
}
private static byte[] ambientLightEffect;
private static byte[] pointLightEffect;
private static byte[] directionalLightEffect;
@ -144,14 +156,15 @@ namespace Kav
private static byte[] deferredPBREffect;
private static byte[] pbrEffect;
private static byte[] simpleDepthEffect;
private static byte[] skyboxEffect;
private static byte[] unitCubeModel;
private static byte[] linearDepthEffect;
private static byte[] skyboxEffect;
private static byte[] unitCubeModel;
private static byte[] GetResource(string name)
{
Stream stream = typeof(Resources).Assembly.GetManifestResourceStream(
"Kav.Resources." + name
"Kav.Resources." + name
);
using (MemoryStream ms = new MemoryStream())
{