public void AddShadowMap(ShadowMap shadowMap) { shadowMaps.Add(shadowMap); shadowMap.Passes = new RenderPass[shadowMap.LevelCount]; shadowMap.Plugins = new RenderTargetsPlugin[shadowMap.LevelCount]; for (int i = 0; i < shadowMap.Passes.Length; i++) { shadowMap.Passes[i] = new RenderPass(string.Format("Pass {0}", i)) { Parameters = new ParameterCollection(string.Format("Parameters ShadowMap {0}", i)) }; shadowMap.Passes[i].Parameters.AddSources(MainPlugin.ViewParameters); shadowMap.Passes[i].Parameters.AddSources(shadowMap.CasterParameters); unsafe { int currentPassIndex = i; shadowMap.Passes[i].Parameters.AddDynamic(TransformationKeys.ViewProjection, ParameterDynamicValue.New(LightingPlugin.ViewProjectionArray, (ref ShadowMapData input, ref Matrix output) => { fixed (Matrix* vpPtr = &input.ViewProjCaster0) { output = vpPtr[currentPassIndex]; } })); shadowMap.Passes[i].Parameters.AddDynamic(LightingPlugin.ShadowLightOffset, ParameterDynamicValue.New(LightingPlugin.ViewProjectionArray, (ref ShadowMapData input, ref Vector3 output) => { fixed (Vector3* vpPtr = &input.Offset0) { output = vpPtr[currentPassIndex]; } })); } shadowMap.Plugins[i] = new RenderTargetsPlugin { Services = Services, EnableClearTarget = false, EnableClearDepth = false, RenderPass = shadowMap.Passes[i], RenderTarget = null, }; shadowMap.Plugins[i].Apply(); } RenderPass.Passes.InsertRange(0, shadowMap.Passes); // Dynamic value used for ViewProjectionArray key var dynamicViewProjectionArray = ParameterDynamicValue.New( TransformationKeys.View, TransformationKeys.Projection, LightKeys.LightDirection, LightKeys.LightColor, LightingPlugin.Offsets, (ref Matrix view, ref Matrix projection, ref Vector3 lightDirection, ref Color3 lightColor, ref Vector3[] offsets, ref ShadowMapData result) => { if (projections == null) { // Preallocates temporary variables (thread static) projections = new Matrix[4]; views = new Matrix[4]; shadowsViewProj = new Matrix[8]; points = new Vector3[8]; directions = new Vector3[4]; } Matrix inverseView, inverseProjection; Matrix.Invert(ref projection, out inverseProjection); Matrix.Invert(ref view, out inverseView); // Frustum in World Space for (int i = 0; i < 8; ++i) Vector3.TransformCoordinate(ref FrustrumBasePoints[i], ref inverseProjection, out points[i]); for (int i = 0; i < 4; i++) { directions[i] = Vector3.Normalize(points[i + 4] - points[i]); } // TODO Make these factors configurable. They need also to be correctly tweaked. float shadowDistribute = 1.0f / shadowMap.LevelCount; float znear = 1.0f; float zfar = shadowMap.ShadowDistance; var shadowOffsets = new Vector3[shadowMap.LevelCount]; var boudingBoxVectors = new Vector3[shadowMap.LevelCount * 2]; var direction = Vector3.Normalize(lightDirection); // Fake value // It will be setup by next loop Vector3 side = Vector3.UnitX; Vector3 up = Vector3.UnitX; // Select best Up vector foreach (var vectorUp in VectorUps) { if (Vector3.Dot(direction, vectorUp) < (1.0 - 0.0001)) { side = Vector3.Normalize(Vector3.Cross(vectorUp, direction)); up = Vector3.Normalize(Vector3.Cross(direction, side)); break; } } for (int cascadeLevel = 0; cascadeLevel < shadowMap.LevelCount; ++cascadeLevel) { float k0 = (float)(cascadeLevel + 0) / shadowMap.LevelCount; float k1 = (float)(cascadeLevel + 1) / shadowMap.LevelCount; float min = (float)(znear * Math.Pow(zfar / znear, k0)) * (1.0f - shadowDistribute) + (znear + (zfar - znear) * k0) * shadowDistribute; float max = (float)(znear * Math.Pow(zfar / znear, k1)) * (1.0f - shadowDistribute) + (znear + (zfar - znear) * k1) * shadowDistribute; for (int j = 0; j < shadowMap.LevelCount; j++) { boudingBoxVectors[j] = points[j] + directions[j] * min; boudingBoxVectors[j + shadowMap.LevelCount] = points[j] + directions[j] * max; } var boundingBox = BoundingBox.FromPoints(boudingBoxVectors); var radius = (boundingBox.Maximum - boundingBox.Minimum).Length() * 0.5f; var target = Vector3.TransformCoordinate(boundingBox.Center, inverseView); // Snap camera to texel units (so that shadow doesn't jitter) var shadowMapHalfSize = shadowMap.ShadowMapSize * 0.5f; float x = (float)Math.Ceiling(Vector3.Dot(target, up) * shadowMapHalfSize / radius) * radius / shadowMapHalfSize; float y = (float)Math.Ceiling(Vector3.Dot(target, side) * shadowMapHalfSize / radius) * radius / shadowMapHalfSize; float z = Vector3.Dot(target, direction); //target = up * x + side * y + direction * R32G32B32_Float.Dot(target, direction); target = up * x + side * y + direction * z; views[cascadeLevel] = Matrix.LookAtLH(target - direction * zfar * 0.5f, target + direction * zfar * 0.5f, up); // View; projections[cascadeLevel] = Matrix.OrthoOffCenterLH(-radius, radius, -radius, radius, znear / zfar, zfar); // Projection //float leftX = shadowMap.Level == CascadeShadowMapLevel.X1 ? 0.5f : 0.25f; //float leftY = shadowMap.Level == CascadeShadowMapLevel.X4 ? 0.25f : 0.5f; //float centerX = 0.5f * (cascadeLevel % 2) + leftX; //float centerY = 0.5f * (cascadeLevel / 2) + leftY; var cascadeTextureCoords = shadowMap.TextureCoordsBorder[cascadeLevel]; float leftX = (float)shadowMap.ShadowMapSize / (float)AtlasSize * 0.5f; float leftY = (float)shadowMap.ShadowMapSize / (float)AtlasSize * 0.5f; float centerX = 0.5f * (cascadeTextureCoords.X + cascadeTextureCoords.Z); float centerY = 0.5f * (cascadeTextureCoords.Y + cascadeTextureCoords.W); shadowsViewProj[cascadeLevel] = views[cascadeLevel] * projections[cascadeLevel]; shadowsViewProj[cascadeLevel + 4] = shadowsViewProj[cascadeLevel] * Matrix.Scaling(leftX, -leftY, 0.5f) // Texture0 mapping offsets/scaling * Matrix.Translation(centerX, centerY, 0.5f); var shadowVInverse = Matrix.Invert(views[cascadeLevel]); shadowOffsets[cascadeLevel] = new Vector3(shadowVInverse.M41, shadowVInverse.M42, shadowVInverse.M43); } result.LightColor = lightColor; unsafe { fixed (Matrix* resultPtr = &result.ViewProjCaster0) Utilities.Write((IntPtr)resultPtr, shadowsViewProj, 0, shadowsViewProj.Length); fixed (Vector3* resultPtr = &result.Offset0) Utilities.Write((IntPtr)resultPtr, shadowOffsets, 0, shadowOffsets.Length); } }); shadowMap.Parameters.SetDefault(LightKeys.LightDirection); shadowMap.Parameters.SetDefault(Offsets); shadowMap.Parameters.SetDefault(ViewProjectionArray); shadowMap.Parameters.AddDynamic(ViewProjectionArray, dynamicViewProjectionArray); shadowMap.CasterParameters.Set(EffectPlugin.RasterizerStateKey, null); shadowMap.Texture = shadowMap.Filter is ShadowMapFilterVsm ? ShadowMapVsm : ShadowMapDepth.Texture; }
public ShadowMapFilterDefault(ShadowMap shadowMap) : base(shadowMap) { }
public void RemoveShadowMap(ShadowMap shadowMap) { shadowMaps.Remove(shadowMap); RenderPass.Passes.RemoveAll(shadowMap.Passes.Contains); }
public void AddShadowMap(ShadowMap shadowMap) { shadowMaps.Add(shadowMap); shadowMap.Passes = new RenderPass[shadowMap.LevelCount]; shadowMap.Plugins = new RenderTargetsPlugin[shadowMap.LevelCount]; for (int i = 0; i < shadowMap.Passes.Length; i++) { shadowMap.Passes[i] = new RenderPass(string.Format("Pass {0}", i)) { Parameters = new ParameterCollection(string.Format("Parameters ShadowMap {0}", i)) }; shadowMap.Passes[i].Parameters.AddSources(MainPlugin.ViewParameters); shadowMap.Passes[i].Parameters.AddSources(shadowMap.CasterParameters); unsafe { int currentPassIndex = i; shadowMap.Passes[i].Parameters.AddDynamic(TransformationKeys.ViewProjection, ParameterDynamicValue.New(LightingPlugin.ViewProjectionArray, (ref ShadowMapData input, ref Matrix output) => { fixed(Matrix * vpPtr = &input.ViewProjCaster0) { output = vpPtr[currentPassIndex]; } })); shadowMap.Passes[i].Parameters.AddDynamic(LightingPlugin.ShadowLightOffset, ParameterDynamicValue.New(LightingPlugin.ViewProjectionArray, (ref ShadowMapData input, ref Vector3 output) => { fixed(Vector3 * vpPtr = &input.Offset0) { output = vpPtr[currentPassIndex]; } })); } shadowMap.Plugins[i] = new RenderTargetsPlugin { Services = Services, EnableClearTarget = false, EnableClearDepth = false, RenderPass = shadowMap.Passes[i], RenderTarget = null, }; shadowMap.Plugins[i].Apply(); } RenderPass.Passes.InsertRange(0, shadowMap.Passes); // Dynamic value used for ViewProjectionArray key var dynamicViewProjectionArray = ParameterDynamicValue.New( TransformationKeys.View, TransformationKeys.Projection, LightKeys.LightDirection, LightKeys.LightColor, LightingPlugin.Offsets, (ref Matrix view, ref Matrix projection, ref Vector3 lightDirection, ref Color3 lightColor, ref Vector3[] offsets, ref ShadowMapData result) => { if (projections == null) { // Preallocates temporary variables (thread static) projections = new Matrix[4]; views = new Matrix[4]; shadowsViewProj = new Matrix[8]; points = new Vector3[8]; directions = new Vector3[4]; } Matrix inverseView, inverseProjection; Matrix.Invert(ref projection, out inverseProjection); Matrix.Invert(ref view, out inverseView); // Frustum in World Space for (int i = 0; i < 8; ++i) { Vector3.TransformCoordinate(ref FrustrumBasePoints[i], ref inverseProjection, out points[i]); } for (int i = 0; i < 4; i++) { directions[i] = Vector3.Normalize(points[i + 4] - points[i]); } // TODO Make these factors configurable. They need also to be correctly tweaked. float shadowDistribute = 1.0f / shadowMap.LevelCount; float znear = 1.0f; float zfar = shadowMap.ShadowDistance; var shadowOffsets = new Vector3[shadowMap.LevelCount]; var boudingBoxVectors = new Vector3[shadowMap.LevelCount * 2]; var direction = Vector3.Normalize(lightDirection); // Fake value // It will be setup by next loop Vector3 side = Vector3.UnitX; Vector3 up = Vector3.UnitX; // Select best Up vector foreach (var vectorUp in VectorUps) { if (Vector3.Dot(direction, vectorUp) < (1.0 - 0.0001)) { side = Vector3.Normalize(Vector3.Cross(vectorUp, direction)); up = Vector3.Normalize(Vector3.Cross(direction, side)); break; } } for (int cascadeLevel = 0; cascadeLevel < shadowMap.LevelCount; ++cascadeLevel) { float k0 = (float)(cascadeLevel + 0) / shadowMap.LevelCount; float k1 = (float)(cascadeLevel + 1) / shadowMap.LevelCount; float min = (float)(znear * Math.Pow(zfar / znear, k0)) * (1.0f - shadowDistribute) + (znear + (zfar - znear) * k0) * shadowDistribute; float max = (float)(znear * Math.Pow(zfar / znear, k1)) * (1.0f - shadowDistribute) + (znear + (zfar - znear) * k1) * shadowDistribute; for (int j = 0; j < shadowMap.LevelCount; j++) { boudingBoxVectors[j] = points[j] + directions[j] * min; boudingBoxVectors[j + shadowMap.LevelCount] = points[j] + directions[j] * max; } var boundingBox = BoundingBox.FromPoints(boudingBoxVectors); var radius = (boundingBox.Maximum - boundingBox.Minimum).Length() * 0.5f; var target = Vector3.TransformCoordinate(boundingBox.Center, inverseView); // Snap camera to texel units (so that shadow doesn't jitter) var shadowMapHalfSize = shadowMap.ShadowMapSize * 0.5f; float x = (float)Math.Ceiling(Vector3.Dot(target, up) * shadowMapHalfSize / radius) * radius / shadowMapHalfSize; float y = (float)Math.Ceiling(Vector3.Dot(target, side) * shadowMapHalfSize / radius) * radius / shadowMapHalfSize; float z = Vector3.Dot(target, direction); //target = up * x + side * y + direction * R32G32B32_Float.Dot(target, direction); target = up * x + side * y + direction * z; views[cascadeLevel] = Matrix.LookAtLH(target - direction * zfar * 0.5f, target + direction * zfar * 0.5f, up); // View; projections[cascadeLevel] = Matrix.OrthoOffCenterLH(-radius, radius, -radius, radius, znear / zfar, zfar); // Projection //float leftX = shadowMap.Level == CascadeShadowMapLevel.X1 ? 0.5f : 0.25f; //float leftY = shadowMap.Level == CascadeShadowMapLevel.X4 ? 0.25f : 0.5f; //float centerX = 0.5f * (cascadeLevel % 2) + leftX; //float centerY = 0.5f * (cascadeLevel / 2) + leftY; var cascadeTextureCoords = shadowMap.TextureCoordsBorder[cascadeLevel]; float leftX = (float)shadowMap.ShadowMapSize / (float)AtlasSize * 0.5f; float leftY = (float)shadowMap.ShadowMapSize / (float)AtlasSize * 0.5f; float centerX = 0.5f * (cascadeTextureCoords.X + cascadeTextureCoords.Z); float centerY = 0.5f * (cascadeTextureCoords.Y + cascadeTextureCoords.W); shadowsViewProj[cascadeLevel] = views[cascadeLevel] * projections[cascadeLevel]; shadowsViewProj[cascadeLevel + 4] = shadowsViewProj[cascadeLevel] * Matrix.Scaling(leftX, -leftY, 0.5f) // Texture0 mapping offsets/scaling * Matrix.Translation(centerX, centerY, 0.5f); var shadowVInverse = Matrix.Invert(views[cascadeLevel]); shadowOffsets[cascadeLevel] = new Vector3(shadowVInverse.M41, shadowVInverse.M42, shadowVInverse.M43); } result.LightColor = lightColor; unsafe { fixed(Matrix * resultPtr = &result.ViewProjCaster0) Utilities.Write((IntPtr)resultPtr, shadowsViewProj, 0, shadowsViewProj.Length); fixed(Vector3 * resultPtr = &result.Offset0) Utilities.Write((IntPtr)resultPtr, shadowOffsets, 0, shadowOffsets.Length); } }); shadowMap.Parameters.SetDefault(LightKeys.LightDirection); shadowMap.Parameters.SetDefault(Offsets); shadowMap.Parameters.SetDefault(ViewProjectionArray); shadowMap.Parameters.AddDynamic(ViewProjectionArray, dynamicViewProjectionArray); shadowMap.CasterParameters.Set(EffectPlugin.RasterizerStateKey, null); shadowMap.Texture = shadowMap.Filter is ShadowMapFilterVsm ? ShadowMapVsm : ShadowMapDepth.Texture; }
protected ShadowMapFilter(ShadowMap shadowMap) { this.shadowMap = shadowMap; }
public ShadowMapFilterVsm(ShadowMap shadowMap) : base(shadowMap) { MinVariance = 0.0000001f; BleedingFactor = 0.38f; }