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
94 changed files with 5173 additions and 604 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,15 +10,47 @@ namespace Kav
EffectParameter gNormalParam; EffectParameter gNormalParam;
EffectParameter gMetallicRoughnessParam; EffectParameter gMetallicRoughnessParam;
EffectParameter shadowMapOneParam;
EffectParameter shadowMapTwoParam;
EffectParameter shadowMapThreeParam;
EffectParameter shadowMapFourParam;
EffectParameter lightSpaceMatrixOneParam;
EffectParameter lightSpaceMatrixTwoParam;
EffectParameter lightSpaceMatrixThreeParam;
EffectParameter lightSpaceMatrixFourParam;
EffectParameter viewMatrixParam;
EffectParameter cascadeFarPlanesParam;
EffectParameter directionalLightColorParam;
EffectParameter directionalLightDirectionParam;
EffectParameter eyePositionParam; EffectParameter eyePositionParam;
PointLightCollection pointLightCollection; PointLightCollection pointLightCollection;
DirectionalLightCollection directionalLightCollection;
public Texture2D GPosition { get; set; } public Texture2D GPosition { get; set; }
public Texture2D GAlbedo { get; set; } public Texture2D GAlbedo { get; set; }
public Texture2D GNormal { get; set; } public Texture2D GNormal { get; set; }
public Texture2D GMetallicRoughness { get; set; } public Texture2D GMetallicRoughness { get; set; }
public Texture2D ShadowMapOne { get; set; }
public Texture2D ShadowMapTwo { get; set; }
public Texture2D ShadowMapThree { get; set; }
public Texture2D ShadowMapFour { get; set; }
public Matrix LightSpaceMatrixOne { get; set; }
public Matrix LightSpaceMatrixTwo { get; set; }
public Matrix LightSpaceMatrixThree { get; set; }
public Matrix LightSpaceMatrixFour { get; set; }
public Matrix ViewMatrix { get; set; }
public readonly float[] CascadeFarPlanes;
public Vector3 DirectionalLightColor { get; set; }
public Vector3 DirectionalLightDirection { get; set; }
public Vector3 EyePosition { get; set; } public Vector3 EyePosition { get; set; }
public int MaxPointLights { get; } = 64; public int MaxPointLights { get; } = 64;
@ -29,16 +61,10 @@ namespace Kav
private set { pointLightCollection = value; } private set { pointLightCollection = value; }
} }
public int MaxDirectionalLights { get; } = 4;
public DirectionalLightCollection DirectionalLights
{
get { return directionalLightCollection; }
private set { directionalLightCollection = value; }
}
public DeferredPBREffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBREffect) public DeferredPBREffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBREffect)
{ {
CascadeFarPlanes = new float[4];
CacheEffectParameters(); CacheEffectParameters();
pointLightCollection = new PointLightCollection( pointLightCollection = new PointLightCollection(
@ -46,47 +72,6 @@ namespace Kav
Parameters["PointLightColors"], Parameters["PointLightColors"],
MaxPointLights MaxPointLights
); );
DirectionalLights = new DirectionalLightCollection(
Parameters["DirectionalLightDirections"],
Parameters["DirectionalLightColors"]
);
}
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];
}
DirectionalLights = new DirectionalLightCollection(
Parameters["DirectionalLightDirections"],
Parameters["DirectionalLightColors"]
);
for (int i = 0; i < MaxDirectionalLights; i++)
{
DirectionalLights[i] = cloneSource.DirectionalLights[i];
}
}
public override Effect Clone()
{
return new DeferredPBREffect(this);
} }
protected override void OnApply() protected override void OnApply()
@ -96,6 +81,22 @@ namespace Kav
gNormalParam.SetValue(GNormal); gNormalParam.SetValue(GNormal);
gMetallicRoughnessParam.SetValue(GMetallicRoughness); gMetallicRoughnessParam.SetValue(GMetallicRoughness);
shadowMapOneParam.SetValue(ShadowMapOne);
shadowMapTwoParam.SetValue(ShadowMapTwo);
shadowMapThreeParam.SetValue(ShadowMapThree);
shadowMapFourParam.SetValue(ShadowMapFour);
lightSpaceMatrixOneParam.SetValue(LightSpaceMatrixOne);
lightSpaceMatrixTwoParam.SetValue(LightSpaceMatrixTwo);
lightSpaceMatrixThreeParam.SetValue(LightSpaceMatrixThree);
lightSpaceMatrixFourParam.SetValue(LightSpaceMatrixFour);
viewMatrixParam.SetValue(ViewMatrix);
cascadeFarPlanesParam.SetValue(CascadeFarPlanes);
directionalLightColorParam.SetValue(DirectionalLightColor);
directionalLightDirectionParam.SetValue(DirectionalLightDirection);
eyePositionParam.SetValue(EyePosition); eyePositionParam.SetValue(EyePosition);
} }
@ -106,6 +107,22 @@ namespace Kav
gNormalParam = Parameters["gNormal"]; gNormalParam = Parameters["gNormal"];
gMetallicRoughnessParam = Parameters["gMetallicRoughness"]; gMetallicRoughnessParam = Parameters["gMetallicRoughness"];
shadowMapOneParam = Parameters["shadowMapOne"];
shadowMapTwoParam = Parameters["shadowMapTwo"];
shadowMapThreeParam = Parameters["shadowMapThree"];
shadowMapFourParam = Parameters["shadowMapFour"];
lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"];
lightSpaceMatrixTwoParam = Parameters["LightSpaceMatrixTwo"];
lightSpaceMatrixThreeParam = Parameters["LightSpaceMatrixThree"];
lightSpaceMatrixFourParam = Parameters["LightSpaceMatrixFour"];
viewMatrixParam = Parameters["ViewMatrix"];
cascadeFarPlanesParam = Parameters["CascadeFarPlanes"];
directionalLightDirectionParam = Parameters["DirectionalLightDirection"];
directionalLightColorParam = Parameters["DirectionalLightColor"];
eyePositionParam = Parameters["EyePosition"]; 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,47 +0,0 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class DirectionalLightCollection
{
private readonly Vector3[] directions = new Vector3[4];
private readonly Vector3[] colors = new Vector3[4];
private readonly float[] intensities = new float[4];
readonly EffectParameter lightDirectionsParam;
readonly EffectParameter lightColorsParam;
public DirectionalLightCollection(EffectParameter lightDirectionsParam, EffectParameter lightColorsParam)
{
this.lightDirectionsParam = lightDirectionsParam;
this.lightColorsParam = lightColorsParam;
}
public DirectionalLight this[int i]
{
get
{
var color = colors[i] / intensities[i];
return new DirectionalLight(
directions[i],
new Color(
color.X,
color.Y,
color.Z,
1f
),
intensities[i]
);
}
set
{
directions[i] = value.Direction;
colors[i] = value.Color.ToVector3() * value.Intensity;
intensities[i] = value.Intensity;
lightDirectionsParam.SetValue(directions);
lightColorsParam.SetValue(colors);
}
}
}
}

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

View File

@ -0,0 +1,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

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

View File

@ -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,22 +16,31 @@ struct VertexShaderInput
struct VertexShaderOutput struct VertexShaderOutput
{ {
float4 Position : SV_Position; float4 Position : SV_POSITION;
float Depth : TEXCOORD0;
}; };
VertexShaderOutput main_vs(VertexShaderInput input) VertexShaderOutput main_vs(VertexShaderInput input)
{ {
VertexShaderOutput output; VertexShaderOutput output;
output.Position = mul(float4(input.Position.xyz, 1.0), ModelViewProjection); float4x4 worldViewProjection = mul(World, ViewProjection);
output.Position = mul(input.Position, worldViewProjection);
output.Depth = output.Position.z / output.Position.w;
return output; return output;
} }
float4 main_ps(VertexShaderOutput input) : SV_TARGET0
{
return float4(input.Depth, 0.0, 0.0, 0.0);
}
Technique SimpleDepth Technique SimpleDepth
{ {
Pass Pass
{ {
VertexShader = compile vs_3_0 main_vs(); VertexShader = compile vs_3_0 main_vs();
PixelShader = compile ps_3_0 main_ps();
} }
} }

View File

@ -0,0 +1,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,60 +195,6 @@ namespace Kav
Parameters["PositionLightColors"], Parameters["PositionLightColors"],
MaxPointLights MaxPointLights
); );
directionalLightCollection = new DirectionalLightCollection(
Parameters["LightDirections"],
Parameters["DirectionLightColors"]
);
}
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"]
);
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()
@ -290,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;
@ -325,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,9 +40,10 @@ namespace Kav
set set
{ {
projection = value; projection = value;
dirtyFlags |= EffectDirtyFlags.WorldViewProj; dirtyFlags |= EffectDirtyFlags.ViewProj;
} }
} }
public SimpleDepthEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.SimpleDepthEffect) public SimpleDepthEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.SimpleDepthEffect)
{ {
CacheEffectParameters(); CacheEffectParameters();
@ -49,20 +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

@ -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,11 +2,13 @@
<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>
<Platforms>x64</Platforms>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -15,9 +17,24 @@
</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>
@ -27,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,12 +1,14 @@
<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>
<Platforms>x64;x86</Platforms>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -15,9 +17,24 @@
</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>
@ -27,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

@ -4,15 +4,15 @@ namespace Kav
{ {
public struct DirectionalLight public struct DirectionalLight
{ {
public Vector3 Direction { get; set; } public Vector3 Direction { get; }
public Color Color { get; set; } public Color Color { get; }
public float Intensity { get; set; } public float Intensity { get; }
public Matrix View public Matrix View
{ {
get get
{ {
return Matrix.CreateLookAt(-Direction * 100f, Vector3.Zero, Vector3.Up); return Matrix.CreateLookAt(Direction * 100f, Vector3.Zero, Vector3.Up);
} }
} }

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