internal void AddDepthPass(MyShadowmapQuery shadowmapQuery) { var worldToProjection = shadowmapQuery.ProjectionInfo.WorldToProjection; int frustumMask = AddFrustum(ref worldToProjection); MyDepthPass pass = MyObjectPoolManager.Allocate <MyDepthPass>(); pass.DebugName = MyVisibilityCuller.ToString(shadowmapQuery.QueryType); pass.ProcessingMask = frustumMask; pass.ViewProjection = shadowmapQuery.ProjectionInfo.CurrentLocalToProjection; pass.Viewport = shadowmapQuery.Viewport; pass.FrustumIndex = shadowmapQuery.Index; pass.Dsv = shadowmapQuery.DepthBuffer; bool isCascade = shadowmapQuery.QueryType == MyFrustumEnum.ShadowCascade; pass.IsCascade = isCascade; pass.DefaultRasterizer = isCascade ? MyRasterizerStateManager.CascadesRasterizerStateOld : MyRasterizerStateManager.ShadowRasterizerState; pass.PerFrame(); RenderingPasses[Size - 1] = pass; }
private void PrepareSpotlights() { MyLights.SpotlightsBvh.OverlapAllFrustum(ref MyEnvironment.ViewFrustumClippedD, MyLightRendering.VisibleSpotlights); if (MyLightRendering.VisibleSpotlights.Count == 0) { OtherShadowsTriangleCounter = 0; } MyLightRendering.VisibleSpotlights.Sort(m_spotlightCastersComparer); MyArrayHelpers.Reserve(ref MyLightRendering.Spotlights, MyLightRendering.VisibleSpotlights.Count); int index = 0; int casterIndex = 0; foreach (var id in MyLightRendering.VisibleSpotlights) { var nearPlaneDistance = 0.5f; var worldMatrix = MatrixD.CreateTranslation(MyEnvironment.CameraPosition); var viewMatrix = MatrixD.CreateLookAt(id.Position, id.Position + MyLights.Spotlights[id.Index].Direction, MyLights.Spotlights[id.Index].Up); var projectionMatrix = MatrixD.CreatePerspectiveFieldOfView((float)(Math.Acos(MyLights.Spotlights[id.Index].ApertureCos) * 2), 1.0f, nearPlaneDistance, Math.Max(id.ShadowDistance, nearPlaneDistance)); var viewProjection = viewMatrix * projectionMatrix; MatrixD worldViewProjection = worldMatrix * viewProjection; MyLightRendering.Spotlights[index].ShadowMatrix = Matrix.Transpose(worldViewProjection * MyMatrixHelpers.ClipspaceToTexture); if (id.CastsShadows && casterIndex < MAX_SPOTLIGHT_SHADOWCASTERS) { if (ShadowmapsPool.Count <= casterIndex) { ShadowmapsPool.Add(MyRwTextures.CreateShadowmap(SpotlightShadowmapSize, SpotlightShadowmapSize)); } MyLights.Lights.Data[id.Index].CastsShadowsThisFrame = true; var query = new MyShadowmapQuery { DepthBuffer = ShadowmapsPool[casterIndex].Dsv, Viewport = new MyViewport(SpotlightShadowmapSize, SpotlightShadowmapSize), QueryType = MyFrustumEnum.ShadowProjection, ProjectionInfo = new MyProjectionInfo { WorldCameraOffsetPosition = MyEnvironment.CameraPosition, WorldToProjection = viewProjection, LocalToProjection = worldViewProjection }, IgnoredEntities = MyLights.IgnoredEntitites.ContainsKey(id) ? MyLights.IgnoredEntitites[id] : null, }; m_shadowmapQueries.Add(query); ++casterIndex; } else { MyLights.Lights.Data[id.Index].CastsShadowsThisFrame = false; } MyLights.WriteSpotlightConstants(id, ref MyLightRendering.Spotlights[index]); index++; } }
static void PrepareSpotlights() { MyLights.SpotlightsBvh.OverlapAllFrustum(ref MyEnvironment.ViewFrustum, MyLightRendering.VisibleSpotlights); MyLightRendering.VisibleSpotlights.Sort(m_spotlightCastersComparer); while (MyLightRendering.VisibleSpotlights.Count > MAX_SPOTLIGHT_SHADOWCASTERS) { MyLightRendering.VisibleSpotlights.RemoveAtFast(MyLightRendering.VisibleSpotlights.Count - 1); } MyArrayHelpers.Reserve(ref MyLightRendering.Spotlights, MyLightRendering.VisibleSpotlights.Count); int index = 0; int casterIndex = 0; foreach (var id in MyLightRendering.VisibleSpotlights) { MyLights.WriteSpotlightConstants(id, ref MyLightRendering.Spotlights[index]); if (id.CastsShadows) { var query = new MyShadowmapQuery(); if (MyLights.IgnoredEntitites.ContainsKey(id)) { query.IgnoredEntities = MyLights.IgnoredEntitites[id]; } var shadowMatrix = Matrix.CreateLookAt(id.Position, id.Position + MyLights.Spotlights[id.Index].Direction, MyLights.Spotlights[id.Index].Up) * Matrix.CreatePerspectiveFieldOfView((float)(Math.Acos(MyLights.Spotlights[id.Index].ApertureCos) * 2), 1.0f, 0.5f, id.ShadowDistance); if (ShadowmapsPool.Count <= casterIndex) { ShadowmapsPool.Add(MyRwTextures.CreateShadowmap(512, 512)); } query.DepthBuffer = ShadowmapsPool[casterIndex].Dsv; query.Viewport = new MyViewport(512, 512); query.QueryType = MyFrustumEnum.ShadowProjection; query.ProjectionInfo = new MyProjectionInfo { WorldCameraOffsetPosition = MyEnvironment.CameraPosition, WorldToProjection = shadowMatrix, LocalToProjection = Matrix.CreateTranslation(MyEnvironment.CameraPosition) * shadowMatrix }; MyLightRendering.Spotlights[index].ShadowMatrix = Matrix.Transpose(query.ProjectionInfo.CurrentLocalToProjection * MyMatrixHelpers.ClipspaceToTexture); m_shadowmapQueries.Add(query); casterIndex++; } index++; } }
private void PrepareSpotlights() { MyLights.Update(); MyLights.SpotlightsBvh.OverlapAllFrustum(ref MyRender11.Environment.ViewFrustumClippedD, MyLightRendering.VisibleSpotlights); if (MyLightRendering.VisibleSpotlights.Count == 0) { OtherShadowsTriangleCounter = 0; } MyLightRendering.VisibleSpotlights.Sort(m_spotlightCastersComparer); int index = 0; int casterIndex = 0; var worldMatrix = MatrixD.CreateTranslation(MyRender11.Environment.CameraPosition); foreach (var id in MyLightRendering.VisibleSpotlights) { if (id.CastsShadows && casterIndex < MAX_SPOTLIGHT_SHADOWCASTERS) { if (ShadowmapsPool.Count <= casterIndex) { ShadowmapsPool.Add(MyRwTextures.CreateShadowmap(SpotlightShadowmapSize, SpotlightShadowmapSize)); } MyLights.Lights.Data[id.Index].CastsShadowsThisFrame = true; MatrixD viewProjection = MyLights.GetSpotlightViewProjection(id); var query = new MyShadowmapQuery { DepthBuffer = ShadowmapsPool[casterIndex].Dsv, Viewport = new MyViewport(SpotlightShadowmapSize, SpotlightShadowmapSize), QueryType = MyFrustumEnum.ShadowProjection, ProjectionInfo = new MyProjectionInfo { WorldCameraOffsetPosition = MyRender11.Environment.CameraPosition, WorldToProjection = viewProjection, LocalToProjection = worldMatrix * viewProjection }, IgnoredEntities = MyLights.IgnoredEntitites.ContainsKey(id) ? MyLights.IgnoredEntitites[id] : null, }; m_shadowmapQueries.Add(query); ++casterIndex; } else { MyLights.Lights.Data[id.Index].CastsShadowsThisFrame = false; } index++; } }
static void AddShadowmapQueryIntoCullQuery(MyCullQuery cullQuery, MyShadowmapQuery shadowmapQuery) { cullQuery.AddDepthPass(shadowmapQuery); if (shadowmapQuery.QueryType == MyFrustumEnum.ShadowCascade) { var smallCulling = new MyCullingSmallObjects { ProjectionFactor = shadowmapQuery.ProjectionFactor, // <- this disables culling of objects depending on the previous cascade SkipThreshold = MyManagers.Shadow.GetSettingsSmallObjectSkipping(shadowmapQuery.Index), }; cullQuery.FrustumCullQueries[cullQuery.Size - 1].SmallObjects = smallCulling; } cullQuery.FrustumCullQueries[cullQuery.Size - 1].Type = shadowmapQuery.QueryType; cullQuery.FrustumCullQueries[cullQuery.Size - 1].Index = shadowmapQuery.Index; cullQuery.FrustumCullQueries[cullQuery.Size - 1].Ignored = shadowmapQuery.IgnoredEntities; }
private void PrepareSpotlights() { int index = 0; int casterIndex = 0; var worldMatrix = MatrixD.CreateTranslation(MyRender11.Environment.Matrices.CameraPosition); foreach (var id in MyLightsRendering.VisibleSpotlights) { if (id.CastsShadows && casterIndex < MAX_SPOTLIGHT_SHADOWCASTERS) { if (ShadowmapsPool.Count <= casterIndex) { ShadowmapsPool.Add(MyManagers.RwTextures.CreateDepth("ShadowmapsPool.Item", SpotlightShadowmapSize, SpotlightShadowmapSize, Format.R32_Typeless, Format.R32_Float, Format.D32_Float)); } MyLights.SetCastsShadowsThisFrame(id, true); MatrixD viewProjection = MyLights.GetSpotlightViewProjection(id); var query = new MyShadowmapQuery { DepthBuffer = ShadowmapsPool[casterIndex], Viewport = new MyViewport(SpotlightShadowmapSize, SpotlightShadowmapSize), QueryType = MyFrustumEnum.ShadowProjection, Index = casterIndex, ProjectionInfo = new MyProjectionInfo { WorldCameraOffsetPosition = MyRender11.Environment.Matrices.CameraPosition, WorldToProjection = viewProjection, LocalToProjection = worldMatrix * viewProjection }, IgnoredEntities = MyLights.GetEntitiesIgnoringShadow(id) }; m_shadowmapQueries.Add(query); ++casterIndex; } else { MyLights.SetCastsShadowsThisFrame(id, false); } index++; } }
static void AddShadowmapQueryIntoCullQuery(MyCullQuery cullQuery, MyShadowmapQuery shadowmapQuery) { bool isCascade = shadowmapQuery.QueryType == MyFrustumEnum.ShadowCascade; var matrix = shadowmapQuery.ProjectionInfo.WorldToProjection; cullQuery.AddDepthPass(ref matrix, shadowmapQuery.ProjectionInfo.CurrentLocalToProjection, shadowmapQuery.Viewport, shadowmapQuery.DepthBuffer, isCascade, ToString(shadowmapQuery.QueryType)); if (isCascade) { var smallCulling = new MyCullingSmallObjects { ProjectionFactor = shadowmapQuery.ProjectionFactor, // <- this disables culling of objects depending on the previous cascade SkipThreshold = MyManagers.Shadow.GetSettingsSmallObjectSkipping(shadowmapQuery.CascadeIndex), }; cullQuery.FrustumCullQueries[cullQuery.Size - 1].SmallObjects = smallCulling; cullQuery.FrustumCullQueries[cullQuery.Size - 1].CascadeIndex = shadowmapQuery.CascadeIndex; } cullQuery.FrustumCullQueries[cullQuery.Size - 1].Type = shadowmapQuery.QueryType; cullQuery.FrustumCullQueries[cullQuery.Size - 1].Ignored = shadowmapQuery.IgnoredEntities; }
static void PrepareCascades() { MyImmediateRC.RC.Context.CopyResource(m_cascadeShadowmapArray.Resource, m_cascadeShadowmapBackup.Resource); bool stabilize = true; for (int i = 0; i < 4; i++) { ++FramesSinceUpdate[i]; } CascadeLightDirection[0] = MyEnvironment.DirectionalLightDir; CascadeLightDirection[1] = MyEnvironment.DirectionalLightDir; const float DirectionDifferenceThreshold = 0.02f; if (FramesSinceUpdate[2] > 180 || MyEnvironment.DirectionalLightDir.Dot(CascadeLightDirection[2]) < (1 - DirectionDifferenceThreshold)) { FramesSinceUpdate[2] = 0; CascadeLightDirection[2] = MyEnvironment.DirectionalLightDir; } if (FramesSinceUpdate[3] > 180 || MyEnvironment.DirectionalLightDir.Dot(CascadeLightDirection[3]) < (1 - DirectionDifferenceThreshold)) { FramesSinceUpdate[3] = 0; CascadeLightDirection[3] = MyEnvironment.DirectionalLightDir; } var globalMatrix = CreateGlobalMatrix(); Matrix[] cascadesMatrices = new Matrix[8]; var cascadeFrozen = MyRender11.Settings.FreezeCascade.Any(x => x == true); if (!cascadeFrozen) { m_oldView = MyEnvironment.View; } float cascadesNearClip = 1f; float cascadesFarClip = 1000f; float backOffset = 100f; // more and fit projection to objects inside float shadowmapSize = m_cascadeResolution; m_splitDepth[0] = cascadesNearClip; m_splitDepth[1] = MyRender11.Settings.CascadesSplit0; m_splitDepth[2] = MyRender11.Settings.CascadesSplit1; m_splitDepth[3] = MyRender11.Settings.CascadesSplit2; m_splitDepth[4] = MyRender11.Settings.CascadesSplit3; float unitWidth = 1 / MyEnvironment.Projection.M11; float unitHeight = 1 / MyEnvironment.Projection.M22; var vertices = new Vector3[] { new Vector3(-unitWidth, -unitHeight, -1), new Vector3(-unitWidth, unitHeight, -1), new Vector3(unitWidth, unitHeight, -1), new Vector3(unitWidth, -unitHeight, -1), }; var frustumVerticesWS = new Vector3[8]; for (int c = 0; c < m_cascadesNum; c++) { for (int i = 0; i < 4; i++) { frustumVerticesWS[i] = vertices[i] * m_splitDepth[c]; frustumVerticesWS[i + 4] = vertices[i] * m_splitDepth[c + 1]; } if (MyRender11.Settings.FreezeCascade[c]) { // draw cascade bounding primtiive if (VisualizeDebug) { var invView = Matrix.Invert(m_oldView); Vector3.Transform(frustumVerticesWS, ref invView, frustumVerticesWS); var batch = MyLinesRenderer.CreateBatch(); batch.Add6FacedConvex(frustumVerticesWS, Color.Blue); var bs = BoundingSphere.CreateFromPoints(frustumVerticesWS); var bb = BoundingBox.CreateFromSphere(bs); batch.AddBoundingBox(bb, Color.OrangeRed); batch.Commit(); } continue; } /* * Cascades update scheme: * 0: 1 1 1 1 * 1: 1 0 1 0 * 2: 0 1 0 0 * 3: 0 0 0 1 */ bool skipCascade1 = c == 1 && (MyCommon.FrameCounter % 2) != 0; bool skipCascade2 = c == 2 && (MyCommon.FrameCounter % 4) != 1; bool skipCascade3 = c == 3 && (MyCommon.FrameCounter % 4) != 3; // if (skipCascade1 || skipCascade2 || skipCascade3) { if (!MyRender11.Settings.UpdateCascadesEveryFrame) { continue; } } Vector3.Transform(frustumVerticesWS, ref MyEnvironment.InvView, frustumVerticesWS); var bSphere = BoundingSphere.CreateFromPoints(frustumVerticesWS); if (stabilize) { bSphere.Center = bSphere.Center.Round(); bSphere.Radius = (float)Math.Ceiling(bSphere.Radius); } var offset = bSphere.Radius + cascadesNearClip + backOffset; var shadowCameraPosWS = bSphere.Center + CascadeLightDirection[c] * (bSphere.Radius + cascadesNearClip); var lightView = VRageMath.Matrix.CreateLookAt(shadowCameraPosWS, shadowCameraPosWS - CascadeLightDirection[c], Math.Abs(Vector3.UnitY.Dot(CascadeLightDirection[c])) < 0.99f ? Vector3.UnitY : Vector3.UnitX); Vector3 vMin = new Vector3(-bSphere.Radius, -bSphere.Radius, cascadesNearClip); Vector3 vMax = new Vector3(bSphere.Radius, bSphere.Radius, offset + bSphere.Radius); var cascadeProjection = Matrix.CreateOrthographicOffCenter(vMin.X, vMax.X, vMin.Y, vMax.Y, vMax.Z, vMin.Z); cascadesMatrices[c] = lightView * cascadeProjection; var transformed = Vector3.Transform(Vector3.Zero, ref cascadesMatrices[c]) * shadowmapSize / 2; var smOffset = (transformed.Round() - transformed) * 2 / shadowmapSize; // stabilize 1st cascade only if (stabilize) { cascadeProjection.M41 += smOffset.X; cascadeProjection.M42 += smOffset.Y; cascadesMatrices[c] = lightView * cascadeProjection; } var inverseCascadeMatrix = Matrix.Invert(cascadesMatrices[c]); var corner0 = Vector3.Transform(Vector3.Transform(new Vector3(-1, -1, 0), inverseCascadeMatrix), globalMatrix); var corner1 = Vector3.Transform(Vector3.Transform(new Vector3(1, 1, 1), inverseCascadeMatrix), globalMatrix); var d = corner1 - corner0; var cascadeScale = 1f / (corner1 - corner0); m_cascadeScale[c] = new Vector4(cascadeScale, 0); var query = new MyShadowmapQuery(); query.DepthBuffer = m_cascadeShadowmapArray.SubresourceDsv(c); query.Viewport = new MyViewport(shadowmapSize, shadowmapSize); m_cascadeInfo[c].WorldCameraOffsetPosition = MyEnvironment.CameraPosition; m_cascadeInfo[c].WorldToProjection = cascadesMatrices[c]; // todo: skip translation, recalculate matrix in local space, keep world space matrix only for bounding frustum m_cascadeInfo[c].LocalToProjection = Matrix.CreateTranslation(MyEnvironment.CameraPosition) * cascadesMatrices[c]; query.ProjectionInfo = m_cascadeInfo[c]; query.ProjectionDir = CascadeLightDirection[c]; query.ProjectionFactor = shadowmapSize * shadowmapSize / (bSphere.Radius * bSphere.Radius * 4); if (c == 0) { query.QueryType = MyFrustumEnum.Cascade0; } if (c == 1) { query.QueryType = MyFrustumEnum.Cascade1; } if (c == 2) { query.QueryType = MyFrustumEnum.Cascade2; } if (c == 3) { query.QueryType = MyFrustumEnum.Cascade3; } m_shadowmapQueries.Add(query); } if (true) { var verticesWS = new Vector3[8]; var batch = MyLinesRenderer.CreateBatch(); var cascadeColor = new[] { Color.Red, Color.Green, Color.Blue, Color.Yellow }; for (int c = 0; c < m_cascadesNum; c++) { if (MyRender11.Settings.FreezeCascade[c]) { if (VisualizeDebug) { var inverseViewProj = Matrix.Invert(cascadesMatrices[c]); Vector3.Transform(m_cornersCS, ref inverseViewProj, verticesWS); for (int i = 0; i < verticesWS.Length; i++) { verticesWS[i] += MyEnvironment.CameraPosition; } MyPrimitivesRenderer.Draw6FacedConvex(verticesWS, cascadeColor[c], 0.2f); batch.Add6FacedConvex(verticesWS, Color.Pink); } } } batch.Commit(); } var mapping = MyMapping.MapDiscard(m_csmConstants); for (int c = 0; c < m_cascadesNum; c++) { mapping.stream.Write(Matrix.Transpose(m_cascadeInfo[c].CurrentLocalToProjection * MyMatrixHelpers.ClipspaceToTexture)); } for (int i = m_cascadesNum; i < 8; i++) { mapping.stream.Write(Matrix.Zero); } for (int i = 0; i < m_splitDepth.Length; i++) { mapping.stream.Write(m_splitDepth[i]); } for (int i = m_splitDepth.Length; i < 8; i++) { mapping.stream.Write(0.0f); } for (int i = 0; i < 4; i++) { mapping.stream.Write(m_cascadeScale[i] / m_cascadeScale[0]); } mapping.Unmap(); }
/// <summary> /// Creates shadowmap queries and appends them to the provided list /// </summary> internal unsafe void PrepareQueries(List <MyShadowmapQuery> appendShadowmapQueries) { Debug.Assert(appendShadowmapQueries != null, "Shadowmap query list cannot be null!"); if (!MyRenderProxy.Settings.EnableShadows || !MyRender11.DebugOverrides.Shadows) { return; } MyImmediateRC.RC.BeginProfilingBlock("PrepareCascades"); MyImmediateRC.RC.DeviceContext.CopyResource(m_cascadeShadowmapArray.Resource, m_cascadeShadowmapBackup.Resource); bool stabilize = true; const float DirectionDifferenceThreshold = 0.0175f; float shadowChangeDelayMultiplier = 180; for (int cascadeIndex = 0; cascadeIndex < m_initializedShadowCascadesCount; ++cascadeIndex) { ++m_shadowCascadeFramesSinceLightUpdate[cascadeIndex]; if (m_shadowCascadeFramesSinceLightUpdate[cascadeIndex] > cascadeIndex * shadowChangeDelayMultiplier || MyRender11.Environment.DirectionalLightDir.Dot(m_shadowCascadeLightDirections[cascadeIndex]) < (1 - DirectionDifferenceThreshold)) { m_shadowCascadeLightDirections[cascadeIndex] = MyRender11.Environment.DirectionalLightDir; m_shadowCascadeFramesSinceLightUpdate[cascadeIndex] = 0; } } var globalMatrix = CreateGlobalMatrix(); float cascadesNearClip = 1f; float backOffset = MyRender11.RenderSettings.ShadowQuality.BackOffset(); float shadowmapSize = MyRender11.RenderSettings.ShadowQuality.ShadowCascadeResolution(); for (int cascadeIndex = 0; cascadeIndex < ShadowCascadeSplitDepths.Length; ++cascadeIndex) { ShadowCascadeSplitDepths[cascadeIndex] = MyRender11.RenderSettings.ShadowQuality.ShadowCascadeSplit(cascadeIndex); } double unitWidth = 1.0 / MyRender11.Environment.Projection.M11; double unitHeight = 1.0 / MyRender11.Environment.Projection.M22; Vector3D *untransformedVertices = stackalloc Vector3D[4]; untransformedVertices[0] = new Vector3D(-unitWidth, -unitHeight, -1); untransformedVertices[1] = new Vector3D(-unitWidth, unitHeight, -1); untransformedVertices[2] = new Vector3D(unitWidth, unitHeight, -1); untransformedVertices[3] = new Vector3D(unitWidth, -unitHeight, -1); MatrixD *cascadesMatrices = stackalloc MatrixD[MaxShadowCascades]; for (int cascadeIndex = 0; cascadeIndex < m_initializedShadowCascadesCount; ++cascadeIndex) { for (int vertexIndex = 0; vertexIndex < 4; ++vertexIndex) { m_frustumVerticesWS[vertexIndex] = untransformedVertices[vertexIndex] * ShadowCascadeSplitDepths[cascadeIndex]; m_frustumVerticesWS[vertexIndex + 4] = untransformedVertices[vertexIndex] * ShadowCascadeSplitDepths[cascadeIndex + 1]; } bool skipCascade = MyCommon.FrameCounter % (ulong)m_shadowCascadeUpdateIntervals[cascadeIndex].Item1 != (ulong)m_shadowCascadeUpdateIntervals[cascadeIndex].Item2; bool forceUpdate = ShadowCascadeSplitDepths[cascadeIndex] > 1000f && Vector3D.DistanceSquared(m_shadowCascadeUpdatePositions[cascadeIndex], MyRender11.Environment.CameraPosition) > Math.Pow(1000, 2); // if (!forceUpdate && skipCascade && !MyRenderProxy.Settings.UpdateCascadesEveryFrame) { continue; } if (MyRenderProxy.Settings.ShadowCascadeFrozen[cascadeIndex]) { continue; } m_shadowCascadeUpdatePositions[cascadeIndex] = MyRender11.Environment.CameraPosition; MatrixD invView = MyRender11.Environment.InvView; Vector3D.Transform(m_frustumVerticesWS, ref invView, m_frustumVerticesWS); var bSphere = BoundingSphereD.CreateFromPoints(m_frustumVerticesWS); if (stabilize) { bSphere.Center = bSphere.Center.Round(); bSphere.Radius = Math.Ceiling(bSphere.Radius); } var shadowCameraPosWS = bSphere.Center + m_shadowCascadeLightDirections[cascadeIndex] * (bSphere.Radius + cascadesNearClip); var lightView = VRageMath.MatrixD.CreateLookAt(shadowCameraPosWS, shadowCameraPosWS - m_shadowCascadeLightDirections[cascadeIndex], Math.Abs(Vector3.UnitY.Dot(m_shadowCascadeLightDirections[cascadeIndex])) < 0.99f ? Vector3.UnitY : Vector3.UnitX); var offset = bSphere.Radius + cascadesNearClip + backOffset; Vector3D vMin = new Vector3D(-bSphere.Radius, -bSphere.Radius, cascadesNearClip); Vector3D vMax = new Vector3D(bSphere.Radius, bSphere.Radius, offset + bSphere.Radius); var cascadeProjection = MatrixD.CreateOrthographicOffCenter(vMin.X, vMax.X, vMin.Y, vMax.Y, vMax.Z, vMin.Z); cascadesMatrices[cascadeIndex] = lightView * cascadeProjection; var transformed = Vector3D.Transform(Vector3D.Zero, cascadesMatrices[cascadeIndex]) * shadowmapSize / 2; var smOffset = (transformed.Round() - transformed) * 2 / shadowmapSize; // stabilize 1st cascade only if (stabilize) { cascadeProjection.M41 += smOffset.X; cascadeProjection.M42 += smOffset.Y; cascadesMatrices[cascadeIndex] = lightView * cascadeProjection; } var inverseCascadeMatrix = MatrixD.Invert(cascadesMatrices[cascadeIndex]); var corner0 = Vector3D.Transform(Vector3D.Transform(new Vector3D(-1, -1, 0), inverseCascadeMatrix), globalMatrix); var corner1 = Vector3D.Transform(Vector3D.Transform(new Vector3D(1, 1, 1), inverseCascadeMatrix), globalMatrix); var diameter = corner1 - corner0; var cascadeScale = 1f / diameter; ShadowCascadeScales[cascadeIndex] = new Vector4D(cascadeScale, 0); var query = new MyShadowmapQuery(); query.DepthBuffer = m_cascadeShadowmapArray.SubresourceDsv(cascadeIndex); query.Viewport = new MyViewport(shadowmapSize, shadowmapSize); m_cascadeInfo[cascadeIndex].WorldCameraOffsetPosition = MyRender11.Environment.CameraPosition; m_cascadeInfo[cascadeIndex].WorldToProjection = cascadesMatrices[cascadeIndex]; // todo: skip translation, recalculate matrix in local space, keep world space matrix only for bounding frustum m_cascadeInfo[cascadeIndex].LocalToProjection = Matrix.CreateTranslation(MyRender11.Environment.CameraPosition) * cascadesMatrices[cascadeIndex]; query.ProjectionInfo = m_cascadeInfo[cascadeIndex]; query.ProjectionDir = m_shadowCascadeLightDirections[cascadeIndex]; query.ProjectionFactor = (float)(shadowmapSize * shadowmapSize / (bSphere.Radius * bSphere.Radius * 4)); query.QueryType = MyFrustumEnum.ShadowCascade; query.CascadeIndex = cascadeIndex; appendShadowmapQueries.Add(query); } DebugProcessFrustrums(); FillConstantBuffer(m_csmConstants); MyImmediateRC.RC.EndProfilingBlock(); }
static void PrepareCascades() { MyImmediateRC.RC.BeginProfilingBlock("PrepareCascades"); MyImmediateRC.RC.Context.CopyResource(m_cascadeShadowmapArray.Resource, m_cascadeShadowmapBackup.Resource); bool stabilize = true; const float DirectionDifferenceThreshold = 0.02f; for (int cascadeIndex = 0; cascadeIndex < m_initializedShadowCascadesCount; ++cascadeIndex) { ++m_shadowCascadeFramesSinceUpdate[cascadeIndex]; if (m_shadowCascadeFramesSinceUpdate[cascadeIndex] > cascadeIndex * 60 || MyEnvironment.DirectionalLightDir.Dot(m_shadowCascadeLightDirections[cascadeIndex]) < (1 - DirectionDifferenceThreshold)) { m_shadowCascadeLightDirections[cascadeIndex] = MyEnvironment.DirectionalLightDir; m_shadowCascadeFramesSinceUpdate[cascadeIndex] = 0; } } var globalMatrix = CreateGlobalMatrix(); MatrixD[] cascadesMatrices = new MatrixD[8]; var cascadeFrozen = MyRender11.Settings.ShadowCascadeFrozen.Any(x => x == true); if (!cascadeFrozen) { m_oldView = MyEnvironment.View; } float cascadesNearClip = 1f; float cascadesFarClip = MyRender11.RenderSettings.ShadowQuality.ShadowCascadeSplit(m_initializedShadowCascadesCount); float backOffset = MyRender11.RenderSettings.ShadowQuality.BackOffset(); float shadowmapSize = MyRender11.RenderSettings.ShadowQuality.ShadowCascadeResolution(); var oldCascadeSplit = 0.0f; bool useFarShadows = MyRenderProxy.Settings.FarShadowDistanceOverride > MyRender11.Settings.ShadowCascadeMaxDistance; if (useFarShadows) { oldCascadeSplit = MyRender11.Settings.ShadowCascadeMaxDistance; MyRender11.Settings.ShadowCascadeMaxDistance = MyRenderProxy.Settings.FarShadowDistanceOverride; } for (int cascadeIndex = 0; cascadeIndex < ShadowCascadeSplitDepths.Length; ++cascadeIndex) { ShadowCascadeSplitDepths[cascadeIndex] = MyRender11.RenderSettings.ShadowQuality.ShadowCascadeSplit(cascadeIndex); } if (useFarShadows) { MyRender11.Settings.ShadowCascadeMaxDistance = oldCascadeSplit; } double unitWidth = 1.0 / MyEnvironment.Projection.M11; double unitHeight = 1.0 / MyEnvironment.Projection.M22; var vertices = new Vector3D[] { new Vector3D(-unitWidth, -unitHeight, -1), new Vector3D(-unitWidth, unitHeight, -1), new Vector3D(unitWidth, unitHeight, -1), new Vector3D(unitWidth, -unitHeight, -1), }; var frustumVerticesWS = new Vector3D[8]; for (int cascadeIndex = 0; cascadeIndex < m_initializedShadowCascadesCount; ++cascadeIndex) { for (int i = 0; i < 4; i++) { frustumVerticesWS[i] = vertices[i] * ShadowCascadeSplitDepths[cascadeIndex]; frustumVerticesWS[i + 4] = vertices[i] * ShadowCascadeSplitDepths[cascadeIndex + 1]; } if (MyRender11.Settings.ShadowCascadeFrozen[cascadeIndex]) { // draw cascade bounding primtiive if (VisualizeDebug) { var oldInvView = MatrixD.Invert(m_oldView); Vector3D.Transform(frustumVerticesWS, ref oldInvView, frustumVerticesWS); var verticesF = new Vector3[8]; for (int i = 0; i < 8; i++) { verticesF[i] = frustumVerticesWS[i]; } var batch = MyLinesRenderer.CreateBatch(); batch.Add6FacedConvex(verticesF, Color.Blue); var bs = BoundingSphere.CreateFromPoints(verticesF); var bb = BoundingBox.CreateFromSphere(bs); batch.AddBoundingBox(bb, Color.OrangeRed); batch.Commit(); } continue; } /* * Cascades update scheme: * 1 2 3 4 5 6 7 * 0: 1 1 1 1 1 1 1 * 1: 1 0 1 0 1 0 1 * 2: 0 1 0 0 1 0 0 * 3: 0 0 0 1 0 0 0 * 4: 0 0 0 0 0 1 0 * 5: 0 0 0 0 0 0 1 */ bool skipCascade = false; // TODO: properly bool skipCascade1 = cascadeIndex == 1 && (MyCommon.FrameCounter % 2) != 0; bool skipCascade2 = cascadeIndex == 2 && (MyCommon.FrameCounter % 4) != 1; bool skipCascade3 = cascadeIndex == 3 && (MyCommon.FrameCounter % 4) != 3; bool skipCascade4 = cascadeIndex == 4 && (MyCommon.FrameCounter % 8) != 5; bool skipCascade5 = cascadeIndex == 5 && (MyCommon.FrameCounter % 8) != 7; skipCascade = skipCascade1 || skipCascade2 || skipCascade3 || skipCascade4 || skipCascade5; // if (skipCascade && !MyRender11.Settings.UpdateCascadesEveryFrame) { continue; } MatrixD invView = MyEnvironment.InvView; Vector3D.Transform(frustumVerticesWS, ref invView, frustumVerticesWS); var bSphere = BoundingSphereD.CreateFromPoints((IEnumerable <Vector3D>)frustumVerticesWS); if (stabilize) { bSphere.Center = bSphere.Center.Round(); bSphere.Radius = Math.Ceiling(bSphere.Radius); } var shadowCameraPosWS = bSphere.Center + m_shadowCascadeLightDirections[cascadeIndex] * (bSphere.Radius + cascadesNearClip); var lightView = VRageMath.MatrixD.CreateLookAt(shadowCameraPosWS, shadowCameraPosWS - m_shadowCascadeLightDirections[cascadeIndex], Math.Abs(Vector3.UnitY.Dot(m_shadowCascadeLightDirections[cascadeIndex])) < 0.99f ? Vector3.UnitY : Vector3.UnitX); var longestShadow = MyRenderProxy.Settings.LongShadowFactor; var offset = bSphere.Radius + cascadesNearClip + backOffset + (longestShadow < 0 ? 0.0 : longestShadow); Vector3D vMin = new Vector3D(-bSphere.Radius, -bSphere.Radius, cascadesNearClip); Vector3D vMax = new Vector3D(bSphere.Radius, bSphere.Radius, offset + bSphere.Radius); var cascadeProjection = MatrixD.CreateOrthographicOffCenter(vMin.X, vMax.X, vMin.Y, vMax.Y, vMax.Z, vMin.Z); cascadesMatrices[cascadeIndex] = lightView * cascadeProjection; var transformed = Vector3D.Transform(Vector3D.Zero, cascadesMatrices[cascadeIndex]) * shadowmapSize / 2; var smOffset = (transformed.Round() - transformed) * 2 / shadowmapSize; // stabilize 1st cascade only if (stabilize) { cascadeProjection.M41 += smOffset.X; cascadeProjection.M42 += smOffset.Y; cascadesMatrices[cascadeIndex] = lightView * cascadeProjection; } var inverseCascadeMatrix = MatrixD.Invert(cascadesMatrices[cascadeIndex]); var corner0 = Vector3D.Transform(Vector3D.Transform(new Vector3D(-1, -1, 0), inverseCascadeMatrix), globalMatrix); var corner1 = Vector3D.Transform(Vector3D.Transform(new Vector3D(1, 1, 1), inverseCascadeMatrix), globalMatrix); var d = corner1 - corner0; var cascadeScale = 1f / (corner1 - corner0); ShadowCascadeScales[cascadeIndex] = new Vector4D(cascadeScale, 0); var query = new MyShadowmapQuery(); query.DepthBuffer = m_cascadeShadowmapArray.SubresourceDsv(cascadeIndex); query.Viewport = new MyViewport(shadowmapSize, shadowmapSize); m_cascadeInfo[cascadeIndex].WorldCameraOffsetPosition = MyEnvironment.CameraPosition; m_cascadeInfo[cascadeIndex].WorldToProjection = cascadesMatrices[cascadeIndex]; // todo: skip translation, recalculate matrix in local space, keep world space matrix only for bounding frustum m_cascadeInfo[cascadeIndex].LocalToProjection = Matrix.CreateTranslation(MyEnvironment.CameraPosition) * cascadesMatrices[cascadeIndex]; query.ProjectionInfo = m_cascadeInfo[cascadeIndex]; query.ProjectionDir = m_shadowCascadeLightDirections[cascadeIndex]; query.ProjectionFactor = (float)(shadowmapSize * shadowmapSize / (bSphere.Radius * bSphere.Radius * 4)); query.QueryType = MyFrustumEnum.ShadowCascade; query.CascadeIndex = cascadeIndex; m_shadowmapQueries.Add(query); } if (true) { var verticesWS = new Vector3D[8]; var batch = MyLinesRenderer.CreateBatch(); var cascadeColor = new[] { Color.Red, Color.Green, Color.Blue, Color.Yellow }; for (int c = 0; c < m_initializedShadowCascadesCount; c++) { if (MyRender11.Settings.ShadowCascadeFrozen[c]) { if (VisualizeDebug) { var inverseViewProj = MatrixD.Invert(cascadesMatrices[c]); Vector3D.Transform(m_cornersCS, ref inverseViewProj, verticesWS); for (int i = 0; i < verticesWS.Length; i++) { verticesWS[i] += MyEnvironment.CameraPosition; } var verticesF = new Vector3[8]; for (int i = 0; i < 8; i++) { verticesF[i] = verticesWS[i]; } MyPrimitivesRenderer.Draw6FacedConvex(verticesF, cascadeColor[c], 0.2f); batch.Add6FacedConvex(verticesF, Color.Pink); } } } batch.Commit(); } var mapping = MyMapping.MapDiscard(m_csmConstants); for (int c = 0; c < m_initializedShadowCascadesCount; c++) { mapping.stream.Write(Matrix.Transpose(m_cascadeInfo[c].CurrentLocalToProjection * MyMatrixHelpers.ClipspaceToTexture)); } for (int i = m_initializedShadowCascadesCount; i < 8; i++) { mapping.stream.Write(Matrix.Zero); } for (int i = 0; i < ShadowCascadeSplitDepths.Length; i++) { mapping.stream.Write(ShadowCascadeSplitDepths[i]); } for (int i = ShadowCascadeSplitDepths.Length; i < 8; i++) { mapping.stream.Write(0.0f); } for (int i = 0; i < m_initializedShadowCascadesCount; i++) { mapping.stream.Write(ShadowCascadeScales[i] / ShadowCascadeScales[0]); } mapping.Unmap(); MyImmediateRC.RC.EndProfilingBlock(); }