Compare commits

..

64 Commits

Author SHA1 Message Date
cosmonaut f5cdd0b566 fix crash on 0 instance 2020-12-14 00:52:40 -08:00
cosmonaut 808d50ccc2 more sprite mesh rendering 2020-12-13 22:03:06 -08:00
cosmonaut d537993530 flat sprite lighting in deferred pipeline 2020-12-13 22:03:06 -08:00
cosmonaut 3338bf3b06 experimenting with g buffer sprites 2020-12-13 22:03:06 -08:00
cosmonaut 16ecb5afa6 fix bad palette crush FLT_MAX 2020-12-12 21:07:22 -08:00
cosmonaut 51d7f3e772 fix diffuse lit effect not discarding transparency 2020-12-12 01:23:08 -08:00
cosmonaut 3a0aa8894d draw zero alpha pixels in g buffer 2020-12-12 00:17:10 -08:00
cosmonaut c468d6276d texture atlas stuff 2020-12-11 21:05:54 -08:00
cosmonaut d476f254e4 fix sprite mesh normals 2020-12-11 18:52:05 -08:00
cosmonaut 2ff5cb1ca5 decouple sprite mesh from textures 2020-12-11 18:22:54 -08:00
cosmonaut 40ca0402d6 instance data struct 2020-12-10 17:38:18 -08:00
cosmonaut 0ebad486c5 diffuse sprite effect takes uv data 2020-12-10 13:53:55 -08:00
cosmonaut 8ff6e26887 tweak to mesh sprite draws 2020-12-10 00:34:06 -08:00
cosmonaut 7f0bf47f07 implement vertical mesh sprite flipping 2020-12-09 22:31:09 -08:00
cosmonaut 2f589e584d allow sprite flipping on meshsprite 2020-12-09 22:28:51 -08:00
cosmonaut 7964d8a171 store right vector on the camera 2020-12-09 17:47:54 -08:00
cosmonaut d5d0a38ff1 turn point lights into classes and give them a shadow map 2020-12-09 17:10:41 -08:00
cosmonaut ee909ba94e tweak point light rendering 2020-12-09 16:28:40 -08:00
cosmonaut 0060c22d72 point light sphere optimization 2020-12-09 16:10:53 -08:00
cosmonaut 2c113ed642 palette crush effect 2020-12-09 12:44:53 -08:00
cosmonaut b1395babfe offset texture rendering 2020-12-08 22:20:54 -08:00
cosmonaut ef948cf7fb add quaternion utility function 2020-12-08 17:20:10 -08:00
cosmonaut 9a7fb75ec5 refactor for reusing instance buffers 2020-12-08 16:37:22 -08:00
cosmonaut 0c576668cb add instanced linear depth effect + fix platforms 2020-12-08 16:02:58 -08:00
cosmonaut 367e2795ae decouple billboard constraints from meshsprite 2020-12-08 15:29:28 -08:00
cosmonaut 1723f1dff8 change simpledeptheffect to use texcoord0 2020-12-08 13:33:45 -08:00
cosmonaut 14e07c7476 instanced directional shadows 2020-12-08 03:35:06 -08:00
cosmonaut 4b7d31f2b2 refactor directional shadow data 2020-12-08 03:07:41 -08:00
cosmonaut 4d3c5fc316 more shadow decoupling + change world instance to position instance 2020-12-08 02:49:18 -08:00
cosmonaut 96f6d22896 shadow refactor 2020-12-07 21:36:16 -08:00
cosmonaut 84601379b5 major API refactor 2020-12-07 20:10:27 -08:00
cosmonaut fe222e266f started instance based depth render 2020-12-07 18:46:53 -08:00
cosmonaut bb694d3dbe handle case where there are no instances 2020-12-07 15:51:24 -08:00
cosmonaut e2fdbff7d1 cullable instance calls 2020-12-07 14:58:03 -08:00
cosmonaut 283b078641 instanced g buffer draws work now 2020-12-07 14:42:02 -08:00
cosmonaut b784f9df4b trying different shader semantics 2020-12-07 13:50:32 -08:00
cosmonaut 8b43e8f45e start removing global render procedures 2020-12-07 01:57:57 -08:00
cosmonaut ee8b0c5ee8 started adding support for instanced draws + started decoupling API 2020-12-07 01:30:09 -08:00
cosmonaut c9a4e35816 fix meshsprite normal 2020-12-06 19:45:03 -08:00
cosmonaut 7f986c546a abstract frustum culling 2020-12-06 19:36:00 -08:00
cosmonaut fc09082f1b frustum cull meshsprites 2020-12-06 19:27:46 -08:00
cosmonaut 46f2cad81a optimize mesh sprite index buffer 2020-12-06 19:01:23 -08:00
cosmonaut ca6c91446e new mesh sprite and diffuse lit sprite system 2020-12-06 18:52:14 -08:00
cosmonaut 60ebb19e24 pass ambient light to shader 2020-12-05 19:58:29 -08:00
cosmonaut 47242e4f52 initial diffuse lit sprite effect 2020-12-05 19:47:01 -08:00
cosmonaut ae445d94d3 implement different kinds of billboarding 2020-12-04 18:51:59 -08:00
cosmonaut 665ff6dd44 all public facing renderer functions take an RT 2020-12-04 15:49:35 -08:00
cosmonaut 56cdffdff6 remove broken rotation code 2020-12-04 15:46:30 -08:00
cosmonaut acaafdcdcd batched billboard implementation 2020-12-04 15:39:29 -08:00
cosmonaut d83aacd57f remove anycpu and x86 2020-11-22 16:51:22 -08:00
cosmonaut ed5a7df614 check off frustum culling 2020-10-19 18:24:02 -07:00
cosmonaut 8fa22260d2 frustum culling 2020-10-19 18:22:54 -07:00
cosmonaut cb0baf0bf0 integrate all shadows 2020-10-19 14:00:11 -07:00
cosmonaut 8407a34c37 flip horizontal UVs of cubemap render 2020-10-19 12:48:32 -07:00
cosmonaut 0a7698c315 starting on point shadows 2020-10-19 03:01:37 -07:00
cosmonaut 06e5523996 deferred skybox 2020-10-17 13:53:26 -07:00
cosmonaut 19a61985ca support dithered shadow toggle 2020-10-15 18:19:43 -07:00
cosmonaut 1f10698811 more toon shader control + dithered shading 2020-10-05 15:45:10 -07:00
cosmonaut a914c586b2 experiment with color banding 2020-10-02 17:29:20 -07:00
cosmonaut d370d4e2e4 toon shadows 2020-10-02 12:28:28 -07:00
cosmonaut 632f0a5b06 break shadow computes out into include file 2020-10-02 11:57:13 -07:00
cosmonaut 2fb20747e4 more toon effect + remove material from MeshPart 2020-10-01 22:26:40 -07:00
cosmonaut 565be374bb basic toon shading + controllable ambient light setting 2020-10-01 14:52:19 -07:00
cosmonaut 66d4e5bf6e Directional Light + Shadows + Tone Mapping + One Pass Per Light (#2) 2020-10-01 19:46:25 +00:00
95 changed files with 5120 additions and 678 deletions

1
.gitattributes vendored Normal file
View File

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

View File

@ -2,22 +2,41 @@ 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; }
Transform = transform; public Vector3 Up { get; }
Projection = projection; public Vector3 Right { 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
) {
Position = position;
Forward = forward;
Up = up;
Right = Vector3.Cross(forward, up);
View = Matrix.CreateLookAt(Position, Position + Forward, Up);
FieldOfView = fieldOfView;
AspectRatio = aspectRatio;
NearPlane = nearPlane;
FarPlane = farPlane;
Projection = Matrix.CreatePerspectiveFieldOfView(FieldOfView, AspectRatio, NearPlane, FarPlane);
} }
} }
} }

14
Data/AtlasAnimation.cs Normal file
View File

@ -0,0 +1,14 @@
namespace Kav
{
public struct AtlasAnimation
{
public UVData[] Frames { get; }
public int Framerate { get; }
public AtlasAnimation(UVData[] frames, int framerate)
{
Frames = frames;
Framerate = framerate;
}
}
}

63
Data/InstanceData.cs Normal file
View File

@ -0,0 +1,63 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class InstanceData<T> where T : struct, IVertexType
{
public T[] InstanceDataArray { get; }
public DynamicVertexBuffer VertexBuffer { get; }
public int InstanceCount { get; private set; }
public InstanceData(GraphicsDevice graphicsDevice, int size)
{
InstanceDataArray = new T[size];
VertexBuffer = new DynamicVertexBuffer(
graphicsDevice,
typeof(T),
size,
BufferUsage.WriteOnly
);
InstanceCount = 0;
}
public void AddAndSetData(IEnumerable<T> data)
{
InstanceCount = 0;
foreach (var datum in data)
{
AddData(datum);
}
if (InstanceCount == 0) { throw new System.Exception(); }
SetData();
}
public void AddData(T datum)
{
InstanceDataArray[InstanceCount] = datum;
InstanceCount += 1;
}
public void SetData()
{
if (InstanceCount > 0)
{
VertexBuffer.SetData(
InstanceDataArray,
0,
InstanceCount,
SetDataOptions.NoOverwrite
);
}
}
public void Clear()
{
InstanceCount = 0;
}
}
}

View File

@ -0,0 +1,35 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav.Data
{
public struct MeshSpriteDrawData : IIndexDrawable, ICullable, ITransformable
{
public SpriteMesh MeshSprite { get; }
public Texture2D Texture { get; }
public Texture2D Normal { get; }
public SpriteBillboardConstraint BillboardConstraint { get; }
public Matrix TransformMatrix { get; }
public UVData UVOffset { get; }
public IndexBuffer IndexBuffer => MeshSprite.IndexBuffer;
public VertexBuffer VertexBuffer => MeshSprite.VertexBuffer;
public BoundingBox BoundingBox => MeshSprite.BoundingBox;
public MeshSpriteDrawData(
SpriteMesh meshSprite,
Texture2D texture,
Texture2D normal,
SpriteBillboardConstraint billboardConstraint,
Matrix transformMatrix,
UVData offset
) {
MeshSprite = meshSprite;
Texture = texture;
Normal = normal;
BillboardConstraint = billboardConstraint;
TransformMatrix = transformMatrix;
UVOffset = offset;
}
}
}

96
Data/TextureAtlas.cs Normal file
View File

@ -0,0 +1,96 @@
using Kav.Data;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
using System.IO;
namespace Kav
{
public struct TextureAtlasSlice
{
public int X { get; }
public int Y { get; }
public int W { get; }
public int H { get; }
public UVData UVData { get; }
// for use with tiling
public Vector2 TiledUVOffset { get; }
public TextureAtlasSlice(int x, int y, int w, int h, int totalW, int totalH)
{
X = x;
Y = y;
W = w;
H = h;
UVData = new UVData(new Vector2(x, y), new Vector2(w, h), new Vector2(totalW, totalH));
TiledUVOffset = new Vector2(x / (float)totalW, y / (float)totalH);
}
}
public class TextureAtlas
{
public Texture2D Texture { get; }
protected List<string> Names { get; } = new List<string>();
protected Dictionary<string, TextureAtlasSlice> Slices { get; } = new Dictionary<string, TextureAtlasSlice>();
public TextureAtlas(GraphicsDevice graphicsDevice, FileInfo atlasMetadataFile)
{
var atlasData = CrunchAtlasReader.ReadTextureAtlas(atlasMetadataFile);
var textureData = atlasData.Textures[0];
using var stream = File.OpenRead(Path.Combine(atlasMetadataFile.DirectoryName, textureData.Name + ".png"));
Texture = Texture2D.FromStream(graphicsDevice, stream);
foreach (var slice in textureData.Images)
{
Names.Add(slice.N);
Slices.Add(
slice.N,
new TextureAtlasSlice(
slice.X,
slice.Y,
slice.W,
slice.H,
Texture.Width,
Texture.Height
)
);
}
}
public TextureAtlasSlice Lookup(string name)
{
return Slices[name];
}
public string Name(int index)
{
return Names[index];
}
public int Count()
{
return Names.Count;
}
}
// Assumes all subimages are the same size
public class TiledTextureAtlas : TextureAtlas
{
public int NumRows { get; }
public int NumColumns { get; }
public TiledTextureAtlas(GraphicsDevice graphicsDevice, FileInfo atlasMetadataFile) : base(graphicsDevice, atlasMetadataFile)
{
var subImageSlice = Slices[Names[0]];
NumRows = Texture.Height / subImageSlice.H;
NumColumns = Texture.Width / subImageSlice.W;
}
}
}

19
Data/UVData.cs Normal file
View File

@ -0,0 +1,19 @@
using Microsoft.Xna.Framework;
namespace Kav
{
public struct UVData
{
public Vector2 Offset { get; }
public Vector2 Percentage { get; }
public UVData(
Vector2 positionInAtlas,
Vector2 subTextureDimensions,
Vector2 atlasDimensions
) {
Percentage = subTextureDimensions / atlasDimensions;
Offset = positionInAtlas / atlasDimensions;
}
}
}

View File

@ -0,0 +1,59 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class DirectionalShadowMapData
{
public static readonly int MAX_SHADOW_CASCADES = 4;
public RenderTarget2D[] ShadowMaps { get; }
public Matrix[] LightSpaceViews { get; }
public Matrix[] LightSpaceProjections { get; }
public float[] CascadeFarPlanes { get; }
public int ShadowMapSize { get; }
public int NumShadowCascades { get; }
internal DirectionalShadowMapData(
GraphicsDevice graphicsDevice,
int shadowMapSize,
int numCascades
) {
ShadowMapSize = shadowMapSize;
NumShadowCascades = (int)MathHelper.Clamp(numCascades, 1, MAX_SHADOW_CASCADES);
LightSpaceViews = new Matrix[4];
LightSpaceProjections = new Matrix[4];
ShadowMaps = new RenderTarget2D[NumShadowCascades];
for (var i = 0; i < NumShadowCascades; i++)
{
ShadowMaps[i] = new RenderTarget2D(
graphicsDevice,
shadowMapSize,
shadowMapSize,
false,
SurfaceFormat.Single,
DepthFormat.Depth24,
0,
RenderTargetUsage.PreserveContents
);
}
CascadeFarPlanes = new float[MAX_SHADOW_CASCADES];
}
public void Clear(GraphicsDevice graphicsDevice)
{
foreach (var shadowMap in ShadowMaps)
{
graphicsDevice.SetRenderTarget(shadowMap);
graphicsDevice.Clear(Color.White);
}
}
}
}

View File

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

View File

@ -0,0 +1,9 @@
using Microsoft.Xna.Framework;
namespace Kav
{
public interface IHasTranslation
{
Vector3 Translation { get; set; }
}
}

View File

@ -0,0 +1,9 @@
using Microsoft.Xna.Framework;
namespace Kav
{
public interface IHasWorldMatrix
{
Matrix World { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using Microsoft.Xna.Framework;
namespace Kav
{
public interface ShadowCascadeEffect
{
Matrix LightSpaceMatrixOne { get; set; }
Matrix LightSpaceMatrixTwo { get; set; }
Matrix LightSpaceMatrixThree { get; set; }
Matrix LightSpaceMatrixFour { get; set; }
float[] CascadeFarPlanes { get; }
}
}

View File

@ -10,10 +10,21 @@ namespace Kav
EffectParameter gNormalParam; EffectParameter gNormalParam;
EffectParameter gMetallicRoughnessParam; EffectParameter gMetallicRoughnessParam;
EffectParameter directionalShadowMapParam; EffectParameter shadowMapOneParam;
EffectParameter directionalLightDirectionParam; EffectParameter shadowMapTwoParam;
EffectParameter shadowMapThreeParam;
EffectParameter shadowMapFourParam;
EffectParameter lightSpaceMatrixOneParam;
EffectParameter lightSpaceMatrixTwoParam;
EffectParameter lightSpaceMatrixThreeParam;
EffectParameter lightSpaceMatrixFourParam;
EffectParameter viewMatrixParam;
EffectParameter cascadeFarPlanesParam;
EffectParameter directionalLightColorParam; EffectParameter directionalLightColorParam;
EffectParameter directionalLightMatrixParam; EffectParameter directionalLightDirectionParam;
EffectParameter eyePositionParam; EffectParameter eyePositionParam;
@ -24,10 +35,21 @@ namespace Kav
public Texture2D GNormal { get; set; } public Texture2D GNormal { get; set; }
public Texture2D GMetallicRoughness { get; set; } public Texture2D GMetallicRoughness { get; set; }
public Texture2D DirectionalShadowMap { get; set; } public Texture2D ShadowMapOne { get; set; }
public Vector3 DirectionalLightDirection { 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 DirectionalLightColor { get; set; }
public Matrix DirectionalLightMatrix { get; set; } public Vector3 DirectionalLightDirection { get; set; }
public Vector3 EyePosition { get; set; } public Vector3 EyePosition { get; set; }
@ -41,6 +63,8 @@ namespace Kav
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(
@ -50,32 +74,6 @@ namespace Kav
); );
} }
protected DeferredPBREffect(DeferredPBREffect cloneSource) : base(cloneSource)
{
GPosition = cloneSource.GPosition;
GAlbedo = cloneSource.GAlbedo;
GNormal = cloneSource.GNormal;
GMetallicRoughness = cloneSource.GMetallicRoughness;
EyePosition = cloneSource.EyePosition;
PointLights = new PointLightCollection(
Parameters["LightPositions"],
Parameters["PositionLightColors"],
MaxPointLights
);
for (int i = 0; i < MaxPointLights; i++)
{
PointLights[i] = cloneSource.PointLights[i];
}
}
public override Effect Clone()
{
return new DeferredPBREffect(this);
}
protected override void OnApply() protected override void OnApply()
{ {
gPositionParam.SetValue(GPosition); gPositionParam.SetValue(GPosition);
@ -83,10 +81,21 @@ namespace Kav
gNormalParam.SetValue(GNormal); gNormalParam.SetValue(GNormal);
gMetallicRoughnessParam.SetValue(GMetallicRoughness); gMetallicRoughnessParam.SetValue(GMetallicRoughness);
directionalShadowMapParam.SetValue(DirectionalShadowMap); shadowMapOneParam.SetValue(ShadowMapOne);
directionalLightDirectionParam.SetValue(DirectionalLightDirection); 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); directionalLightColorParam.SetValue(DirectionalLightColor);
directionalLightMatrixParam.SetValue(DirectionalLightMatrix); directionalLightDirectionParam.SetValue(DirectionalLightDirection);
eyePositionParam.SetValue(EyePosition); eyePositionParam.SetValue(EyePosition);
} }
@ -97,11 +106,22 @@ namespace Kav
gAlbedoParam = Parameters["gAlbedo"]; gAlbedoParam = Parameters["gAlbedo"];
gNormalParam = Parameters["gNormal"]; gNormalParam = Parameters["gNormal"];
gMetallicRoughnessParam = Parameters["gMetallicRoughness"]; gMetallicRoughnessParam = Parameters["gMetallicRoughness"];
directionalShadowMapParam = Parameters["directionalShadowMap"];
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"]; directionalLightDirectionParam = Parameters["DirectionalLightDirection"];
directionalLightColorParam = Parameters["DirectionalLightColor"]; directionalLightColorParam = Parameters["DirectionalLightColor"];
directionalLightMatrixParam = Parameters["DirectionalLightMatrix"];
eyePositionParam = Parameters["EyePosition"]; eyePositionParam = Parameters["EyePosition"];
} }

View File

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

View File

@ -0,0 +1,160 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class DeferredPBR_DirectionalLightEffect : Effect, ShadowCascadeEffect
{
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 float[] CascadeFarPlanes { get; }
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

@ -6,8 +6,7 @@ namespace Kav
public class DeferredPBR_GBufferEffect : Effect, TransformEffect public class DeferredPBR_GBufferEffect : Effect, TransformEffect
{ {
EffectParameter worldParam; EffectParameter worldParam;
EffectParameter worldViewProjectionParam; EffectParameter viewProjectionParam;
EffectParameter worldInverseTransposeParam;
EffectParameter albedoTextureParam; EffectParameter albedoTextureParam;
EffectParameter normalTextureParam; EffectParameter normalTextureParam;
@ -17,6 +16,13 @@ namespace Kav
EffectParameter metallicParam; EffectParameter metallicParam;
EffectParameter roughnessParam; EffectParameter roughnessParam;
EffectParameter uvOffsetAndDimensionsParam;
EffectParameter isSpriteParam;
EffectParameter numTextureRowsParam;
EffectParameter numTextureColumnsParam;
EffectParameter vertexShaderIndexParam;
EffectParameter shaderIndexParam; EffectParameter shaderIndexParam;
Matrix world = Matrix.Identity; Matrix world = Matrix.Identity;
@ -27,9 +33,18 @@ namespace Kav
float metallic; float metallic;
float roughness; float roughness;
Vector2 uvOffset;
Vector2 subTextureDimensions;
bool isSprite = false;
int numTextureRows = 1;
int numTextureColumns = 1;
bool albedoTextureEnabled = false; bool albedoTextureEnabled = false;
bool metallicRoughnessMapEnabled = false; bool metallicRoughnessMapEnabled = false;
bool normalMapEnabled = false; bool normalMapEnabled = false;
bool hardwareInstancingEnabled = false;
EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All; EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
@ -39,7 +54,7 @@ namespace Kav
set set
{ {
world = value; world = value;
dirtyFlags |= EffectDirtyFlags.World | EffectDirtyFlags.WorldViewProj; dirtyFlags |= EffectDirtyFlags.World;
} }
} }
@ -49,7 +64,7 @@ namespace Kav
set set
{ {
view = value; view = value;
dirtyFlags |= EffectDirtyFlags.WorldViewProj | EffectDirtyFlags.EyePosition; dirtyFlags |= EffectDirtyFlags.ViewProj | EffectDirtyFlags.EyePosition;
} }
} }
@ -59,7 +74,7 @@ namespace Kav
set set
{ {
projection = value; projection = value;
dirtyFlags |= EffectDirtyFlags.WorldViewProj; dirtyFlags |= EffectDirtyFlags.ViewProj;
} }
} }
@ -100,7 +115,7 @@ namespace Kav
{ {
albedoTextureParam.SetValue(value); albedoTextureParam.SetValue(value);
albedoTextureEnabled = value != null; albedoTextureEnabled = value != null;
dirtyFlags |= EffectDirtyFlags.ShaderIndex; dirtyFlags |= EffectDirtyFlags.PixelShaderIndex;
} }
} }
@ -111,7 +126,7 @@ namespace Kav
{ {
normalTextureParam.SetValue(value); normalTextureParam.SetValue(value);
normalMapEnabled = value != null; normalMapEnabled = value != null;
dirtyFlags |= EffectDirtyFlags.ShaderIndex; dirtyFlags |= EffectDirtyFlags.PixelShaderIndex;
} }
} }
@ -122,7 +137,70 @@ namespace Kav
{ {
metallicRoughnessTextureParam.SetValue(value); metallicRoughnessTextureParam.SetValue(value);
metallicRoughnessMapEnabled = value != null; metallicRoughnessMapEnabled = value != null;
dirtyFlags |= EffectDirtyFlags.ShaderIndex; dirtyFlags |= EffectDirtyFlags.PixelShaderIndex;
}
}
public int NumTextureRows
{
get { return numTextureRows; }
set
{
numTextureRows = value;
numTextureRowsParam.SetValue(numTextureRows);
}
}
public int NumTextureColumns
{
get { return numTextureColumns; }
set
{
numTextureColumns = value;
numTextureColumnsParam.SetValue(numTextureColumns);
}
}
public Vector2 UVOffset
{
get { return uvOffset; }
set
{
uvOffset = value;
dirtyFlags |= EffectDirtyFlags.UVOrDimensions;
}
}
public Vector2 SubTextureDimensions
{
get { return subTextureDimensions; }
set
{
subTextureDimensions = value;
dirtyFlags |= EffectDirtyFlags.UVOrDimensions;
}
}
public bool IsSprite
{
get { return isSprite; }
set
{
isSprite = value;
isSpriteParam.SetValue(isSprite ? 1f : 0f);
}
}
public bool HardwareInstancingEnabled
{
get { return hardwareInstancingEnabled; }
set
{
if (value != hardwareInstancingEnabled)
{
hardwareInstancingEnabled = value;
dirtyFlags |= EffectDirtyFlags.VertexShaderIndex;
}
} }
} }
@ -159,30 +237,40 @@ namespace Kav
{ {
worldParam.SetValue(world); worldParam.SetValue(world);
Matrix.Invert(ref world, out Matrix worldInverse);
Matrix.Transpose(ref worldInverse, out Matrix worldInverseTranspose);
worldInverseTransposeParam.SetValue(worldInverseTranspose);
dirtyFlags &= ~EffectDirtyFlags.World; dirtyFlags &= ~EffectDirtyFlags.World;
} }
if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0) if ((dirtyFlags & EffectDirtyFlags.ViewProj) != 0)
{ {
Matrix.Multiply(ref world, ref view, out Matrix worldView); Matrix.Multiply(ref view, ref projection, out Matrix viewProj);
Matrix.Multiply(ref worldView, ref projection, out Matrix worldViewProj); viewProjectionParam.SetValue(viewProj);
worldViewProjectionParam.SetValue(worldViewProj);
dirtyFlags &= ~EffectDirtyFlags.WorldViewProj; dirtyFlags &= ~EffectDirtyFlags.ViewProj;
} }
if ((dirtyFlags & EffectDirtyFlags.EyePosition) != 0) if ((dirtyFlags & EffectDirtyFlags.UVOrDimensions) != 0)
{ {
Matrix.Invert(ref view, out Matrix inverseView); uvOffsetAndDimensionsParam.SetValue(new Vector4(
UVOffset.X, UVOffset.Y,
SubTextureDimensions.X, SubTextureDimensions.Y
));
dirtyFlags &= ~EffectDirtyFlags.EyePosition; dirtyFlags &= ~EffectDirtyFlags.UVOrDimensions;
} }
if ((dirtyFlags & EffectDirtyFlags.ShaderIndex) != 0) if ((dirtyFlags & EffectDirtyFlags.VertexShaderIndex) != 0)
{
int vertexShaderIndex = 0;
if (hardwareInstancingEnabled)
{
vertexShaderIndex = 1;
}
vertexShaderIndexParam.SetValue(vertexShaderIndex);
}
if ((dirtyFlags & EffectDirtyFlags.PixelShaderIndex) != 0)
{ {
int shaderIndex = 0; int shaderIndex = 0;
@ -217,15 +305,14 @@ namespace Kav
shaderIndexParam.SetValue(shaderIndex); shaderIndexParam.SetValue(shaderIndex);
dirtyFlags &= ~EffectDirtyFlags.ShaderIndex; dirtyFlags &= ~EffectDirtyFlags.PixelShaderIndex;
} }
} }
void CacheEffectParameters() void CacheEffectParameters()
{ {
worldParam = Parameters["World"]; worldParam = Parameters["World"];
worldViewProjectionParam = Parameters["WorldViewProjection"]; viewProjectionParam = Parameters["ViewProjection"];
worldInverseTransposeParam = Parameters["WorldInverseTranspose"];
albedoTextureParam = Parameters["AlbedoTexture"]; albedoTextureParam = Parameters["AlbedoTexture"];
normalTextureParam = Parameters["NormalTexture"]; normalTextureParam = Parameters["NormalTexture"];
@ -235,7 +322,14 @@ namespace Kav
metallicParam = Parameters["MetallicValue"]; metallicParam = Parameters["MetallicValue"];
roughnessParam = Parameters["RoughnessValue"]; roughnessParam = Parameters["RoughnessValue"];
shaderIndexParam = Parameters["ShaderIndex"]; numTextureRowsParam = Parameters["NumTextureRows"];
numTextureColumnsParam = Parameters["NumTextureColumns"];
uvOffsetAndDimensionsParam = Parameters["UVOffsetAndDimensions"];
isSpriteParam = Parameters["IsSprite"];
shaderIndexParam = Parameters["PixelShaderIndex"];
vertexShaderIndexParam = Parameters["VertexShaderIndex"];
} }
} }
} }

View File

@ -0,0 +1,141 @@
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 shadowMapParam;
EffectParameter eyePositionParam;
EffectParameter pointLightColorParam;
EffectParameter pointLightPositionParam;
EffectParameter farPlaneParam;
EffectParameter worldViewProjectionParam;
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; }
Matrix world = Matrix.Identity;
Matrix view = Matrix.Identity;
Matrix projection = Matrix.Identity;
EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
public Matrix World
{
get { return world; }
set
{
world = value;
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 DeferredPBR_PointLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_PointLightEffect)
{
CacheEffectParameters();
}
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()
{
return new DeferredPBR_PointLightEffect(this);
}
protected override void OnApply()
{
gPositionParam.SetValue(GPosition);
gAlbedoParam.SetValue(GAlbedo);
gNormalParam.SetValue(GNormal);
gMetallicRoughnessParam.SetValue(GMetallicRoughness);
shadowMapParam.SetValue(ShadowMap);
eyePositionParam.SetValue(EyePosition);
pointLightPositionParam.SetValue(PointLightPosition);
pointLightColorParam.SetValue(PointLightColor);
farPlaneParam.SetValue(FarPlane);
if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0)
{
worldViewProjectionParam.SetValue(world * view * projection);
dirtyFlags &= ~EffectDirtyFlags.WorldViewProj;
}
}
void CacheEffectParameters()
{
gPositionParam = Parameters["gPosition"];
gAlbedoParam = Parameters["gAlbedo"];
gNormalParam = Parameters["gNormal"];
gMetallicRoughnessParam = Parameters["gMetallicRoughness"];
shadowMapParam = Parameters["shadowMap"];
eyePositionParam = Parameters["EyePosition"];
pointLightPositionParam = Parameters["PointLightPosition"];
pointLightColorParam = Parameters["PointLightColor"];
farPlaneParam = Parameters["FarPlane"];
worldViewProjectionParam = Parameters["WorldViewProjection"];
}
}
}

View File

@ -0,0 +1,147 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class Deferred_ToonEffect : Effect, ShadowCascadeEffect
{
EffectParameter gPositionParam;
EffectParameter gAlbedoParam;
EffectParameter gNormalParam;
EffectParameter gMetallicRoughnessParam;
EffectParameter shadowMapOneParam;
EffectParameter shadowMapTwoParam;
EffectParameter shadowMapThreeParam;
EffectParameter shadowMapFourParam;
EffectParameter eyePositionParam;
EffectParameter directionalLightDirectionParam;
EffectParameter directionalLightColorParam;
EffectParameter cascadeFarPlanesParam;
EffectParameter shadowMapSizeParam;
EffectParameter lightSpaceMatrixOneParam;
EffectParameter lightSpaceMatrixTwoParam;
EffectParameter lightSpaceMatrixThreeParam;
EffectParameter lightSpaceMatrixFourParam;
EffectParameter viewMatrixParam;
EffectParameter shaderIndexParam;
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 float[] CascadeFarPlanes { get; }
public float 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; }
private bool ditheredShadowValue = false;
public bool DitheredShadows
{
get { return ditheredShadowValue; }
set
{
ditheredShadowValue = value;
CalculateShaderIndex();
}
}
private int shaderIndex = 0;
public Deferred_ToonEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.Deferred_ToonEffect)
{
CascadeFarPlanes = new float[4];
CacheEffectParameters();
}
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);
shaderIndexParam.SetValue(shaderIndex);
}
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"];
shaderIndexParam = Parameters["ShaderIndex"];
}
private void CalculateShaderIndex()
{
if (ditheredShadowValue)
{
shaderIndex = 1;
}
else
{
shaderIndex = 0;
}
}
}
}

View File

@ -0,0 +1,210 @@
using Kav.Data;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class DiffuseLitSpriteEffect : Effect
{
EffectParameter ambientColorParam;
EffectParameter directionalLightDirectionParam;
EffectParameter directionalLightColorParam;
EffectParameter uvOffsetAndDimensionsParam;
EffectParameter worldParam;
EffectParameter worldViewProjectionParam;
EffectParameter worldInverseTransposeParam;
EffectParameter shaderIndexParam;
Texture2D texture;
Texture2D normal;
bool normalMapEnabled = false;
Vector3 ambientColor;
Vector3 directionalLightDirection;
Vector3 directionalLightColor;
Matrix world = Matrix.Identity;
Matrix view = Matrix.Identity;
Matrix projection = Matrix.Identity;
Vector2 uvOffset;
Vector2 subTextureDimensions;
EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
public bool NormalMapEnabled
{
get { return normalMapEnabled; }
set
{
if (normalMapEnabled != value)
{
normalMapEnabled = value;
dirtyFlags |= EffectDirtyFlags.PixelShaderIndex;
}
}
}
public Matrix World
{
get { return world; }
set
{
world = value;
dirtyFlags |= EffectDirtyFlags.World | EffectDirtyFlags.WorldViewProj;
}
}
public Matrix View
{
get { return view; }
set
{
view = value;
dirtyFlags |= EffectDirtyFlags.WorldViewProj | EffectDirtyFlags.EyePosition;
}
}
public Matrix Projection
{
get { return projection; }
set
{
projection = value;
dirtyFlags |= EffectDirtyFlags.WorldViewProj;
}
}
public int MaxPointLights { get; } = 8;
public Vector3 AmbientColor
{
get { return ambientColor; }
set
{
ambientColor = value;
ambientColorParam.SetValue(ambientColor);
}
}
public PointLightCollection PointLights { get; private set; }
public Vector3 DirectionalLightDirection
{
get { return directionalLightDirection; }
set
{
directionalLightDirection = value;
directionalLightDirectionParam.SetValue(directionalLightDirection);
}
}
public Vector3 DirectionalLightColor
{
get { return directionalLightColor; }
set
{
directionalLightColor = value;
directionalLightColorParam.SetValue(directionalLightColor);
}
}
public Vector2 UVOffset
{
get { return uvOffset; }
set
{
uvOffset = value;
dirtyFlags |= EffectDirtyFlags.UVOrDimensions;
}
}
public Vector2 SubTextureDimensions
{
get { return subTextureDimensions; }
set
{
subTextureDimensions = value;
dirtyFlags |= EffectDirtyFlags.UVOrDimensions;
}
}
public DiffuseLitSpriteEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DiffuseLitSpriteEffect)
{
CacheEffectParameters();
PointLights = new PointLightCollection(
Parameters["PointLightPositions"],
Parameters["PointLightColors"],
MaxPointLights
);
}
protected override void OnApply()
{
if ((dirtyFlags & EffectDirtyFlags.World) != 0)
{
worldParam.SetValue(world);
Matrix.Invert(ref world, out Matrix worldInverse);
Matrix.Transpose(ref worldInverse, out Matrix worldInverseTranspose);
worldInverseTransposeParam.SetValue(worldInverseTranspose);
dirtyFlags &= ~EffectDirtyFlags.World;
}
if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0)
{
Matrix.Multiply(ref world, ref view, out Matrix worldView);
Matrix.Multiply(ref worldView, ref projection, out Matrix worldViewProj);
worldViewProjectionParam.SetValue(worldViewProj);
dirtyFlags &= ~EffectDirtyFlags.WorldViewProj;
}
if ((dirtyFlags & EffectDirtyFlags.UVOrDimensions) != 0)
{
uvOffsetAndDimensionsParam.SetValue(new Vector4(
UVOffset.X, UVOffset.Y,
SubTextureDimensions.X, SubTextureDimensions.Y
));
dirtyFlags &= ~EffectDirtyFlags.UVOrDimensions;
}
if ((dirtyFlags & EffectDirtyFlags.PixelShaderIndex) != 0)
{
int shaderIndex = 0;
if (normalMapEnabled)
{
shaderIndex = 1;
}
shaderIndexParam.SetValue(shaderIndex);
}
}
private void CacheEffectParameters()
{
worldParam = Parameters["World"];
worldViewProjectionParam = Parameters["WorldViewProjection"];
worldInverseTransposeParam = Parameters["WorldInverseTranspose"];
ambientColorParam = Parameters["AmbientColor"];
directionalLightDirectionParam = Parameters["DirectionalLightDirection"];
directionalLightColorParam = Parameters["DirectionalLightColor"];
uvOffsetAndDimensionsParam = Parameters["UVOffsetAndDimensions"];
shaderIndexParam = Parameters["ShaderIndex"];
}
}
}

View File

@ -1,71 +0,0 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class DirectionalLightCollection
{
private readonly Vector3[] directions = new Vector3[6];
private readonly Vector3[] colors = new Vector3[6];
private readonly float[] intensities = new float[6];
private readonly Matrix[] viewMatrices = new Matrix[6];
private readonly Matrix[] projectionMatrices = new Matrix[6];
private readonly Matrix[] viewProjectionMatrices = new Matrix[6];
readonly EffectParameter lightDirectionsParam;
readonly EffectParameter lightColorsParam;
readonly EffectParameter lightSpaceMatricesParam;
public DirectionalLightCollection(
EffectParameter lightDirectionsParam,
EffectParameter lightColorsParam,
EffectParameter lightSpaceMatricesParam
) {
this.lightDirectionsParam = lightDirectionsParam;
this.lightColorsParam = lightColorsParam;
this.lightSpaceMatricesParam = lightSpaceMatricesParam;
}
public void SetMatrices(int index, Matrix view, Matrix projection)
{
viewMatrices[index] = view;
projectionMatrices[index] = projection;
}
public void Apply()
{
for (int i = 0; i < 6; i++)
{
viewProjectionMatrices[i] = viewMatrices[i] * projectionMatrices[i];
}
lightSpaceMatricesParam.SetValue(viewProjectionMatrices);
}
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);
}
}
}
}

View File

@ -8,7 +8,10 @@ namespace Kav
WorldViewProj = 1, WorldViewProj = 1,
World = 2, World = 2,
EyePosition = 4, EyePosition = 4,
ShaderIndex = 8, VertexShaderIndex = 8,
PixelShaderIndex = 16,
ViewProj = 32,
UVOrDimensions = 64,
All = -1 All = -1
} }
} }

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.

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

Binary file not shown.

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

Binary file not shown.

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

Binary file not shown.

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

Binary file not shown.

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

View File

@ -0,0 +1,34 @@
float3 HUEtoRGB(in float H)
{
float R = abs(H * 6 - 3) - 1;
float G = 2 - abs(H * 6 - 2);
float B = 2 - abs(H * 6 - 4);
return saturate(float3(R,G,B));
}
float Epsilon = 1e-10;
float3 RGBtoHCV(in float3 RGB)
{
// Based on work by Sam Hocevar and Emil Persson
float4 P = (RGB.g < RGB.b) ? float4(RGB.bg, -1.0, 2.0/3.0) : float4(RGB.gb, 0.0, -1.0/3.0);
float4 Q = (RGB.r < P.x) ? float4(P.xyw, RGB.r) : float4(RGB.r, P.yzx);
float C = Q.x - min(Q.w, Q.y);
float H = abs((Q.w - Q.y) / (6 * C + Epsilon) + Q.z);
return float3(H, C, Q.x);
}
float3 RGBtoHSL(float3 RGB)
{
float3 HCV = RGBtoHCV(RGB);
float L = HCV.z - HCV.y * 0.5;
float S = HCV.y / (1 - abs(L * 2 - 1) + Epsilon);
return float3(HCV.x, S, L);
}
float3 HSLtoRGB(float3 HSL)
{
float3 RGB = HUEtoRGB(HSL.x);
float C = (1 - abs(2 * HSL.z - 1)) * HSL.y;
return (RGB - 0.5) * C + HSL.z;
}

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 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(directionalShadowMap, 4); DECLARE_TEXTURE(shadowMapOne, 4);
DECLARE_TEXTURE(shadowMapTwo, 5);
DECLARE_TEXTURE(shadowMapThree, 6);
DECLARE_TEXTURE(shadowMapFour, 7);
BEGIN_CONSTANTS BEGIN_CONSTANTS
@ -19,9 +23,17 @@ BEGIN_CONSTANTS
float3 DirectionalLightDirection _ps(c129) _cb(c129); float3 DirectionalLightDirection _ps(c129) _cb(c129);
float3 DirectionalLightColor _ps(c130) _cb(c130); float3 DirectionalLightColor _ps(c130) _cb(c130);
float CascadeFarPlanes[NUM_SHADOW_CASCADES] _ps(c131) _cb(c131);
MATRIX_CONSTANTS MATRIX_CONSTANTS
float4x4 DirectionalLightMatrix _ps(c131) _cb(c131); 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
@ -89,28 +101,110 @@ float GeometrySmith(float3 N, float3 V, float3 L, float roughness)
return ggx1 * ggx2; return ggx1 * ggx2;
} }
float ComputeShadow(float4 positionLightSpace) float ComputeShadow(float3 positionWorldSpace, float3 N, float L)
{ {
float bias = 0.001; 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] // maps to [-1, 1]
float3 projectionCoords = positionLightSpace.xyz / positionLightSpace.w; float3 projectionCoords = positionLightSpace.xyz / positionLightSpace.w;
//transform to [0, 1] range // maps to [0, 1]
projectionCoords = projectionCoords * 0.5 + 0.5; 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 closestDepth = SAMPLE_TEXTURE(directionalShadowMap, positionLightSpace.xy).r; float inc = 1.0 / 1024.0;
float currentDepth = projectionCoords.z;
if (projectionCoords.z > 1.0) { return 0.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;
}
}
float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; shadowFactor /= 9.0;
return shadow; 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,
@ -120,7 +214,6 @@ float3 ComputeLight(
float roughness, float roughness,
float shadow 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);
@ -155,25 +248,24 @@ float4 ComputeColor(
float3 Lo = float3(0.0, 0.0, 0.0); float3 Lo = float3(0.0, 0.0, 0.0);
// point lights // point light
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, 1.0); Lo += ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, 1.0);
} }
// directional light // directional light
float4 positionLightSpace = mul(float4(worldPosition, 1.0), DirectionalLightMatrix); float3 L = normalize(DirectionalLightDirection);
float shadow = ComputeShadow(positionLightSpace);
float3 lightDir = DirectionalLightDirection;
float3 radiance = DirectionalLightColor; float3 radiance = DirectionalLightColor;
Lo += ComputeLight(lightDir, radiance, F0, V, N, albedo, metallic, roughness, (1.0 - shadow)); 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;

View File

@ -0,0 +1,60 @@
#include "Macros.fxh" // from FNA
DECLARE_TEXTURE(gPosition, 0);
DECLARE_TEXTURE(gAlbedo, 1);
BEGIN_CONSTANTS
float3 AmbientLightColor _ps(c0) _cb(c0);
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;
}
float4 ComputeColor(
float3 worldPosition,
float3 albedo
) {
float3 color = AmbientLightColor * 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,189 @@
#include "Macros.fxh" //from FNA
#include "Lighting.fxh"
#include "Shadow.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
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
float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L)
{
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;
}
// PCF + Poisson soft shadows
if (shadowCascadeIndex == 0)
{
return PoissonShadow(
positionWorldSpace,
N,
L,
lightSpaceMatrix,
SAMPLER(shadowMapOne),
ShadowMapSize
);
}
else if (shadowCascadeIndex == 1)
{
return PoissonShadow(
positionWorldSpace,
N,
L,
lightSpaceMatrix,
SAMPLER(shadowMapTwo),
ShadowMapSize
);
}
else if (shadowCascadeIndex == 2)
{
return PoissonShadow(
positionWorldSpace,
N,
L,
lightSpaceMatrix,
SAMPLER(shadowMapThree),
ShadowMapSize
);
}
else
{
return PoissonShadow(
positionWorldSpace,
N,
L,
lightSpaceMatrix,
SAMPLER(shadowMapFour),
ShadowMapSize
);
}
}
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

@ -4,17 +4,22 @@ DECLARE_TEXTURE(AlbedoTexture, 0);
DECLARE_TEXTURE(NormalTexture, 1); DECLARE_TEXTURE(NormalTexture, 1);
DECLARE_TEXTURE(MetallicRoughnessTexture, 2); DECLARE_TEXTURE(MetallicRoughnessTexture, 2);
float4 UVOffsetAndDimensions;
float IsSprite;
BEGIN_CONSTANTS BEGIN_CONSTANTS
float3 AlbedoValue _ps(c0) _cb(c0); float3 AlbedoValue _ps(c0) _cb(c0);
float MetallicValue _ps(c1) _cb(c1); float MetallicValue _ps(c1) _cb(c1);
float RoughnessValue _ps(c2) _cb(c2); float RoughnessValue _ps(c2) _cb(c2);
int NumTextureRows _vs(c9) _cb(c3);
int NumTextureColumns _vs(c10) _cb(c4);
MATRIX_CONSTANTS MATRIX_CONSTANTS
float4x4 World _vs(c0) _cb(c7); float4x4 World _vs(c0) _cb(c7);
float4x4 WorldInverseTranspose _vs(c4) _cb(c11); float4x4 ViewProjection _vs(c4) _cb(c11);
float4x4 WorldViewProjection _vs(c8) _cb(c15);
END_CONSTANTS END_CONSTANTS
@ -22,7 +27,7 @@ struct VertexInput
{ {
float4 Position : POSITION; float4 Position : POSITION;
float3 Normal : NORMAL; float3 Normal : NORMAL;
float2 TexCoord : TEXCOORD0; float2 TexCoord : TEXCOORD;
}; };
struct PixelInput struct PixelInput
@ -48,9 +53,44 @@ PixelInput main_vs(VertexInput input)
PixelInput output; PixelInput output;
output.PositionWorld = mul(input.Position, World).xyz; output.PositionWorld = mul(input.Position, World).xyz;
output.NormalWorld = mul(input.Normal, (float3x3)WorldInverseTranspose).xyz; output.NormalWorld = normalize(mul(input.Normal, World));
output.TexCoord = input.TexCoord;
output.Position = mul(input.Position, WorldViewProjection); float2 texCoord;
texCoord.x = (input.TexCoord.x * UVOffsetAndDimensions.z) + UVOffsetAndDimensions.x;
texCoord.y = (input.TexCoord.y * UVOffsetAndDimensions.w) + UVOffsetAndDimensions.y;
output.TexCoord = texCoord;
float4x4 worldViewProjection = mul(World, ViewProjection);
output.Position = mul(input.Position, worldViewProjection);
return output;
}
PixelInput instanced_vs(
VertexInput input,
float3 Translation : TEXCOORD2,
float2 UVOffset : TEXCOORD5
) {
PixelInput output;
float4x4 world = float4x4(
float4(1, 0, 0, 0),
float4(0, 1, 0, 0),
float4(0, 0, 1, 0),
float4(Translation.x, Translation.y, Translation.z, 1)
);
float4x4 worldViewProjection = mul(world, ViewProjection);
output.PositionWorld = mul(input.Position, world);
output.NormalWorld = mul(input.Normal, world);
float2 texCoord;
texCoord.x = (input.TexCoord.x / NumTextureColumns + UVOffset.x);
texCoord.y = (input.TexCoord.y / NumTextureRows + UVOffset.y);
output.TexCoord = texCoord;
output.Position = mul(input.Position, worldViewProjection);
return output; return output;
} }
@ -79,10 +119,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), IsSprite);
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 +131,12 @@ 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), IsSprite);
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);
if (output.gAlbedo.a == 0.0) { discard; }
return output; return output;
} }
@ -103,8 +145,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), IsSprite);
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 +157,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), IsSprite);
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,11 +169,13 @@ 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), IsSprite);
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);
if (output.gAlbedo.a == 0.0) { discard; }
return output; return output;
} }
@ -139,10 +183,12 @@ 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), IsSprite);
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);
if (output.gAlbedo.a == 0.0) { discard; }
return output; return output;
} }
@ -151,8 +197,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), IsSprite);
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,14 +209,29 @@ 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), IsSprite);
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);
if (output.gAlbedo.a == 0.0) { discard; }
return output; return output;
} }
VertexShader VSArray[2] =
{
compile vs_3_0 main_vs(),
compile vs_3_0 instanced_vs()
};
int VSIndices[2] =
{
0, 1
};
int VertexShaderIndex = 0;
PixelShader PSArray[8] = PixelShader PSArray[8] =
{ {
compile ps_3_0 NonePS(), compile ps_3_0 NonePS(),
@ -191,13 +252,13 @@ int PSIndices[8] =
0, 1, 2, 3, 4, 5, 6, 7 0, 1, 2, 3, 4, 5, 6, 7
}; };
int ShaderIndex = 0; int PixelShaderIndex = 0;
Technique GBuffer Technique GBuffer
{ {
Pass Pass
{ {
VertexShader = compile vs_3_0 main_vs(); VertexShader = (VSArray[VSIndices[VertexShaderIndex]]);
PixelShader = (PSArray[PSIndices[ShaderIndex]]); PixelShader = (PSArray[PSIndices[PixelShaderIndex]]);
} }
} }

View File

@ -0,0 +1,118 @@
// Assumes you are drawing a sphere!!
#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
float3 EyePosition _ps(c0) _cb(c0);
float3 PointLightPosition _ps(c1) _cb(c1);
float3 PointLightColor _ps(c2) _cb(c2);
float FarPlane _ps(c3) _cb(c3);
MATRIX_CONSTANTS
float4x4 WorldViewProjection _vs(c0) _cb(c4);
END_CONSTANTS
struct VertexInput
{
float4 Position : POSITION;
};
struct PixelInput
{
float4 Position : SV_POSITION;
float4 ScreenPosition : TEXCOORD0;
};
PixelInput main_vs(VertexInput input)
{
PixelInput output;
output.Position = mul(input.Position, WorldViewProjection);
output.ScreenPosition = output.Position;
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;
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
{
input.ScreenPosition.xy /= input.ScreenPosition.w;
float2 texCoord = 0.5f * float2(input.ScreenPosition.x,-input.ScreenPosition.y) + 0.5f;
float3 worldPosition = SAMPLE_TEXTURE(gPosition, texCoord).rgb;
float4 normalSample = SAMPLE_TEXTURE(gNormal, texCoord);
float3 normal = normalSample.xyz;
float isSprite = normalSample.a;
float3 albedo = SAMPLE_TEXTURE(gAlbedo, texCoord).rgb;
float2 metallicRoughness = SAMPLE_TEXTURE(gMetallicRoughness, texCoord).rg;
if (isSprite == 1.0)
{
float3 lightDir = PointLightPosition - worldPosition;
float3 L = normalize(lightDir);
float distance = length(lightDir);
float attenuation = 1.0 / (distance * distance);
float3 radiance = PointLightColor * attenuation;
return float4(albedo * radiance * 0.1, 1.0);
}
else
{
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();
}
}

View File

@ -0,0 +1,285 @@
#include "Macros.fxh"
#include "Shadow.fxh"
#include "Dither.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(c4) _cb(c4);
float ShadowMapSize _ps(c8) _cb(c8);
MATRIX_CONSTANTS
float4x4 LightSpaceMatrixOne _ps(c9) _cb(c9);
float4x4 LightSpaceMatrixTwo _ps(c13) _cb(c13);
float4x4 LightSpaceMatrixThree _ps(c17) _cb(c17);
float4x4 LightSpaceMatrixFour _ps(c21) _cb(c21);
float4x4 ViewMatrix _ps(c25) _cb(c25);
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;
}
float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L)
{
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;
}
if (shadowCascadeIndex == 0)
{
return HardShadow(
positionWorldSpace,
N,
L,
lightSpaceMatrix,
SAMPLER(shadowMapOne),
ShadowMapSize
);
}
else if (shadowCascadeIndex == 1)
{
return HardShadow(
positionWorldSpace,
N,
L,
lightSpaceMatrix,
SAMPLER(shadowMapTwo),
ShadowMapSize
);
}
else if (shadowCascadeIndex == 2)
{
return HardShadow(
positionWorldSpace,
N,
L,
lightSpaceMatrix,
SAMPLER(shadowMapThree),
ShadowMapSize
);
}
else
{
return HardShadow(
positionWorldSpace,
N,
L,
lightSpaceMatrix,
SAMPLER(shadowMapFour),
ShadowMapSize
);
}
}
float IntensityBanding(float NdotL)
{
// if (NdotL > 0.5)
// {
// return 1.0;
// }
// else if (NdotL > 0.25)
// {
// return 0.5;
// }
// else if (NdotL > 0.0)
// {
// return 0.25;
// }
// else
// {
// return 0.0;
// }
if (NdotL > 0)
{
return 1.0;
}
else
{
return 0.25;
}
}
float4 FlatShadow(PixelInput input) : SV_TARGET0
{
float2 screenPosition = input.Position.xy;
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;
// the lower the glossiness, the sharper the specular highlight
float glossiness = lerp(64, 16, 1.0 - metallicRoughness.r);
float3 V = normalize(EyePosition - worldPosition);
float3 L = normalize(DirectionalLightDirection);
float3 N = normalize(normal);
float3 H = normalize(V + L);
float NdotL = dot(N, L);
float NdotH = max(dot(N, H), 0.0);
float lightIntensity = IntensityBanding(NdotL);
float3 light = lightIntensity * DirectionalLightColor;
float specularIntensity = pow(NdotH * lightIntensity, glossiness * glossiness);
float specularSmooth = smoothstep(0.005, 0.01, specularIntensity);
float3 specular = specularSmooth * float3(1.0, 1.0, 1.0);
if (metallicRoughness.r == 0.0) { specular = float3(0.0, 0.0, 0.0); }
float3 rimColor = float3(1.0, 1.0, 1.0);
float rimThreshold = 0.1;
float rimAmount = 1 - metallicRoughness.g;
float rimDot = 1 - dot(V, N);
float rimIntensity = rimDot * pow(max(NdotL, 0.0), rimThreshold);
rimIntensity = smoothstep(rimAmount - 0.01, rimAmount + 0.01, rimIntensity);
float3 rim = rimIntensity * rimColor;
float shadow = ComputeShadow(worldPosition, N, L);
float3 color = albedo * (light + specular + rim) * shadow;
return float4(color, 1.0);
}
// FIXME: organize this
float4 DitheredShadow(PixelInput input) : SV_TARGET0
{
float2 screenPosition = input.Position.xy;
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;
// the lower the glossiness, the sharper the specular highlight
float glossiness = lerp(64, 16, 1.0 - metallicRoughness.r);
float3 V = normalize(EyePosition - worldPosition);
float3 L = normalize(DirectionalLightDirection);
float3 N = normalize(normal);
float3 H = normalize(V + L);
float NdotL = dot(N, L);
float NdotH = max(dot(N, H), 0.0);
float lightIntensity = IntensityBanding(NdotL);
//float3 light = lightIntensity * DirectionalLightColor;
float3 light = DirectionalLightColor;
if (lightIntensity < 1)
{
light *= dither(lightIntensity, screenPosition);
}
float specularIntensity = pow(NdotH * lightIntensity, glossiness * glossiness);
float specularSmooth = smoothstep(0.005, 0.01, specularIntensity);
float3 specular = specularSmooth * float3(1.0, 1.0, 1.0);
if (metallicRoughness.r == 0.0) { specular = float3(0.0, 0.0, 0.0); }
float3 rimColor = float3(1.0, 1.0, 1.0);
float rimThreshold = 0.1;
float rimAmount = 1 - metallicRoughness.g;
float rimDot = 1 - dot(V, N);
float rimIntensity = rimDot * pow(max(NdotL, 0.0), rimThreshold);
rimIntensity = smoothstep(rimAmount - 0.01, rimAmount + 0.01, rimIntensity);
float3 rim = rimIntensity * rimColor;
float shadow = ComputeShadow(worldPosition, N, L);
float3 color = albedo * (light + specular + rim); // * shadow;
if (shadow < 1)
{
color *= dither(shadow, screenPosition);
}
return float4(color, 1.0);
}
PixelShader PSArray[2] =
{
compile ps_3_0 FlatShadow(),
compile ps_3_0 DitheredShadow()
};
int PSIndices[2] =
{
0, 1
};
int ShaderIndex = 0;
Technique Deferred_Toon
{
Pass
{
VertexShader = compile vs_3_0 main_vs();
PixelShader = (PSArray[PSIndices[ShaderIndex]]);
}
}

View File

@ -0,0 +1,140 @@
#include "Macros.fxh" //from FNA
// Effect applies normalmapped lighting to a 2D sprite.
DECLARE_TEXTURE(Texture, 0);
DECLARE_TEXTURE(Normal, 1);
float3 AmbientColor;
float3 PointLightPositions[8];
float3 PointLightColors[8];
float3 DirectionalLightDirection;
float3 DirectionalLightColor;
float4 UVOffsetAndDimensions;
float4x4 WorldInverseTranspose;
float4x4 World;
float4x4 WorldViewProjection;
struct VertexShaderInput
{
float4 Position : POSITION;
float3 Normal : NORMAL;
float2 TexCoord : TEXCOORD0;
};
struct PixelShaderInput
{
float4 Position : SV_Position;
float2 TexCoord : TEXCOORD0;
float3 NormalWS : TEXCOORD1;
float3 PositionWS : TEXCOORD2;
};
PixelShaderInput main_vs(VertexShaderInput input)
{
PixelShaderInput output;
output.Position = mul(input.Position, WorldViewProjection);
output.NormalWS = normalize(mul(input.Normal, (float3x3)WorldInverseTranspose));
output.PositionWS = mul(input.Position, World).xyz;
float2 texCoord;
texCoord.x = (input.TexCoord.x * UVOffsetAndDimensions.z) + UVOffsetAndDimensions.x;
texCoord.y = (input.TexCoord.y * UVOffsetAndDimensions.w) + UVOffsetAndDimensions.y;
output.TexCoord = texCoord;
return output;
}
// Easy trick to get tangent-normals to world-space to keep PBR code simplified.
float3 GetNormalFromMap(float3 worldPos, float2 texCoords, float3 normal)
{
float3 tangentNormal = SAMPLE_TEXTURE(Normal, texCoords).xyz * 2.0 - 1.0;
float3 Q1 = ddx(worldPos);
float3 Q2 = ddy(worldPos);
float2 st1 = ddx(texCoords);
float2 st2 = ddy(texCoords);
float3 N = normalize(normal);
float3 T = normalize(Q1*st2.y - Q2*st1.y);
float3 B = -normalize(cross(N, T));
float3x3 TBN = float3x3(T, B, N);
return normalize(mul(tangentNormal, TBN));
}
float4 LightColor(float3 worldPosition, float3 worldNormal)
{
float3 lightColor = float3(0.0, 0.0, 0.0);
// point lights
for (int i = 0; i < 8; i++)
{
float3 lightVec = PointLightPositions[i] - worldPosition;
float distance = length(lightVec);
float3 lightDir = normalize(lightVec);
float diffuse = max(dot(worldNormal, lightDir), 0.0);
float3 attenuation = 1.0 / (distance * distance);
lightColor += diffuse * attenuation * PointLightColors[i];
}
// directional light
float directionalDiffuse = max(dot(worldNormal, DirectionalLightDirection), 0.0);
lightColor += directionalDiffuse * DirectionalLightColor;
// ambient light
lightColor += AmbientColor;
return float4(lightColor, 1.0);
}
float4 WithoutNormalMap(PixelShaderInput input) : COLOR0
{
float4 tex = SAMPLE_TEXTURE(Texture, input.TexCoord);
if (tex.a == 0.0) { discard; }
float3 normalWS = normalize(input.NormalWS);
return tex * LightColor(input.PositionWS, normalWS);
}
float4 WithNormalMap(PixelShaderInput input) : COLOR0
{
float4 tex = SAMPLE_TEXTURE(Texture, input.TexCoord);
if (tex.a == 0.0) { discard; }
float3 normalWS = GetNormalFromMap(input.PositionWS, input.TexCoord, input.NormalWS);
return tex * LightColor(input.PositionWS, normalWS);
}
PixelShader PSArray[2] =
{
compile ps_3_0 WithoutNormalMap(),
compile ps_3_0 WithNormalMap()
};
int PSIndices[2] =
{
0, 1
};
int ShaderIndex = 0;
Technique DiffuseLitSprite
{
pass
{
VertexShader = compile vs_3_0 main_vs();
PixelShader = (PSArray[PSIndices[ShaderIndex]]);
}
}

89
Effects/HLSL/Dither.fxh Normal file
View File

@ -0,0 +1,89 @@
#include "Conversion.fxh"
// technique from http://alex-charlton.com/posts/Dithering_on_the_GPU/
uniform float3 palette[8];
static const int paletteSize = 8;
static const int indexMatrix4x4[16] =
{
0, 8, 2, 10,
12, 4, 14, 6,
3, 11, 1, 9,
15, 7, 13, 5
};
float indexValue(float2 screenCoords)
{
int x = int(screenCoords.x % 4);
int y = int(screenCoords.y % 4);
return indexMatrix4x4[(x + y * 4)] / 16.0;
}
float hueDistance(float h1, float h2)
{
float diff = abs(h1 - h2);
return min(abs(1.0 - diff), diff);
}
void closestColors(float hue, out float3 ret[2])
{
float3 closest = float3(-2, 0, 0);
float3 secondClosest = float3(-2, 0, 0);
float3 temp;
for (int i = 0; i < paletteSize; i++)
{
temp = palette[i];
float tempDistance = hueDistance(temp.x, hue);
if (tempDistance < hueDistance(closest.x, hue))
{
secondClosest = closest;
closest = temp;
}
else
{
if (tempDistance < hueDistance(secondClosest.x, hue))
{
secondClosest = temp;
}
}
}
ret[0] = closest;
ret[1] = secondClosest;
}
float3 dither(float3 color, float2 screenCoords)
{
float3 colors[2];
float3 hsl = RGBtoHSL(color);
closestColors(hsl.x, colors);
float3 closestColor = colors[0];
float3 secondClosestColor = colors[1];
float d = indexValue(screenCoords);
float hueDiff = hueDistance(hsl.x, closestColor.x) / hueDistance(secondClosestColor.x, secondClosestColor.x);
return HSLtoRGB(hueDiff < d ? closestColor : secondClosestColor);
}
// brightColor refers to undithered max color
// float3 dither(float3 color, float3 brightColor, float2 screenCoords)
// {
// float brightHue = RGBtoHSL(brightColor.x);
// float colorHue = RGBtoHSL(color.x);
// float halfDistance = hueDistance(0.0, brightHue) / 2.0;
// float3 closestColor = (colorHue < halfDistance) ? float3(0.0, 0.0, 0.0) : brightColor;
// float3 secondClosestColor = brightColor - closestColor;
// float d = indexValue(screenCoords);
// float distance = abs(closestColor - color);
// return (distance < d) ? closestColor : secondClosestColor;
// }
float3 dither(float color, float2 screenCoords) {
float closestColor = (color < 0.5) ? 0 : 1;
float secondClosestColor = 1 - closestColor;
float d = indexValue(screenCoords);
float distance = abs(closestColor - color);
return (distance < d) ? closestColor : secondClosestColor;
}

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

@ -0,0 +1,47 @@
#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.Position.x *= -1; // otherwise cube map render will be horizontally flipped
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

@ -0,0 +1,55 @@
#include "Macros.fxh"
BEGIN_CONSTANTS
float4x4 ViewProjection _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 instanced_vs(VertexShaderInput input, float3 Translation : TEXCOORD2)
{
VertexShaderOutput output;
float4x4 world = float4x4(
float4(1, 0, 0, 0),
float4(0, 1, 0, 0),
float4(0, 0, 1, 0),
float4(Translation.x, Translation.y, Translation.z, 1)
);
float4x4 worldViewProjection = mul(world, ViewProjection);
output.Position = mul(input.Position, worldViewProjection);
output.Position.x *= -1; // otherwise cube map render will be horizontally flipped
output.PositionWorld = mul(input.Position, world);
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 instanced_vs();
PixelShader = compile ps_3_0 main_ps();
}
}

View File

@ -52,8 +52,7 @@
sampler Name##Sampler : register(s##index) = sampler_state { Texture = (Name); }; sampler Name##Sampler : register(s##index) = sampler_state { Texture = (Name); };
#define SAMPLE_TEXTURE(Name, texCoord) tex2D(Name##Sampler, texCoord) #define SAMPLE_TEXTURE(Name, texCoord) tex2D(Name##Sampler, texCoord)
#define SAMPLE_TEXTURE_LOD(Name, texCoord) tex2Dlod(Name##Sampler, texCoord)
#define SAMPLE_VERTEX_TEXTURE(Name, texCoord) tex2Dlod(Name##Sampler, float4(texCoord, 0, 0))
#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

@ -0,0 +1,65 @@
#include "Macros.fxh"
DECLARE_TEXTURE(Texture, 0);
DECLARE_TEXTURE(Palette, 1);
BEGIN_CONSTANTS
int PaletteWidth _ps(c0) _cb(c0);
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;
}
float4 main_ps(PixelInput input) : SV_TARGET0
{
float4 sampled = SAMPLE_TEXTURE(Texture, input.TexCoord);
float3 sampled_color = sampled.rgb;
float3 closest_color = float3(0, 0, 0);
float closest_dist = 100000;
for (int i = 0; i < PaletteWidth; i++)
{
float texX = (i / (float)PaletteWidth);
float3 palette_color = SAMPLE_TEXTURE(Palette, float2(texX, 0));
float dist = distance(palette_color, sampled_color);
if (dist < closest_dist)
{
closest_dist = dist;
closest_color = palette_color;
}
}
return float4(closest_color, sampled.a);
}
Technique PaletteCrush
{
Pass
{
VertexShader = compile vs_3_0 main_vs();
PixelShader = compile ps_3_0 main_ps();
}
}

141
Effects/HLSL/Shadow.fxh Normal file
View File

@ -0,0 +1,141 @@
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 )
};
// TODO: this should probably sample a noise texture instead
// 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 PoissonShadow(
float3 positionWorldSpace,
float3 N,
float3 L,
float4x4 lightSpaceMatrix,
sampler shadowMap,
int shadowMapSize
) {
float bias = 0.005 * tan(acos(dot(N, L)));
bias = clamp(bias, 0, 0.01);
float4 positionLightSpace = mul(float4(positionWorldSpace, 1.0), lightSpaceMatrix);
// maps to [-1, 1]
float3 projectionCoords = positionLightSpace.xyz / positionLightSpace.w;
// maps to [0, 1]
// NOTE: In XNA, clip space z is [0, 1] range
projectionCoords.x = (projectionCoords.x * 0.5) + 0.5;
projectionCoords.y = (projectionCoords.y * 0.5) + 0.5;
projectionCoords.y *= -1;
if (projectionCoords.z > 1.0)
{
return 1.0;
}
float inc = 1.0 / shadowMapSize;
// Poisson soft shadows
float visibility = 0.0;
visibility = PoissonCoord(
shadowMap,
positionWorldSpace,
projectionCoords.xy,
projectionCoords.z,
bias
);
return visibility;
}
float HardShadow(
float3 positionWorldSpace,
float3 N,
float3 L,
float4x4 lightSpaceMatrix,
sampler shadowMap,
int shadowMapSize
) {
// float bias = 0.005 * tan(acos(dot(N, L)));
// bias = clamp(bias, 0, 0.01);
float bias = max(0.05 * (1.0 - dot(N, L)), 0.005);
float4 positionLightSpace = mul(float4(positionWorldSpace, 1.0), lightSpaceMatrix);
// maps to [-1, 1]
float3 projectionCoords = positionLightSpace.xyz / positionLightSpace.w;
// maps to [0, 1]
// NOTE: In XNA, clip space z is [0, 1] range
projectionCoords.x = (projectionCoords.x * 0.5) + 0.5;
projectionCoords.y = (projectionCoords.y * 0.5) + 0.5;
projectionCoords.y *= -1;
if (projectionCoords.z > 1.0)
{
return 1.0;
}
float closestDepth = tex2D(shadowMap, projectionCoords.xy);
float currentDepth = projectionCoords.z;
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 : 1.0);
}

View File

@ -2,10 +2,11 @@
BEGIN_CONSTANTS BEGIN_CONSTANTS
float4x4 ModelViewProjection _vs(c0) _cb(c0);
MATRIX_CONSTANTS MATRIX_CONSTANTS
float4x4 World _vs(c0) _cb(c0);
float4x4 ViewProjection _vs(c4) _cb(c4);
END_CONSTANTS END_CONSTANTS
struct VertexShaderInput struct VertexShaderInput
@ -15,7 +16,7 @@ struct VertexShaderInput
struct VertexShaderOutput struct VertexShaderOutput
{ {
float4 Position : SV_Position; float4 Position : SV_POSITION;
float Depth : TEXCOORD0; float Depth : TEXCOORD0;
}; };
@ -23,7 +24,8 @@ VertexShaderOutput main_vs(VertexShaderInput input)
{ {
VertexShaderOutput output; VertexShaderOutput output;
output.Position = mul(input.Position, ModelViewProjection); float4x4 worldViewProjection = mul(World, ViewProjection);
output.Position = mul(input.Position, worldViewProjection);
output.Depth = output.Position.z / output.Position.w; output.Depth = output.Position.z / output.Position.w;
return output; return output;

View File

@ -0,0 +1,45 @@
float4x4 ViewProjection;
struct VertexShaderInput
{
float4 Position : POSITION;
};
struct VertexShaderOutput
{
float4 Position : POSITION0;
float4 ProjectedPosition : TEXCOORD0;
};
VertexShaderOutput instanced_vs(VertexShaderInput input, float3 Translation : TEXCOORD2)
{
VertexShaderOutput output;
float4x4 world = float4x4(
float4(1, 0, 0, 0),
float4(0, 1, 0, 0),
float4(0, 0, 1, 0),
float4(Translation.x, Translation.y, Translation.z, 1)
);
float4x4 worldViewProjection = mul(world, ViewProjection);
output.Position = mul(input.Position, worldViewProjection);
output.ProjectedPosition = output.Position;
return output;
}
float4 main_ps(VertexShaderOutput input) : COLOR0
{
return float4(input.ProjectedPosition.z / input.ProjectedPosition.w, 0.0, 0.0, 0.0);
}
Technique SimpleDepth
{
Pass
{
VertexShader = compile vs_3_0 instanced_vs();
PixelShader = compile ps_3_0 main_ps();
}
}

View File

@ -0,0 +1,45 @@
#include "Macros.fxh"
DECLARE_CUBEMAP(skybox, 0);
BEGIN_CONSTANTS
float4x4 ViewProjection _vs(c0) _cb(c0);
END_CONSTANTS
struct VertexShaderInput
{
float3 Position : POSITION;
};
struct VertexShaderOutput
{
float4 Position : SV_POSITION;
float3 TexCoord : TEXCOORD;
};
VertexShaderOutput main_vs(VertexShaderInput input)
{
VertexShaderOutput output;
output.Position = mul(float4(input.Position, 1.0), ViewProjection);
output.Position = output.Position.xyww;
output.TexCoord = input.Position;
return output;
}
float4 main_ps(VertexShaderOutput input) : SV_TARGET0
{
return SAMPLE_CUBEMAP(skybox, input.TexCoord);
}
Technique Skybox
{
Pass
{
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

@ -0,0 +1,90 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class LinearDepthEffect : Effect, IHasWorldMatrix
{
EffectParameter worldParam;
EffectParameter worldViewProjectionParam;
EffectParameter lightPositionParam;
EffectParameter farPlaneParam;
EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
Matrix world;
Matrix view;
Matrix projection;
public Vector3 LightPosition { get; set; }
public float FarPlane { get; set; }
public Matrix World
{
get { return world; }
set
{
world = 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 world, ref viewProjection, out Matrix worldViewProj);
worldViewProjectionParam.SetValue(worldViewProj);
dirtyFlags &= ~EffectDirtyFlags.WorldViewProj;
}
if ((dirtyFlags & EffectDirtyFlags.World) != 0)
{
worldParam.SetValue(world);
dirtyFlags &= ~EffectDirtyFlags.World;
}
lightPositionParam.SetValue(LightPosition);
farPlaneParam.SetValue(FarPlane);
}
private void CacheEffectParameters()
{
worldParam = Parameters["Model"];
worldViewProjectionParam = Parameters["ModelViewProjection"];
lightPositionParam = Parameters["LightPosition"];
farPlaneParam = Parameters["FarPlane"];
}
}
}

View File

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

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; }
@ -139,7 +130,7 @@ namespace Kav
{ {
albedoTextureParam.SetValue(value); albedoTextureParam.SetValue(value);
albedoTextureEnabled = value != null; albedoTextureEnabled = value != null;
dirtyFlags |= EffectDirtyFlags.ShaderIndex; dirtyFlags |= EffectDirtyFlags.PixelShaderIndex;
} }
} }
@ -150,7 +141,7 @@ namespace Kav
{ {
normalTextureParam.SetValue(value); normalTextureParam.SetValue(value);
normalMapEnabled = value != null; normalMapEnabled = value != null;
dirtyFlags |= EffectDirtyFlags.ShaderIndex; dirtyFlags |= EffectDirtyFlags.PixelShaderIndex;
} }
} }
@ -173,7 +164,7 @@ namespace Kav
{ {
metallicRoughnessTextureParam.SetValue(value); metallicRoughnessTextureParam.SetValue(value);
metallicRoughnessMapEnabled = value != null; metallicRoughnessMapEnabled = value != null;
dirtyFlags |= EffectDirtyFlags.ShaderIndex; dirtyFlags |= EffectDirtyFlags.PixelShaderIndex;
} }
} }
@ -204,62 +195,6 @@ namespace Kav
Parameters["PositionLightColors"], Parameters["PositionLightColors"],
MaxPointLights MaxPointLights
); );
directionalLightCollection = new DirectionalLightCollection(
Parameters["LightDirections"],
Parameters["DirectionLightColors"],
Parameters["DirectionalLightMatrices"]
);
}
protected PBREffect(PBREffect cloneSource) : base(cloneSource)
{
CacheEffectParameters();
World = cloneSource.World;
View = cloneSource.View;
Projection = cloneSource.Projection;
PointLights = new PointLightCollection(
Parameters["LightPositions"],
Parameters["PositionLightColors"],
MaxPointLights
);
for (int i = 0; i < MaxPointLights; i++)
{
PointLights[i] = cloneSource.PointLights[i];
}
DirectionalLights = new DirectionalLightCollection(
Parameters["LightDirections"],
Parameters["DirectionLightColors"],
Parameters["DirectionalLightMatrices"]
);
for (int i = 0; i < MaxDirectionalLights; i++)
{
DirectionalLights[i] = cloneSource.DirectionalLights[i];
}
AlbedoTexture = cloneSource.AlbedoTexture;
NormalTexture = cloneSource.NormalTexture;
EmissionTexture = cloneSource.EmissionTexture;
OcclusionTexture = cloneSource.OcclusionTexture;
MetallicRoughnessTexture = cloneSource.MetallicRoughnessTexture;
EnvDiffuseTexture = cloneSource.EnvDiffuseTexture;
BRDFLutTexture = cloneSource.BRDFLutTexture;
EnvSpecularTexture = cloneSource.EnvSpecularTexture;
Albedo = cloneSource.Albedo;
Metallic = cloneSource.Metallic;
Roughness = cloneSource.Roughness;
AO = cloneSource.AO;
}
public override Effect Clone()
{
return new PBREffect(this);
} }
protected override void OnApply() protected override void OnApply()
@ -292,7 +227,7 @@ namespace Kav
dirtyFlags &= ~EffectDirtyFlags.EyePosition; dirtyFlags &= ~EffectDirtyFlags.EyePosition;
} }
if ((dirtyFlags & EffectDirtyFlags.ShaderIndex) != 0) if ((dirtyFlags & EffectDirtyFlags.PixelShaderIndex) != 0)
{ {
int shaderIndex = 0; int shaderIndex = 0;
@ -327,7 +262,7 @@ namespace Kav
shaderIndexParam.SetValue(shaderIndex); shaderIndexParam.SetValue(shaderIndex);
dirtyFlags &= ~EffectDirtyFlags.ShaderIndex; dirtyFlags &= ~EffectDirtyFlags.PixelShaderIndex;
} }
} }

View File

@ -0,0 +1,51 @@
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class PaletteCrushEffect : Effect
{
EffectParameter textureParam;
EffectParameter paletteParam;
EffectParameter paletteWidthParam;
Texture2D texture;
Texture2D palette;
int paletteWidth;
public Texture2D Texture
{
get { return texture; }
set
{
texture = value;
textureParam.SetValue(texture);
}
}
public Texture2D Palette
{
get { return palette; }
set
{
palette = value;
paletteWidth = palette.Width;
paletteParam.SetValue(palette);
paletteWidthParam.SetValue(paletteWidth);
}
}
public PaletteCrushEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.PaletteCrushEffect)
{
CacheEffectParameters();
}
void CacheEffectParameters()
{
textureParam = Parameters["Texture"];
paletteParam = Parameters["Palette"];
paletteWidthParam = Parameters["PaletteWidth"];
}
}
}

View File

@ -7,41 +7,24 @@ namespace Kav
{ {
private readonly Vector3[] positions; private readonly Vector3[] positions;
private readonly Vector3[] colors; private readonly Vector3[] colors;
private readonly float[] intensities;
readonly EffectParameter lightPositionsParam; readonly EffectParameter lightPositionsParam;
readonly EffectParameter lightColorsParam; readonly EffectParameter lightColorsParam;
public PointLightCollection(EffectParameter lightPositionsParam, EffectParameter lightColorsParam, int maxLights) public PointLightCollection(EffectParameter lightPositionsParam, EffectParameter lightColorsParam, int maxLights)
{ {
this.positions = new Vector3[maxLights]; positions = new Vector3[maxLights];
this.colors = new Vector3[maxLights]; colors = new Vector3[maxLights];
this.intensities = new float[maxLights];
this.lightPositionsParam = lightPositionsParam; this.lightPositionsParam = lightPositionsParam;
this.lightColorsParam = lightColorsParam; this.lightColorsParam = lightColorsParam;
} }
public PointLight this[int i] public PointLight this[int i]
{ {
get
{
var color = colors[i] / intensities[i];
return new PointLight(
positions[i],
new Color(
color.X,
color.Y,
color.Z,
1f
),
intensities[i]
);
}
set set
{ {
positions[i] = value.Position; positions[i] = value.Position;
colors[i] = value.Color.ToVector3() * value.Intensity; colors[i] = value.Color.ToVector3() * value.Radius;
intensities[i] = value.Intensity;
lightPositionsParam.SetValue(positions); lightPositionsParam.SetValue(positions);
lightColorsParam.SetValue(colors); lightColorsParam.SetValue(colors);
} }

View File

@ -3,23 +3,24 @@ using Microsoft.Xna.Framework.Graphics;
namespace Kav namespace Kav
{ {
public class SimpleDepthEffect : Effect public class SimpleDepthEffect : Effect, IHasWorldMatrix
{ {
EffectParameter modelViewProjectionParam; EffectParameter worldParam;
EffectParameter viewProjectionParam;
Matrix model; Matrix world;
Matrix view; Matrix view;
Matrix projection; Matrix projection;
EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All; EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
public Matrix Model public Matrix World
{ {
get { return model; } get { return world; }
set set
{ {
model = value; world = value;
dirtyFlags |= EffectDirtyFlags.WorldViewProj; dirtyFlags |= EffectDirtyFlags.World;
} }
} }
@ -29,7 +30,7 @@ namespace Kav
set set
{ {
view = value; view = value;
dirtyFlags |= EffectDirtyFlags.WorldViewProj; dirtyFlags |= EffectDirtyFlags.ViewProj;
} }
} }
@ -39,7 +40,7 @@ namespace Kav
set set
{ {
projection = value; projection = value;
dirtyFlags |= EffectDirtyFlags.WorldViewProj; dirtyFlags |= EffectDirtyFlags.ViewProj;
} }
} }
@ -50,21 +51,26 @@ namespace Kav
protected override void OnApply() protected override void OnApply()
{ {
if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0) if ((dirtyFlags & EffectDirtyFlags.World) != 0)
{ {
Matrix.Multiply(ref model, ref view, out Matrix modelView); worldParam.SetValue(world);
Matrix.Multiply(ref modelView, ref projection, out Matrix worldViewProj);
modelViewProjectionParam.SetValue(worldViewProj); dirtyFlags &= ~EffectDirtyFlags.World;
dirtyFlags &= ~EffectDirtyFlags.WorldViewProj;
} }
if ((dirtyFlags & EffectDirtyFlags.ViewProj) != 0)
{
Matrix.Multiply(ref view, ref projection, out Matrix viewProjection);
viewProjectionParam.SetValue(viewProjection);
dirtyFlags &= ~EffectDirtyFlags.ViewProj;
}
} }
private void CacheEffectParameters() private void CacheEffectParameters()
{ {
modelViewProjectionParam = Parameters["ModelViewProjection"]; worldParam = Parameters["World"];
viewProjectionParam = Parameters["ViewProjection"];
} }
} }
} }

View File

@ -0,0 +1,56 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class SimpleDepthEffectInstanced : Effect
{
EffectParameter viewProjectionParam;
Matrix view;
Matrix projection;
EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
public Matrix View
{
get { return view; }
set
{
view = value;
dirtyFlags |= EffectDirtyFlags.ViewProj;
}
}
public Matrix Projection
{
get { return projection; }
set
{
projection = value;
dirtyFlags |= EffectDirtyFlags.ViewProj;
}
}
public SimpleDepthEffectInstanced(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.SimpleDepthEffectInstanced)
{
CacheEffectParameters();
}
protected override void OnApply()
{
if ((dirtyFlags & EffectDirtyFlags.ViewProj) != 0)
{
Matrix.Multiply(ref view, ref projection, out Matrix viewProjection);
viewProjectionParam.SetValue(viewProjection);
dirtyFlags &= ~EffectDirtyFlags.ViewProj;
}
}
private void CacheEffectParameters()
{
viewProjectionParam = Parameters["ViewProjection"];
}
}
}

77
Effects/SkyboxEffect.cs Normal file
View File

@ -0,0 +1,77 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class SkyboxEffect : Effect
{
EffectParameter viewProjectionParam;
EffectParameter skyboxParam;
Matrix view;
Matrix projection;
TextureCube skybox;
EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
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 TextureCube Skybox
{
get { return skybox; }
set
{
skybox = value;
dirtyFlags |= EffectDirtyFlags.World; // hack flag but whatever
}
}
public SkyboxEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.SkyboxEffect)
{
CacheEffectParameters();
}
protected override void OnApply()
{
if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0)
{
Matrix.Multiply(ref view, ref projection, out Matrix viewProjection);
viewProjectionParam.SetValue(viewProjection);
dirtyFlags &= ~EffectDirtyFlags.WorldViewProj;
}
if ((dirtyFlags & EffectDirtyFlags.World) != 0)
{
skyboxParam.SetValue(skybox);
dirtyFlags &= ~EffectDirtyFlags.World;
}
}
private void CacheEffectParameters()
{
viewProjectionParam = Parameters["ViewProjection"];
skyboxParam = Parameters["skybox"];
}
}
}

View File

@ -0,0 +1,9 @@
namespace Kav
{
public enum SpriteBillboardConstraint
{
None,
Horizontal,
Full
}
}

View File

@ -1,16 +0,0 @@
using Microsoft.Xna.Framework;
namespace Kav.Extensions
{
public static class Vector3Extensions
{
public static Vector3 Floor(this Vector3 input)
{
var x = (float)System.Math.Floor(input.X);
var y = (float)System.Math.Floor(input.Y);
var z = (float)System.Math.Floor(input.Z);
return new Vector3(x, y, z);
}
}
}

View File

@ -0,0 +1,9 @@
using Microsoft.Xna.Framework;
namespace Kav
{
public interface ICullable
{
BoundingBox BoundingBox { get; }
}
}

View File

@ -0,0 +1,19 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public interface IGBufferDrawable
{
Vector3 Albedo { get; }
float Metallic { get; }
float Roughness { get; }
Texture2D AlbedoTexture { get; }
Texture2D NormalTexture { get; }
Texture2D MetallicRoughnessTexture { get; }
int NumTextureRows { get; }
int NumTextureColumns { get; }
}
}

View File

@ -0,0 +1,9 @@
using Microsoft.Xna.Framework;
namespace Kav
{
public interface IHasVertexPositions
{
Vector3[] Positions { get; }
}
}

View File

@ -0,0 +1,10 @@
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public interface IIndexDrawable
{
VertexBuffer VertexBuffer { get; }
IndexBuffer IndexBuffer { get; }
}
}

View File

@ -0,0 +1,9 @@
using Microsoft.Xna.Framework;
namespace Kav
{
public interface ITransformable
{
Matrix TransformMatrix { get; }
}
}

View File

@ -1,12 +1,23 @@
using Microsoft.Xna.Framework;
namespace Kav namespace Kav
{ {
public class Mesh public class Mesh
{ {
public MeshPart[] MeshParts { get; } public MeshPart[] MeshParts { get; }
public BoundingBox BoundingBox { get; }
public Mesh(MeshPart[] meshParts) public Mesh(MeshPart[] meshParts)
{ {
MeshParts = meshParts; MeshParts = meshParts;
BoundingBox boundingBox = new BoundingBox();
foreach (var meshPart in MeshParts)
{
boundingBox = BoundingBox.CreateMerged(boundingBox, meshPart.BoundingBox);
}
BoundingBox = boundingBox;
} }
} }
} }

View File

@ -3,21 +3,60 @@ using Microsoft.Xna.Framework.Graphics;
namespace Kav namespace Kav
{ {
public class MeshPart public class MeshPart : IIndexDrawable, IGBufferDrawable, ICullable, IHasVertexPositions
{ {
public IndexBuffer IndexBuffer { get; } public IndexBuffer IndexBuffer { get; }
public VertexBuffer VertexBuffer { get; } public VertexBuffer VertexBuffer { get; }
public Triangle[] Triangles { get; } public Triangle[] Triangles { get; }
public Vector3[] Positions { get; } public Vector3[] Positions { get; }
public Effect Effect { get; }
public MeshPart(VertexBuffer vertexBuffer, IndexBuffer indexBuffer, Vector3[] positions, Triangle[] triangles, Effect effect) public BoundingBox BoundingBox { get; }
private Texture2D albedoTexture = null;
private Texture2D normalTexture = null;
private Texture2D metallicRoughnessTexture = null;
public Texture2D AlbedoTexture
{ {
get { return DisableAlbedoMap ? null : albedoTexture; }
set { albedoTexture = value; }
}
public Texture2D NormalTexture
{
get { return DisableNormalMap ? null : normalTexture; }
set { normalTexture = value; }
}
public Texture2D MetallicRoughnessTexture
{
get { return DisableMetallicRoughnessMap ? null : metallicRoughnessTexture; }
set { metallicRoughnessTexture = value; }
}
public Vector3 Albedo { get; set; } = Vector3.One;
public float Metallic { get; set; } = 0.5f;
public float Roughness { get; set; } = 0.5f;
public int NumTextureRows { get; set; } = 1;
public int NumTextureColumns { get; set; } = 1;
public bool DisableAlbedoMap { get; set; } = false;
public bool DisableNormalMap { get; set; } = false;
public bool DisableMetallicRoughnessMap { get; set; } = false;
public MeshPart(
VertexBuffer vertexBuffer,
IndexBuffer indexBuffer,
Vector3[] positions,
Triangle[] triangles
) {
VertexBuffer = vertexBuffer; VertexBuffer = vertexBuffer;
IndexBuffer = indexBuffer; IndexBuffer = indexBuffer;
Positions = positions; Positions = positions;
Triangles = triangles; Triangles = triangles;
Effect = effect;
BoundingBox = BoundingBox.CreateFromPoints(Positions);
} }
} }
} }

View File

@ -2,13 +2,97 @@ using Microsoft.Xna.Framework;
namespace Kav namespace Kav
{ {
public class Model public class Model : ICullable
{ {
public Mesh[] Meshes { get; } public Mesh[] Meshes { get; }
public BoundingBox BoundingBox { get; }
public Color Albedo
{
set
{
foreach (var mesh in Meshes)
{
foreach (var meshPart in mesh.MeshParts)
{
meshPart.Albedo = value.ToVector3();
}
}
}
}
public float Metallic
{
set
{
foreach (var mesh in Meshes)
{
foreach (var meshPart in mesh.MeshParts)
{
meshPart.Metallic = value;
}
}
}
}
public float Roughness
{
set
{
foreach (var mesh in Meshes)
{
foreach (var meshPart in mesh.MeshParts)
{
meshPart.Roughness = value;
}
}
}
}
public Model(Mesh[] meshes) public Model(Mesh[] meshes)
{ {
Meshes = meshes; Meshes = meshes;
BoundingBox boundingBox = new BoundingBox();
foreach (var mesh in Meshes)
{
boundingBox = BoundingBox.CreateMerged(boundingBox, mesh.BoundingBox);
}
BoundingBox = boundingBox;
}
public void DisableAlbedoMaps()
{
foreach (var mesh in Meshes)
{
foreach (var meshPart in mesh.MeshParts)
{
meshPart.DisableAlbedoMap = true;
}
}
}
public void DisableNormalMaps()
{
foreach (var mesh in Meshes)
{
foreach (var meshPart in mesh.MeshParts)
{
meshPart.DisableNormalMap = true;
}
}
}
public void DisableMetallicRoughnessMaps()
{
foreach (var mesh in Meshes)
{
foreach (var meshPart in mesh.MeshParts)
{
meshPart.DisableMetallicRoughnessMap = true;
}
}
} }
} }
} }

57
Geometry/Sprite.cs Normal file
View File

@ -0,0 +1,57 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public struct Sprite
{
public Texture2D Texture { get; }
public Vector3 Position { get; }
public Vector2 Origin { get; }
public float Rotation { get; }
public Vector2 Scale { get; }
public SpriteBillboardConstraint BillboardConstraint { get; }
public Matrix TransformMatrix { get; }
public Sprite(
Texture2D texture,
Vector3 position,
Vector2 origin,
float rotation,
Vector2 scale,
SpriteBillboardConstraint billboardConstraint = SpriteBillboardConstraint.None
) {
Texture = texture;
Position = position;
Origin = origin;
Rotation = rotation;
Scale = scale;
BillboardConstraint = billboardConstraint;
TransformMatrix = ConstructTransformMatrix(Position, Scale);
}
public Sprite(
Texture2D texture,
Vector3 position,
SpriteBillboardConstraint billboardConstraint = SpriteBillboardConstraint.None
) {
Texture = texture;
Position = position;
Origin = Vector2.Zero;
Rotation = 0;
Scale = Vector2.One;
BillboardConstraint = billboardConstraint;
TransformMatrix = ConstructTransformMatrix(Position, Scale);
}
private static Matrix ConstructTransformMatrix(
Vector3 position,
Vector2 scale
) {
return
Matrix.CreateTranslation(position) *
Matrix.CreateScale(scale.X, scale.Y, 1);
}
}
}

109
Geometry/SpriteMesh.cs Normal file
View File

@ -0,0 +1,109 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class SpriteMesh : ICullable, IIndexDrawable
{
public enum FlipOptions
{
None,
Horizontal,
Vertical,
Both
}
private static readonly short[] Indices = new short[]
{
0,
1,
2,
1,
3,
2
};
public IndexBuffer IndexBuffer { get; }
public VertexBuffer VertexBuffer { get; }
public BoundingBox BoundingBox { get; }
public SpriteMesh(
GraphicsDevice graphicsDevice,
int width,
int height,
FlipOptions flipOptions
) {
IndexBuffer = new IndexBuffer(
graphicsDevice,
IndexElementSize.SixteenBits,
6,
BufferUsage.WriteOnly
);
IndexBuffer.SetData(Indices);
var vertexArray = GenerateVertexArray(width, height, flipOptions);
VertexBuffer = new VertexBuffer(
graphicsDevice,
typeof(VertexPositionNormalTexture),
4,
BufferUsage.WriteOnly
);
VertexBuffer.SetData(vertexArray);
BoundingBox = BoundingBox.CreateFromPoints(Positions(vertexArray));
}
private static VertexPositionNormalTexture[] GenerateVertexArray(int pixelWidth, int pixelHeight, FlipOptions flipOptions)
{
var width = pixelWidth / (float)40;
var height = pixelHeight / (float)40;
VertexPositionNormalTexture[] result = new VertexPositionNormalTexture[4];
var xLeft = 0;
var xRight = 1;
var yTop = 0;
var yBottom = 1;
if (flipOptions == FlipOptions.Horizontal || flipOptions == FlipOptions.Both)
{
xLeft = 1;
xRight = 0;
}
if (flipOptions == FlipOptions.Vertical || flipOptions == FlipOptions.Both)
{
yTop = 1;
yBottom = 0;
}
result[0].Position = new Vector3(-width / 2, height / 2, 0);
result[0].Normal = new Vector3(0, 0, -1);
result[0].TextureCoordinate = new Vector2(xLeft, yTop);
result[1].Position = new Vector3(width / 2, height / 2, 0);
result[1].Normal = new Vector3(0, 0, -1);
result[1].TextureCoordinate = new Vector2(xRight, yTop);
result[2].Position = new Vector3(-width / 2, -height / 2, 0);
result[2].Normal = new Vector3(0, 0, -1);
result[2].TextureCoordinate = new Vector2(xLeft, yBottom);
result[3].Position = new Vector3(width / 2, -height / 2, 0);
result[3].Normal = new Vector3(0, 0, -1);
result[3].TextureCoordinate = new Vector2(xRight, yBottom);
return result;
}
private static IEnumerable<Vector3> Positions(IEnumerable<VertexPositionNormalTexture> vertices)
{
foreach (var vertex in vertices)
{
yield return vertex.Position;
}
}
}
}

View File

@ -2,24 +2,39 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<RootNamespace>Kav</RootNamespace> <RootNamespace>Kav</RootNamespace>
<Authors>Evan Hemsley</Authors> <Authors>Evan Hemsley</Authors>
<Copyright>Evan Hemsley 2020</Copyright> <Copyright>Evan Hemsley 2020</Copyright>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<AssemblyName>Kav</AssemblyName> <AssemblyName>Kav</AssemblyName>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <Platforms>x64</Platforms>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="../FNA/FNA.Core.csproj" /> <ProjectReference Include="../FNA/FNA.Core.csproj" />
<ProjectReference Include="../Smuggler/Smuggler.Core.csproj" /> <ProjectReference Include="../Smuggler/Smuggler.Core.csproj" />
<ProjectReference Include="../MoonTools.Bonk/Bonk/Bonk.csproj" />
</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\Deferred_ToonEffect.fxb">
<LogicalName>Kav.Resources.Deferred_ToonEffect.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>
@ -29,6 +44,30 @@
<EmbeddedResource Include="Effects\FXB\SimpleDepthEffect.fxb"> <EmbeddedResource Include="Effects\FXB\SimpleDepthEffect.fxb">
<LogicalName>Kav.Resources.SimpleDepthEffect.fxb</LogicalName> <LogicalName>Kav.Resources.SimpleDepthEffect.fxb</LogicalName>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\SimpleDepthEffectInstanced.fxb">
<LogicalName>Kav.Resources.SimpleDepthEffectInstanced.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\LinearDepthEffect.fxb">
<LogicalName>Kav.Resources.LinearDepthEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\LinearDepthEffectInstanced.fxb">
<LogicalName>Kav.Resources.LinearDepthEffectInstanced.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\SkyboxEffect.fxb">
<LogicalName>Kav.Resources.SkyboxEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\DiffuseLitSpriteEffect.fxb">
<LogicalName>Kav.Resources.DiffuseLitSpriteEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\PaletteCrushEffect.fxb">
<LogicalName>Kav.Resources.PaletteCrushEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Models\UnitCube.glb">
<LogicalName>Kav.Resources.UnitCube.glb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Models\UnitSphere.glb">
<LogicalName>Kav.Resources.UnitSphere.glb</LogicalName>
</EmbeddedResource>
</ItemGroup> </ItemGroup>
</Project> </Project>

22
Kav.Core.sln Normal file
View File

@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26124.0
MinimumVisualStudioVersion = 15.0.26124.0
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kav", "Kav.Core.csproj", "{34C1B784-AE9C-4EBC-A03A-AC401CB1219A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{34C1B784-AE9C-4EBC-A03A-AC401CB1219A}.Debug|x64.ActiveCfg = Debug|x64
{34C1B784-AE9C-4EBC-A03A-AC401CB1219A}.Debug|x64.Build.0 = Debug|x64
{34C1B784-AE9C-4EBC-A03A-AC401CB1219A}.Release|x64.ActiveCfg = Release|x64
{34C1B784-AE9C-4EBC-A03A-AC401CB1219A}.Release|x64.Build.0 = Release|x64
EndGlobalSection
EndGlobal

View File

@ -1,25 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<RootNamespace>Kav</RootNamespace> <RootNamespace>Kav</RootNamespace>
<Authors>Evan Hemsley</Authors> <Authors>Evan Hemsley</Authors>
<Copyright>Evan Hemsley 2020</Copyright> <Copyright>Evan Hemsley 2020</Copyright>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<AssemblyName>Kav</AssemblyName> <AssemblyName>Kav</AssemblyName>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <Platforms>x64;x86</Platforms>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="../FNA/FNA.csproj" /> <ProjectReference Include="../FNA/FNA.csproj" />
<ProjectReference Include="../Smuggler/Smuggler.Framework.csproj" /> <ProjectReference Include="../Smuggler/Smuggler.Framework.csproj" />
<ProjectReference Include="../MoonTools.Bonk/Bonk/Bonk.csproj" />
</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\Deferred_ToonEffect.fxb">
<LogicalName>Kav.Resources.Deferred_ToonEffect.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>
@ -29,6 +44,30 @@
<EmbeddedResource Include="Effects\FXB\SimpleDepthEffect.fxb"> <EmbeddedResource Include="Effects\FXB\SimpleDepthEffect.fxb">
<LogicalName>Kav.Resources.SimpleDepthEffect.fxb</LogicalName> <LogicalName>Kav.Resources.SimpleDepthEffect.fxb</LogicalName>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\SimpleDepthEffectInstanced.fxb">
<LogicalName>Kav.Resources.SimpleDepthEffectInstanced.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\LinearDepthEffect.fxb">
<LogicalName>Kav.Resources.LinearDepthEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\LinearDepthEffectInstanced.fxb">
<LogicalName>Kav.Resources.LinearDepthEffectInstanced.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\SkyboxEffect.fxb">
<LogicalName>Kav.Resources.SkyboxEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\DiffuseLitSpriteEffect.fxb">
<LogicalName>Kav.Resources.DiffuseLitSpriteEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\PaletteCrushEffect.fxb">
<LogicalName>Kav.Resources.PaletteCrushEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Models\UnitCube.glb">
<LogicalName>Kav.Resources.UnitCube.glb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Models\UnitSphere.glb">
<LogicalName>Kav.Resources.UnitSphere.glb</LogicalName>
</EmbeddedResource>
</ItemGroup> </ItemGroup>
</Project> </Project>

22
Kav.Framework.sln Normal file
View File

@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26124.0
MinimumVisualStudioVersion = 15.0.26124.0
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kav.Framework", "Kav.Framework.csproj", "{B46F5438-2EE3-4EB1-9ECD-251D3B3CBD9A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B46F5438-2EE3-4EB1-9ECD-251D3B3CBD9A}.Debug|x64.ActiveCfg = Debug|Any CPU
{B46F5438-2EE3-4EB1-9ECD-251D3B3CBD9A}.Debug|x64.Build.0 = Debug|Any CPU
{B46F5438-2EE3-4EB1-9ECD-251D3B3CBD9A}.Release|x64.ActiveCfg = Release|Any CPU
{B46F5438-2EE3-4EB1-9ECD-251D3B3CBD9A}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

34
Kav.sln
View File

@ -1,34 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26124.0
MinimumVisualStudioVersion = 15.0.26124.0
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kav", "Kav.csproj", "{34C1B784-AE9C-4EBC-A03A-AC401CB1219A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{34C1B784-AE9C-4EBC-A03A-AC401CB1219A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{34C1B784-AE9C-4EBC-A03A-AC401CB1219A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{34C1B784-AE9C-4EBC-A03A-AC401CB1219A}.Debug|x64.ActiveCfg = Debug|Any CPU
{34C1B784-AE9C-4EBC-A03A-AC401CB1219A}.Debug|x64.Build.0 = Debug|Any CPU
{34C1B784-AE9C-4EBC-A03A-AC401CB1219A}.Debug|x86.ActiveCfg = Debug|Any CPU
{34C1B784-AE9C-4EBC-A03A-AC401CB1219A}.Debug|x86.Build.0 = Debug|Any CPU
{34C1B784-AE9C-4EBC-A03A-AC401CB1219A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{34C1B784-AE9C-4EBC-A03A-AC401CB1219A}.Release|Any CPU.Build.0 = Release|Any CPU
{34C1B784-AE9C-4EBC-A03A-AC401CB1219A}.Release|x64.ActiveCfg = Release|Any CPU
{34C1B784-AE9C-4EBC-A03A-AC401CB1219A}.Release|x64.Build.0 = Release|Any CPU
{34C1B784-AE9C-4EBC-A03A-AC401CB1219A}.Release|x86.ActiveCfg = Release|Any CPU
{34C1B784-AE9C-4EBC-A03A-AC401CB1219A}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

14
Lights/AmbientLight.cs Normal file
View File

@ -0,0 +1,14 @@
using Microsoft.Xna.Framework;
namespace Kav
{
public struct AmbientLight
{
public Color Color { get; set; }
public AmbientLight(Color color)
{
Color = color;
}
}
}

View File

@ -8,6 +8,22 @@ namespace Kav
public Color Color { get; } public Color Color { get; }
public float Intensity { get; } public float Intensity { get; }
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) public DirectionalLight(Vector3 direction, Color color, float intensity = 1f)
{ {
Direction = direction; Direction = direction;

View File

@ -1,18 +1,55 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
namespace Kav namespace Kav
{ {
public struct PointLight public sealed class PointLight : IDisposable
{ {
public Vector3 Position { get; } public Vector3 Position { get; }
public Color Color { get; } public Color Color { get; }
public float Intensity { get; } public float Radius { get; }
public PointLight(Vector3 position, Color color, float intensity = 1f) public BoundingSphere BoundingSphere { get; }
{
public RenderTargetCube ShadowMap { get; }
public PointLight(
GraphicsDevice graphicsDevice,
Vector3 position,
Color color,
float radius,
int shadowMapSize
) {
Position = position; Position = position;
Color = color; Color = color;
Intensity = intensity; Radius = radius;
BoundingSphere = new BoundingSphere(position, Radius);
ShadowMap = new RenderTargetCube(
graphicsDevice,
shadowMapSize,
false,
SurfaceFormat.Single,
DepthFormat.Depth24,
0,
RenderTargetUsage.PreserveContents
);
var currentRTs = graphicsDevice.GetRenderTargets();
foreach (CubeMapFace face in Enum.GetValues(typeof(CubeMapFace)))
{
graphicsDevice.SetRenderTarget(ShadowMap, face);
graphicsDevice.Clear(Color.White);
}
graphicsDevice.SetRenderTargets(currentRTs);
}
public void Dispose()
{
ShadowMap.Dispose();
} }
} }
} }

View File

@ -0,0 +1,18 @@
using System.IO;
using System.Text.Json;
namespace Kav
{
public static class CrunchAtlasReader
{
static JsonSerializerOptions options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
public static CrunchTextureAtlasData ReadTextureAtlas(FileInfo file)
{
return JsonSerializer.Deserialize<CrunchTextureAtlasData>(File.ReadAllText(file.FullName), options);
}
}
}

View File

@ -0,0 +1,22 @@
namespace Kav
{
public struct CrunchTextureAtlasData
{
public CrunchTextureAtlasTextureData[] Textures { get; set; }
}
public struct CrunchTextureAtlasTextureData
{
public string Name { get; set; }
public CrunchTextureAtlasImageData[] Images { get; set; }
}
public struct CrunchTextureAtlasImageData
{
public string N { get; set; }
public int X { get; set; }
public int Y { get; set; }
public int W { get; set; }
public int H { get; set; }
}
}

View File

@ -15,19 +15,6 @@ namespace Kav
foreach (var meshPartData in meshData.MeshParts) foreach (var meshPartData in meshData.MeshParts)
{ {
var effect = new Kav.DeferredPBR_GBufferEffect(
graphicsDevice
)
{
Albedo = meshPartData.Albedo,
Metallic = meshPartData.Metallic,
Roughness = meshPartData.Roughness,
AlbedoTexture = meshPartData.AlbedoTexture,
NormalTexture = meshPartData.NormalTexture,
MetallicRoughnessTexture = meshPartData.MetallicRoughnessTexture
};
var triangles = new Kav.Triangle[meshPartData.Triangles.Length]; var triangles = new Kav.Triangle[meshPartData.Triangles.Length];
for (int i = 0; i < meshPartData.Triangles.Length; i++) for (int i = 0; i < meshPartData.Triangles.Length; i++)
{ {
@ -40,13 +27,22 @@ namespace Kav
); );
} }
meshParts.Add(new Kav.MeshPart( var meshPart = new Kav.MeshPart(
meshPartData.VertexBuffer, meshPartData.VertexBuffer,
meshPartData.IndexBuffer, meshPartData.IndexBuffer,
meshPartData.Positions, meshPartData.Positions,
triangles, triangles
effect );
));
meshPart.Albedo = meshPartData.Albedo;
meshPart.Metallic = meshPartData.Metallic;
meshPart.Roughness = meshPartData.Roughness;
meshPart.AlbedoTexture = meshPartData.AlbedoTexture;
meshPart.NormalTexture = meshPartData.NormalTexture;
meshPart.MetallicRoughnessTexture = meshPartData.MetallicRoughnessTexture;
meshParts.Add(meshPart);
} }
meshes.Add(new Kav.Mesh(meshParts.ToArray())); meshes.Add(new Kav.Mesh(meshParts.ToArray()));

BIN
Models/UnitCube.glb Normal file

Binary file not shown.

BIN
Models/UnitSphere.glb Normal file

Binary file not shown.

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
- [x] Frustum culling
- [x] Shadow-casting point lights
- [ ] Parabolic lights
- [x] Skyboxes
- [ ] Screen-space reflection
Nice-To-Haves
- [ ] Anti-aliasing
- [ ] Image-based lighting
- [ ] Volumetric lighting
- [ ] Volumetric smoke
- [ ]

File diff suppressed because it is too large Load Diff

View File

@ -4,25 +4,84 @@ namespace Kav
{ {
internal class Resources internal class Resources
{ {
public static byte[] DeferredPBR_AmbientLightEffect
{
get
{
if (ambientLightEffect == null)
{
ambientLightEffect = GetResource("DeferredPBR_AmbientLightEffect.fxb");
}
return ambientLightEffect;
}
}
public static byte[] DeferredPBR_PointLightEffect
{
get
{
if (pointLightEffect == null)
{
pointLightEffect = GetResource("DeferredPBR_PointLightEffect.fxb");
}
return pointLightEffect;
}
}
public static byte[] DeferredPBR_DirectionalLightEffect
{
get
{
if (directionalLightEffect == null)
{
directionalLightEffect = GetResource("DeferredPBR_DirectionalLightEffect.fxb");
}
return directionalLightEffect;
}
}
public static byte[] DeferredPBR_GBufferEffect public static byte[] DeferredPBR_GBufferEffect
{ {
get get
{ {
if (gBufferEffect == null) if (gBufferEffect == null)
{ {
gBufferEffect = GetResource("DeferredPBR_GBufferEffect"); gBufferEffect = GetResource("DeferredPBR_GBufferEffect.fxb");
} }
return gBufferEffect; return gBufferEffect;
} }
} }
public static byte[] ToneMapEffect
{
get
{
if (toneMapEffect == null)
{
toneMapEffect = GetResource("ToneMapEffect.fxb");
}
return toneMapEffect;
}
}
public static byte[] Deferred_ToonEffect
{
get
{
if (deferredToonEffect == null)
{
deferredToonEffect = GetResource("Deferred_ToonEffect.fxb");
}
return deferredToonEffect;
}
}
public static byte[] DeferredPBREffect public static byte[] DeferredPBREffect
{ {
get get
{ {
if (deferredPBREffect == null) if (deferredPBREffect == null)
{ {
deferredPBREffect = GetResource("DeferredPBREffect"); deferredPBREffect = GetResource("DeferredPBREffect.fxb");
} }
return deferredPBREffect; return deferredPBREffect;
} }
@ -34,7 +93,7 @@ namespace Kav
{ {
if (pbrEffect == null) if (pbrEffect == null)
{ {
pbrEffect = GetResource("PBREffect"); pbrEffect = GetResource("PBREffect.fxb");
} }
return pbrEffect; return pbrEffect;
} }
@ -46,21 +105,131 @@ namespace Kav
{ {
if (simpleDepthEffect == null) if (simpleDepthEffect == null)
{ {
simpleDepthEffect = GetResource("SimpleDepthEffect"); simpleDepthEffect = GetResource("SimpleDepthEffect.fxb");
} }
return simpleDepthEffect; return simpleDepthEffect;
} }
} }
public static byte[] SimpleDepthEffectInstanced
{
get
{
if (simpleDepthEffectInstanced == null)
{
simpleDepthEffectInstanced = GetResource("SimpleDepthEffectInstanced.fxb");
}
return simpleDepthEffectInstanced;
}
}
public static byte[] LinearDepthEffect
{
get
{
if (linearDepthEffect == null)
{
linearDepthEffect = GetResource("LinearDepthEffect.fxb");
}
return linearDepthEffect;
}
}
public static byte[] LinearDepthEffectInstanced
{
get
{
if (linearDepthEffectInstanced == null)
{
linearDepthEffectInstanced = GetResource("LinearDepthEffectInstanced.fxb");
}
return linearDepthEffectInstanced;
}
}
public static byte[] SkyboxEffect
{
get
{
if (skyboxEffect == null)
{
skyboxEffect = GetResource("SkyboxEffect.fxb");
}
return skyboxEffect;
}
}
public static byte[] DiffuseLitSpriteEffect
{
get
{
if (diffuseLitSpriteEffect == null)
{
diffuseLitSpriteEffect = GetResource("DiffuseLitSpriteEffect.fxb");
}
return diffuseLitSpriteEffect;
}
}
public static byte[] PaletteCrushEffect
{
get
{
if (paletteCrushEffect == null)
{
paletteCrushEffect = GetResource("PaletteCrushEffect.fxb");
}
return paletteCrushEffect;
}
}
public static byte[] UnitCubeModel
{
get
{
if (unitCubeModel == null)
{
unitCubeModel = GetResource("UnitCube.glb");
}
return unitCubeModel;
}
}
public static byte[] UnitSphereModel
{
get
{
if (unitSphereModel == null)
{
unitSphereModel = GetResource("UnitSphere.glb");
}
return unitSphereModel;
}
}
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[] deferredToonEffect;
private static byte[] deferredPBREffect; private static byte[] deferredPBREffect;
private static byte[] pbrEffect; private static byte[] pbrEffect;
private static byte[] simpleDepthEffect; private static byte[] simpleDepthEffect;
private static byte[] simpleDepthEffectInstanced;
private static byte[] linearDepthEffect;
private static byte[] linearDepthEffectInstanced;
private static byte[] skyboxEffect;
private static byte[] diffuseLitSpriteEffect;
private static byte[] paletteCrushEffect;
private static byte[] unitCubeModel;
private static byte[] unitSphereModel;
private static byte[] GetResource(string name) private static byte[] GetResource(string name)
{ {
Stream stream = typeof(Resources).Assembly.GetManifestResourceStream( Stream stream = typeof(Resources).Assembly.GetManifestResourceStream(
"Kav.Resources." + name + ".fxb" "Kav.Resources." + name
); );
using (MemoryStream ms = new MemoryStream()) using (MemoryStream ms = new MemoryStream())
{ {

18
Utils/QuaternionUtils.cs Normal file
View File

@ -0,0 +1,18 @@
using Microsoft.Xna.Framework;
namespace Kav.Utils
{
public static class QuaternionUtils
{
// assumes that the input vectors are normalized and orthogonal
public static Quaternion LookAt(in Vector3 forward, in Vector3 up)
{
Matrix orientation = Matrix.Identity;
orientation.Forward = forward;
orientation.Right = Vector3.Normalize(Vector3.Cross(forward, up));
orientation.Up = Vector3.Cross(orientation.Right, forward);
return Quaternion.CreateFromRotationMatrix(orientation);
}
}
}

18
VertexDeclarations.cs Normal file
View File

@ -0,0 +1,18 @@
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public static class VertexDeclarations
{
public static VertexDeclaration PositionInstanceDeclaration = new VertexDeclaration
(
new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.TextureCoordinate, 2)
);
public static VertexDeclaration PositionTextureOffsetInstanceDeclaration = new VertexDeclaration
(
new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.TextureCoordinate, 2),
new VertexElement(12, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 5)
);
}
}

View File

@ -0,0 +1,31 @@
using System.Runtime.InteropServices;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct PositionTextureOffsetInstanceVertex : IVertexType
{
VertexDeclaration IVertexType.VertexDeclaration
{
get
{
return VertexDeclarations.PositionTextureOffsetInstanceDeclaration;
}
}
public Vector3 Translation { get; set; }
public Vector2 UVOffset { get; set; }
public static readonly VertexDeclaration VertexDeclaration;
public PositionTextureOffsetInstanceVertex(
Vector3 translation,
Vector2 uvOffset
) {
Translation = translation;
UVOffset = uvOffset;
}
}
}