static void PrepareCascades() { 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) 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 || MyEnvironment.DirectionalLightDir.Dot(m_shadowCascadeLightDirections[cascadeIndex]) < (1 - DirectionDifferenceThreshold)) { m_shadowCascadeLightDirections[cascadeIndex] = MyEnvironment.DirectionalLightDir; m_shadowCascadeFramesSinceLightUpdate[cascadeIndex] = 0; } } var globalMatrix = CreateGlobalMatrix(); var cascadeFrozen = MyRenderProxy.Settings.ShadowCascadeFrozen.Any(x => x == true); if (!cascadeFrozen) m_oldView = MyEnvironment.View; 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 / MyEnvironment.Projection.M11; double unitHeight = 1.0 / MyEnvironment.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]; } if (MyRender11.Settings.ShadowCascadeFrozen[cascadeIndex]) { // draw cascade bounding primtiive if (VISUALIZE_DEBUG) { var oldInvView = MatrixD.Invert(m_oldView); Vector3D.Transform(m_frustumVerticesWS, ref oldInvView, m_frustumVerticesWS); var verticesF = new Vector3[8]; for (int i = 0; i < 8; i++) { verticesF[i] = m_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; } bool skipCascade = MyCommon.FrameCounter % (ulong)m_shadowCascadeUpdateIntervals[cascadeIndex].Item1 != (ulong)m_shadowCascadeUpdateIntervals[cascadeIndex].Item2; bool forceUpdate = ShadowCascadeSplitDepths[cascadeIndex] > 1000f && Vector3D.DistanceSquared(m_shadowCascadeUpdatePositions[cascadeIndex], MyEnvironment.CameraPosition) > Math.Pow(1000, 2); // if (!forceUpdate && skipCascade && !MyRenderProxy.Settings.UpdateCascadesEveryFrame) continue; m_shadowCascadeUpdatePositions[cascadeIndex] = MyEnvironment.CameraPosition; MatrixD invView = MyEnvironment.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 = 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; appendShadowmapQueries.Add(query); } if (VISUALIZE_DEBUG) DebugDrawFrozen(cascadesMatrices); FillConstantBuffer(m_csmConstants); MyImmediateRC.RC.EndProfilingBlock(); }
static void PrepareSpotlights() { MyLights.SpotlightsBvh.OverlapAllFrustum(ref MyEnvironment.ViewFrustum, MyLightRendering.VisibleSpotlights); 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++; } }