Kav/Effects/PBREffect.cs

440 lines
14 KiB
C#
Raw Normal View History

2020-08-05 03:50:44 +00:00
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
2020-08-05 19:15:22 +00:00
public class PointLightCollection
2020-08-05 03:50:44 +00:00
{
private readonly Vector3[] positions = new Vector3[4];
private readonly Vector3[] colors = new Vector3[4];
2020-08-05 22:09:48 +00:00
private readonly float[] intensities = new float[4];
2020-08-05 03:50:44 +00:00
readonly EffectParameter lightPositionsParam;
readonly EffectParameter lightColorsParam;
2020-08-05 19:15:22 +00:00
public PointLightCollection(EffectParameter lightPositionsParam, EffectParameter lightColorsParam)
2020-08-05 03:50:44 +00:00
{
this.lightPositionsParam = lightPositionsParam;
this.lightColorsParam = lightColorsParam;
}
2020-08-05 19:15:22 +00:00
public PointLight this[int i]
2020-08-05 03:50:44 +00:00
{
2020-08-05 19:15:22 +00:00
get
{
2020-08-05 22:09:48 +00:00
var color = colors[i] / intensities[i];
2020-08-05 19:15:22 +00:00
return new PointLight(
positions[i],
new Color(
2020-08-05 22:09:48 +00:00
color.X,
color.Y,
color.Z,
2020-08-05 19:15:22 +00:00
1f
2020-08-05 22:09:48 +00:00
),
intensities[i]
2020-08-05 19:15:22 +00:00
);
}
2020-08-05 03:50:44 +00:00
set
{
2020-08-05 19:15:22 +00:00
positions[i] = value.Position;
colors[i] = value.Color.ToVector3() * value.Intensity;
2020-08-05 22:09:48 +00:00
intensities[i] = value.Intensity;
2020-08-05 03:50:44 +00:00
lightPositionsParam.SetValue(positions);
lightColorsParam.SetValue(colors);
}
}
}
2020-08-07 08:12:46 +00:00
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
2020-08-05 03:50:44 +00:00
{
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;
2020-08-05 19:15:22 +00:00
PointLightCollection pointLightCollection;
2020-08-07 08:12:46 +00:00
DirectionalLightCollection directionalLightCollection;
2020-08-05 03:50:44 +00:00
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;
2020-08-05 19:15:22 +00:00
public PointLightCollection PointLights
2020-08-05 03:50:44 +00:00
{
2020-08-05 19:15:22 +00:00
get { return pointLightCollection; }
2020-08-07 08:12:46 +00:00
private set { pointLightCollection = value; }
}
public int MaxDirectionalLights { get; } = 4;
public DirectionalLightCollection DirectionalLights
{
get { return directionalLightCollection; }
private set { directionalLightCollection = value; }
2020-08-05 03:50:44 +00:00
}
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();
2020-08-05 19:15:22 +00:00
pointLightCollection = new PointLightCollection(
2020-08-05 03:50:44 +00:00
Parameters["LightPositions"],
2020-08-07 08:12:46 +00:00
Parameters["PositionLightColors"]
);
directionalLightCollection = new DirectionalLightCollection(
Parameters["LightDirections"],
Parameters["DirectionLightColors"]
2020-08-05 03:50:44 +00:00
);
}
protected PBREffect(PBREffect cloneSource) : base(cloneSource)
{
CacheEffectParameters();
World = cloneSource.World;
View = cloneSource.View;
Projection = cloneSource.Projection;
2020-08-05 19:15:22 +00:00
PointLights = new PointLightCollection(
2020-08-05 03:50:44 +00:00
Parameters["LightPositions"],
2020-08-07 08:12:46 +00:00
Parameters["PositionLightColors"]
2020-08-05 03:50:44 +00:00
);
2020-08-07 08:12:46 +00:00
for (int i = 0; i < MaxPointLights; i++)
2020-08-05 03:50:44 +00:00
{
2020-08-05 19:15:22 +00:00
PointLights[i] = cloneSource.PointLights[i];
2020-08-05 03:50:44 +00:00
}
2020-08-07 08:12:46 +00:00
DirectionalLights = new DirectionalLightCollection(
Parameters["LightDirections"],
Parameters["DirectionLightColors"]
);
for (int i = 0; i < MaxDirectionalLights; i++)
{
DirectionalLights[i] = cloneSource.DirectionalLights[i];
}
2020-08-05 03:50:44 +00:00
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"];
}
}
}