440 lines
14 KiB
C#
440 lines
14 KiB
C#
using Microsoft.Xna.Framework;
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
|
|
namespace Kav
|
|
{
|
|
public class PointLightCollection
|
|
{
|
|
private readonly Vector3[] positions = new Vector3[4];
|
|
private readonly Vector3[] colors = new Vector3[4];
|
|
private readonly float[] intensities = new float[4];
|
|
|
|
readonly EffectParameter lightPositionsParam;
|
|
readonly EffectParameter lightColorsParam;
|
|
|
|
public PointLightCollection(EffectParameter lightPositionsParam, EffectParameter lightColorsParam)
|
|
{
|
|
this.lightPositionsParam = lightPositionsParam;
|
|
this.lightColorsParam = lightColorsParam;
|
|
}
|
|
|
|
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
|
|
{
|
|
positions[i] = value.Position;
|
|
colors[i] = value.Color.ToVector3() * value.Intensity;
|
|
intensities[i] = value.Intensity;
|
|
lightPositionsParam.SetValue(positions);
|
|
lightColorsParam.SetValue(colors);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class DirectionalLightCollection
|
|
{
|
|
private readonly Vector3[] directions = new Vector3[4];
|
|
private readonly Vector3[] colors = new Vector3[4];
|
|
private readonly float[] intensities = new float[4];
|
|
|
|
readonly EffectParameter lightPositionsParam;
|
|
readonly EffectParameter lightColorsParam;
|
|
|
|
public DirectionalLightCollection(EffectParameter lightPositionsParam, EffectParameter lightColorsParam)
|
|
{
|
|
this.lightPositionsParam = lightPositionsParam;
|
|
this.lightColorsParam = lightColorsParam;
|
|
}
|
|
|
|
public DirectionalLight this[int i]
|
|
{
|
|
get
|
|
{
|
|
var color = colors[i] / intensities[i];
|
|
return new DirectionalLight(
|
|
directions[i],
|
|
new Color(
|
|
color.X,
|
|
color.Y,
|
|
color.Z,
|
|
1f
|
|
),
|
|
intensities[i]
|
|
);
|
|
}
|
|
set
|
|
{
|
|
directions[i] = value.Direction;
|
|
colors[i] = value.Color.ToVector3() * value.Intensity;
|
|
intensities[i] = value.Intensity;
|
|
lightPositionsParam.SetValue(directions);
|
|
lightColorsParam.SetValue(colors);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class PBREffect : Effect, TransformEffect, PointLightEffect, DirectionalLightEffect
|
|
{
|
|
EffectParameter worldParam;
|
|
EffectParameter worldViewProjectionParam;
|
|
EffectParameter worldInverseTransposeParam;
|
|
|
|
EffectParameter albedoTextureParam;
|
|
EffectParameter normalTextureParam;
|
|
EffectParameter emissionTextureParam;
|
|
EffectParameter occlusionTextureParam;
|
|
EffectParameter metallicRoughnessTextureParam;
|
|
EffectParameter envDiffuseTextureParam;
|
|
EffectParameter brdfLutTextureParam;
|
|
EffectParameter envSpecularTextureParam;
|
|
|
|
EffectParameter albedoParam;
|
|
EffectParameter metallicParam;
|
|
EffectParameter roughnessParam;
|
|
EffectParameter aoParam;
|
|
|
|
EffectParameter eyePositionParam;
|
|
|
|
EffectParameter shaderIndexParam;
|
|
|
|
Matrix world = Matrix.Identity;
|
|
Matrix view = Matrix.Identity;
|
|
Matrix projection = Matrix.Identity;
|
|
PointLightCollection pointLightCollection;
|
|
DirectionalLightCollection directionalLightCollection;
|
|
|
|
Vector3 albedo;
|
|
float metallic;
|
|
float roughness;
|
|
float ao;
|
|
|
|
bool albedoTextureEnabled = false;
|
|
bool metallicRoughnessMapEnabled = false;
|
|
bool normalMapEnabled = false;
|
|
|
|
EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
|
|
|
|
// FIXME: lazily set properties for performance
|
|
|
|
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; } = 4;
|
|
|
|
public PointLightCollection PointLights
|
|
{
|
|
get { return pointLightCollection; }
|
|
private set { pointLightCollection = value; }
|
|
}
|
|
|
|
public int MaxDirectionalLights { get; } = 4;
|
|
|
|
public DirectionalLightCollection DirectionalLights
|
|
{
|
|
get { return directionalLightCollection; }
|
|
private set { directionalLightCollection = value; }
|
|
}
|
|
|
|
public Vector3 Albedo
|
|
{
|
|
get { return albedo; }
|
|
set
|
|
{
|
|
albedo = value;
|
|
albedoParam.SetValue(albedo);
|
|
}
|
|
}
|
|
|
|
public float Metallic
|
|
{
|
|
get { return metallic; }
|
|
set
|
|
{
|
|
metallic = value;
|
|
metallicParam.SetValue(metallic);
|
|
}
|
|
}
|
|
|
|
public float Roughness
|
|
{
|
|
get { return roughness; }
|
|
set
|
|
{
|
|
roughness = value;
|
|
roughnessParam.SetValue(roughness);
|
|
}
|
|
}
|
|
|
|
public float AO
|
|
{
|
|
get { return ao; }
|
|
set
|
|
{
|
|
ao = value;
|
|
aoParam.SetValue(ao);
|
|
}
|
|
}
|
|
|
|
public Texture2D AlbedoTexture
|
|
{
|
|
get { return albedoTextureParam.GetValueTexture2D(); }
|
|
set
|
|
{
|
|
albedoTextureParam.SetValue(value);
|
|
albedoTextureEnabled = value != null;
|
|
dirtyFlags |= EffectDirtyFlags.ShaderIndex;
|
|
}
|
|
}
|
|
|
|
public Texture2D NormalTexture
|
|
{
|
|
get { return normalTextureParam.GetValueTexture2D(); }
|
|
set
|
|
{
|
|
normalTextureParam.SetValue(value);
|
|
normalMapEnabled = value != null;
|
|
dirtyFlags |= EffectDirtyFlags.ShaderIndex;
|
|
}
|
|
}
|
|
|
|
public Texture2D EmissionTexture
|
|
{
|
|
get { return emissionTextureParam.GetValueTexture2D(); }
|
|
set { emissionTextureParam.SetValue(value); }
|
|
}
|
|
|
|
public Texture2D OcclusionTexture
|
|
{
|
|
get { return occlusionTextureParam.GetValueTexture2D(); }
|
|
set { occlusionTextureParam.SetValue(value); }
|
|
}
|
|
|
|
public Texture2D MetallicRoughnessTexture
|
|
{
|
|
get { return metallicRoughnessTextureParam.GetValueTexture2D(); }
|
|
set
|
|
{
|
|
metallicRoughnessTextureParam.SetValue(value);
|
|
metallicRoughnessMapEnabled = value != null;
|
|
dirtyFlags |= EffectDirtyFlags.ShaderIndex;
|
|
}
|
|
}
|
|
|
|
public TextureCube EnvDiffuseTexture
|
|
{
|
|
get { return envDiffuseTextureParam.GetValueTextureCube(); }
|
|
set { envDiffuseTextureParam.SetValue(value); }
|
|
}
|
|
|
|
public Texture2D BRDFLutTexture
|
|
{
|
|
get { return brdfLutTextureParam.GetValueTexture2D(); }
|
|
set { brdfLutTextureParam.SetValue(value); }
|
|
}
|
|
|
|
public TextureCube EnvSpecularTexture
|
|
{
|
|
get { return envSpecularTextureParam.GetValueTextureCube(); }
|
|
set { envSpecularTextureParam.SetValue(value); }
|
|
}
|
|
|
|
public PBREffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.PBREffect)
|
|
{
|
|
CacheEffectParameters();
|
|
|
|
pointLightCollection = new PointLightCollection(
|
|
Parameters["LightPositions"],
|
|
Parameters["PositionLightColors"]
|
|
);
|
|
|
|
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"]
|
|
);
|
|
|
|
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()
|
|
{
|
|
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.EyePosition) != 0)
|
|
{
|
|
Matrix.Invert(ref view, out Matrix inverseView);
|
|
eyePositionParam.SetValue(inverseView.Translation);
|
|
|
|
dirtyFlags &= ~EffectDirtyFlags.EyePosition;
|
|
}
|
|
|
|
if ((dirtyFlags & EffectDirtyFlags.ShaderIndex) != 0)
|
|
{
|
|
int shaderIndex = 0;
|
|
|
|
if (albedoTextureEnabled && metallicRoughnessMapEnabled && normalMapEnabled)
|
|
{
|
|
shaderIndex = 7;
|
|
}
|
|
else if (metallicRoughnessMapEnabled && normalMapEnabled)
|
|
{
|
|
shaderIndex = 6;
|
|
}
|
|
else if (albedoTextureEnabled && normalMapEnabled)
|
|
{
|
|
shaderIndex = 5;
|
|
}
|
|
else if (albedoTextureEnabled && metallicRoughnessMapEnabled)
|
|
{
|
|
shaderIndex = 4;
|
|
}
|
|
else if (normalMapEnabled)
|
|
{
|
|
shaderIndex = 3;
|
|
}
|
|
else if (metallicRoughnessMapEnabled)
|
|
{
|
|
shaderIndex = 2;
|
|
}
|
|
else if (albedoTextureEnabled)
|
|
{
|
|
shaderIndex = 1;
|
|
}
|
|
|
|
shaderIndexParam.SetValue(shaderIndex);
|
|
|
|
dirtyFlags &= ~EffectDirtyFlags.ShaderIndex;
|
|
}
|
|
}
|
|
|
|
void CacheEffectParameters()
|
|
{
|
|
worldParam = Parameters["World"];
|
|
worldViewProjectionParam = Parameters["WorldViewProjection"];
|
|
worldInverseTransposeParam = Parameters["WorldInverseTranspose"];
|
|
|
|
albedoTextureParam = Parameters["AlbedoTexture"];
|
|
normalTextureParam = Parameters["NormalTexture"];
|
|
emissionTextureParam = Parameters["EmissionTexture"];
|
|
occlusionTextureParam = Parameters["OcclusionTexture"];
|
|
metallicRoughnessTextureParam = Parameters["MetallicRoughnessTexture"];
|
|
envDiffuseTextureParam = Parameters["EnvDiffuseTexture"];
|
|
brdfLutTextureParam = Parameters["BrdfLutTexture"];
|
|
envSpecularTextureParam = Parameters["EnvSpecularTexture"];
|
|
|
|
albedoParam = Parameters["AlbedoValue"];
|
|
metallicParam = Parameters["MetallicValue"];
|
|
roughnessParam = Parameters["RoughnessValue"];
|
|
aoParam = Parameters["AO"];
|
|
|
|
eyePositionParam = Parameters["EyePosition"];
|
|
|
|
shaderIndexParam = Parameters["ShaderIndex"];
|
|
}
|
|
}
|
|
}
|