public void TestCullerPlaceInTree() { var game = new DX11Game(); Vector3 radius = new Vector3(100, 1000, 100); FrustumCuller culler = new FrustumCuller(new BoundingBox(-radius, radius), 5); var visualizer = new QuadTreeVisualizer(); List <TestCullObject> cullObjects = new List <TestCullObject>(); TestCullObject obj; obj = new TestCullObject(new Vector3(5, 2, 5), 2); cullObjects.Add(obj); obj = new TestCullObject(new Vector3(-20, 2, -20), 15); cullObjects.Add(obj); obj = new TestCullObject(new Vector3(100, 2, -20), 4); cullObjects.Add(obj); obj = new TestCullObject(new Vector3(-50, 9, 24), 20); cullObjects.Add(obj); for (int i = 0; i < cullObjects.Count; i++) { culler.AddCullable(cullObjects[i]); } game.GameLoopEvent += delegate { for (int i = 0; i < cullObjects.Count; i++) { game.LineManager3D.AddBox(cullObjects[i].BoundingBox, Color.Red.dx()); } visualizer.RenderNodeGroundBoundig(game, culler.RootNode, delegate(FrustumCuller.CullNode node, out Color4 col) { col = Color.Green.dx(); return(node.Cullables.Count == 0); }); visualizer.RenderNodeGroundBoundig(game, culler.RootNode, delegate(FrustumCuller.CullNode node, out Color4 col) { col = Color.Orange.dx(); return(node.Cullables.Count > 0); }); }; game.Run(); }
public DirectionalLight(FrustumCuller culler) { LightDirection = Vector3.Normalize(new Vector3(1, 2, 1)); Color = new Vector3(1, 1, 0.9f); ShadowViews = new FrustumCullerView[4]; for (int i = 0; i < ShadowViews.Length; i++) { ShadowViews[i] = culler.CreateView(); } }
public SpotLight(FrustumCuller culler) { LightPosition = new Vector3(0, 6, 0); LightRadius = 6; LightIntensity = 1; SpotDirection = MathHelper.Down; SpotLightAngle = MathHelper.ToRadians(30); SpotDecayExponent = 1; Color = new Vector3(1, 1, 0.9f); ShadowView = culler.CreateView(); }
public PointLight(FrustumCuller culler) { LightPosition = new Vector3(0, 6, 0); LightRadius = 6; LightIntensity = 1; Color = new Vector3(1, 1, 0.9f); Views = new FrustumCullerView[6]; for (int i = 0; i < Views.Length; i++) { Views[i] = culler.CreateView(); } }
public void TestCullerVisibility() { var game = new DX11Game(); Vector3 radius = new Vector3(100, 1000, 100); FrustumCuller culler = new FrustumCuller(new BoundingBox(-radius, radius), 6); QuadTreeVisualizer visualizer = new QuadTreeVisualizer(); List <TestCullObject> cullObjects = new List <TestCullObject>(); TestCullObject obj; for (int i = 0; i < cullObjects.Count; i++) { culler.AddCullable(cullObjects[i]); } SpectaterCamera cullCam = new SpectaterCamera(10f, 80); cullCam.Positie = new Vector3(8, 10, 8); cullCam.EnableUserInput = false; bool rotate = true; int selectedNode = -1; var view = culler.CreateView(); game.GameLoopEvent += delegate { view.UpdateVisibility(cullCam.ViewProjection); if (rotate) { cullCam.AngleHorizontal += game.Elapsed * MathHelper.Pi * (1 / 8f); } if (game.Keyboard.IsKeyPressed(Key.NumberPadPlus)) { selectedNode++; } if (game.Keyboard.IsKeyPressed(Key.NumberPadMinus)) { selectedNode--; } if (game.Keyboard.IsKeyPressed(Key.Return)) { int count = -1; visualizer.RenderNodeGroundBoundig(game, culler.RootNode, delegate(FrustumCuller.CullNode node, out Color4 col) { col = Color.Red.dx(); count++; if (count == selectedNode) { node.Tag = "SELECTED!"; } return(count == selectedNode); }); } if (game.Keyboard.IsKeyPressed(Key.NumberPad0)) { rotate = !rotate; } game.LineManager3D.AddViewFrustum(new global::DirectX11.BoundingFrustum(cullCam.ViewProjection), Color.Black.dx()); for (int i = 0; i < cullObjects.Count; i++) { game.LineManager3D.AddBox(cullObjects[i].BoundingBox, Color.Red.dx()); } visualizer.RenderNodeGroundBoundig(game, culler.RootNode, delegate(FrustumCuller.CullNode node, out Color4 col) { col = Color.Green.dx(); return(!view.IsNodeVisible(node)); }); visualizer.RenderNodeGroundBoundig(game, culler.RootNode, delegate(FrustumCuller.CullNode node, out Color4 col) { col = Color.Orange.dx(); return(view.IsNodeVisible(node)); }); cullCam.Update(game.Elapsed, game.Keyboard, game.Mouse); /*int count = -1; * visualizer.RenderNodeGroundBoundig(game, culler.RootNode, * delegate(Culler.CullNode node, out Color col) * { * col = Color.Red; * count++; * return count == selectedNode; * });*/ }; game.Run(); }
public void TestCullerObjects() { var game = new DX11Game(); game.InitDirectX(); Vector3 radius = new Vector3(100, 1000, 100); FrustumCuller culler = new FrustumCuller(new BoundingBox(-radius, radius), 6); var mesh = RenderingTestsHelper.CreateSimpleTestMesh(); var texturePool = new TexturePool(game); var gBuffer = new GBuffer(game.Device, 800, 600); var renderer = new DeferredMeshesRenderer(game, gBuffer, texturePool); var final = new CombineFinalRenderer(game, gBuffer); DeferredMeshElement middle = null; for (int i = 0; i < 50; i++) { for (int j = 0; j < 50; j++) { var el = renderer.AddMesh(mesh); el.WorldMatrix = Matrix.Translation(MathHelper.Right * i * 2 + Vector3.UnitZ * j * 2); if (i > 20 && i < 30 && j > 20 && j < 30) { el.Delete(); } else { culler.AddCullable(el); } } } QuadTreeVisualizer visualizer = new QuadTreeVisualizer(); List <TestCullObject> cullObjects = new List <TestCullObject>(); SpectaterCamera cullCam = new SpectaterCamera(10f, 80); cullCam.Positie = new Vector3(8, 10, 8); cullCam.EnableUserInput = false; bool rotate = true; int selectedNode = -1; var view = culler.CreateView(); game.GameLoopEvent += delegate { view.UpdateVisibility(cullCam.ViewProjection); var visibleCullables = view.GetVisibleCullables(); if (rotate) { cullCam.AngleHorizontal += game.Elapsed * MathHelper.Pi * (1 / 8f); } if (game.Keyboard.IsKeyPressed(Key.NumberPadPlus)) { selectedNode++; } if (game.Keyboard.IsKeyPressed(Key.NumberPadMinus)) { selectedNode--; } if (game.Keyboard.IsKeyPressed(Key.Return)) { int count = -1; visualizer.RenderNodeGroundBoundig(game, culler.RootNode, delegate(FrustumCuller.CullNode node, out Color4 col) { col = Color.Red.dx(); count++; if (count == selectedNode) { node.Tag = "SELECTED!"; } return(count == selectedNode); }); } if (game.Keyboard.IsKeyPressed(Key.NumberPad0)) { rotate = !rotate; } gBuffer.Clear(); gBuffer.SetTargetsToOutputMerger(); renderer.Draw(); game.Device.ImmediateContext.ClearState(); game.SetBackbuffer(); final.DrawCombined(); game.LineManager3D.AddViewFrustum(new BoundingFrustum(cullCam.ViewProjection), Color.White.dx()); for (int i = 0; i < visibleCullables.Count; i++) { game.LineManager3D.AddBox(visibleCullables[i].BoundingBox, Color.Red.dx()); } visualizer.RenderNodeGroundBoundig(game, culler.RootNode, delegate(FrustumCuller.CullNode node, out Color4 col) { col = Color.Green.dx(); return(!view.IsNodeVisible(node)); }); visualizer.RenderNodeGroundBoundig(game, culler.RootNode, delegate(FrustumCuller.CullNode node, out Color4 col) { col = Color.Orange.dx(); return(view.IsNodeVisible(node)); }); cullCam.Update(game.Elapsed, game.Keyboard, game.Mouse); /*int count = -1; * visualizer.RenderNodeGroundBoundig(game, culler.RootNode, * delegate(Culler.CullNode node, out Color col) * { * col = Color.Red; * count++; * return count == selectedNode; * });*/ }; game.Run(); }
public void TestPointLightDownViewFrustumVisibility() { var game = new DX11Game(); game.InitDirectX(); Vector3 radius = new Vector3(100, 1000, 100); FrustumCuller culler = new FrustumCuller(new BoundingBox(-radius, radius), 6); QuadTreeVisualizer visualizer = new QuadTreeVisualizer(); List <TestCullObject> cullObjects = new List <TestCullObject>(); TestCullObject obj; for (int i = 0; i < cullObjects.Count; i++) { culler.AddCullable(cullObjects[i]); } Matrix viewProjection = Matrix.Identity; var pos = game.SpectaterCamera.CameraPosition; bool rotate = true; int selectedNode = -1; var view = culler.CreateView(); game.GameLoopEvent += delegate { if (game.Keyboard.IsKeyDown(Key.C)) { pos = game.SpectaterCamera.CameraPosition; } viewProjection = PointLightRenderer.CreateShadowMapView(pos, 3) * PointLightRenderer.CreateShadowMapProjection(10); view.UpdateVisibility(viewProjection); if (game.Keyboard.IsKeyPressed(Key.NumberPadPlus)) { selectedNode++; } if (game.Keyboard.IsKeyPressed(Key.NumberPadMinus)) { selectedNode--; } game.LineManager3D.AddViewFrustum(new BoundingFrustum(viewProjection), Color.Black.dx()); for (int i = 0; i < cullObjects.Count; i++) { game.LineManager3D.AddBox(cullObjects[i].BoundingBox, Color.Red.dx()); } if (game.Keyboard.IsKeyDown(Key.Return)) { int count = -1; visualizer.RenderNodeGroundBoundig(game, culler.RootNode, delegate(FrustumCuller.CullNode node, out Color4 col) { col = Color.Green.dx(); count++; if (count == selectedNode) { col = Color.Red.dx(); node.Tag = "SELECTED!"; } return(count == selectedNode); }); } else { visualizer.RenderNodeGroundBoundig(game, culler.RootNode, delegate(FrustumCuller.CullNode node, out Color4 col) { if (view.IsNodeVisible(node)) { col = Color.Orange.dx(); return(true); } col = Color.Green.dx(); return(false); }); } /*int count = -1; * visualizer.RenderNodeGroundBoundig(game, culler.RootNode, * delegate(Culler.CullNode node, out Color col) * { * col = Color.Red; * count++; * return count == selectedNode; * });*/ }; game.Run(); }
public DeferredRenderer(DX11Game game) { this.game = game; var device = game.Device; context = device.ImmediateContext; screenWidth = game.Form.Form.ClientSize.Width; screenHeight = game.Form.Form.ClientSize.Height; int width = screenWidth; int height = screenHeight; gBuffer = new GBuffer(game.Device, width, height); texturePool = new TexturePool(game); meshesRenderer = new DeferredMeshesRenderer(game, gBuffer, TexturePool); directionalLightRenderer = new DirectionalLightRenderer(game, GBuffer); spotLightRenderer = new SpotLightRenderer(game, GBuffer); pointLightRenderer = new PointLightRenderer(game, GBuffer); combineFinalRenderer = new CombineFinalRenderer(game, GBuffer); var desc = new Texture2DDescription { BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource, Format = Format.R16G16B16A16_Float, Width = screenWidth, Height = screenHeight, ArraySize = 1, SampleDescription = new SampleDescription(1, 0), MipLevels = 1 }; hdrImage = new Texture2D(device, desc); hdrImageRtv = new RenderTargetView(device, hdrImage); hdrImageRV = new ShaderResourceView(device, hdrImage); calculater = new AverageLuminanceCalculater(game, hdrImageRV); toneMap = new ToneMapRenderer(game); var tempDesc = new Texture2DDescription { ArraySize = 1, BindFlags = BindFlags.None, CpuAccessFlags = CpuAccessFlags.Read, Format = Format.R32_Float, Height = 1, MipLevels = 1, OptionFlags = ResourceOptionFlags.None, SampleDescription = new SampleDescription(1, 0), Usage = ResourceUsage.Staging, Width = 1 }; tempTex = new Texture2D(device, tempDesc); ssao = new HorizonSSAORenderer(game, screenWidth, screenHeight); Vector3 radius = new Vector3(500, 1000, 500); frustumCuller = new FrustumCuller(new BoundingBox(-radius, radius), 1); gbufferView = frustumCuller.CreateView(); meshesRenderer.Culler = frustumCuller; Texture2D skyColorTexture;// = Texture2D.FromFile(game.Device, TWDir.GameData.CreateSubdirectory("Core") + "\\skyColor.bmp"); var strm = new DataStream(16 * 4, true, true); var multiplier = 2; strm.Write(new Half4(new Half(135f / 255f * multiplier), new Half(206f / 255f * multiplier), new Half(235 / 255f * multiplier), new Half(1))); strm.Position = 0; var dataRectangle = new DataRectangle(16 * 4, strm); skyColorTexture = new Texture2D(game.Device, new Texture2DDescription { ArraySize = 1, BindFlags = BindFlags.ShaderResource, CpuAccessFlags = CpuAccessFlags.None, Format = Format.R16G16B16A16_Float, Height = 1, MipLevels = 1, OptionFlags = ResourceOptionFlags.None, SampleDescription = new SampleDescription(1, 0), Usage = ResourceUsage.Default, Width = 1 }, dataRectangle); skyColorRV = new ShaderResourceView(game.Device, skyColorTexture); postProcessRT1 = CreateBackbufferLikeRT(); postProcessRT2 = CreateBackbufferLikeRT(); fogRenderer = new FogEffect(game); backgroundDepthStencilState = DepthStencilState.FromDescription(game.Device, new DepthStencilStateDescription() { IsDepthEnabled = true, DepthComparison = Comparison.LessEqual, DepthWriteMask = DepthWriteMask.Zero, }); lineManager = new LineManager3D(game.Device); updateRasterizerState(); }
protected void ComputeProjections( List <SSObject> objects, Matrix4 cameraView, Matrix4 cameraProj, float fov, float aspect, float cameraNearZ, float cameraFarZ) { if (m_light.GetType() != typeof(SSDirectionalLight)) { throw new NotSupportedException(); } SSDirectionalLight dirLight = (SSDirectionalLight)m_light; // light-aligned unit vectors Vector3 lightZ = dirLight.Direction.Normalized(); Vector3 lightX, lightY; OpenTKHelper.TwoPerpAxes(lightZ, out lightX, out lightY); // transform matrix from regular space into light aligned space Matrix4 lightTransform = new Matrix4( lightX.X, lightX.Y, lightX.Z, 0f, lightY.X, lightY.Y, lightY.Z, 0f, lightZ.X, lightZ.Y, lightZ.Z, 0f, 0f, 0f, 0f, 0f ); // Step 0: camera projection matrix (nearZ and farZ modified) for each frustum split float prevFarZ = cameraNearZ; for (int i = 0; i < c_numberOfSplits; ++i) { // generate frustum splits using Practical Split Scheme (GPU Gems 3, 10.2.1) float iRatio = (float)(i + 1) / (float)c_numberOfSplits; float cLog = cameraNearZ * (float)Math.Pow(cameraFarZ / cameraNearZ, iRatio); float cUni = cameraNearZ + (cameraFarZ - cameraNearZ) * iRatio; float nextFarZ = LogVsLinearSplitFactor * cLog + (1f - LogVsLinearSplitFactor) * cUni; float nextNearZ = prevFarZ; // exported to the shader m_viewSplits [i] = nextFarZ; // create a view proj matrix with the nearZ, farZ values for the current split m_frustumViewProjMatrices[i] = cameraView * Matrix4.CreatePerspectiveFieldOfView(fov, aspect, nextNearZ, nextFarZ); // create light-aligned AABBs of frustums m_frustumLightBB [i] = SSAABB.FromFrustum(ref lightTransform, ref m_frustumViewProjMatrices [i]); prevFarZ = nextFarZ; } #if true // Optional scene-dependent optimization for (int i = 0; i < c_numberOfSplits; ++i) { m_objsLightBB[i] = new SSAABB(float.PositiveInfinity, float.NegativeInfinity); m_splitFrustums[i] = new FrustumCuller(ref m_frustumViewProjMatrices[i]); m_shrink[i] = false; } foreach (var obj in objects) { // pass through all shadow casters and receivers if (obj.renderState.toBeDeleted || obj.localBoundingSphereRadius <= 0f || !obj.renderState.visible || !obj.renderState.receivesShadows) { continue; } else { for (int i = 0; i < c_numberOfSplits; ++i) { if (m_splitFrustums[i].isSphereInsideFrustum(obj.worldBoundingSphere)) { // determine AABB in light coordinates of the objects so far m_shrink[i] = true; Vector3 lightAlignedPos = Vector3.Transform(obj.worldBoundingSphereCenter, lightTransform); Vector3 rad = new Vector3(obj.worldBoundingSphereRadius); Vector3 localMin = lightAlignedPos - rad; Vector3 localMax = lightAlignedPos + rad; m_objsLightBB[i].UpdateMin(localMin); m_objsLightBB[i].UpdateMax(localMax); } } } } #endif for (int i = 0; i < c_numberOfSplits; ++i) { if (m_shrink [i]) { m_resultLightBB[i].Min = Vector3.ComponentMax(m_frustumLightBB [i].Min, m_objsLightBB [i].Min); m_resultLightBB [i].Max = Vector3.ComponentMin(m_frustumLightBB [i].Max, m_objsLightBB [i].Max); } else { m_resultLightBB [i] = m_frustumLightBB [i]; } } for (int i = 0; i < c_numberOfSplits; ++i) { // Obtain view + projection + crop matrix, need it later Matrix4 shadowView, shadowProj; viewProjFromLightAlignedBB(ref m_resultLightBB [i], ref lightTransform, ref lightY, out shadowView, out shadowProj); m_shadowViewProjMatrices[i] = shadowView * shadowProj * c_cropMatrices[i]; // obtain view + projection + clio + bias m_shadowViewProjBiasMatrices[i] = m_shadowViewProjMatrices[i] * c_biasMatrix; // There is, currently, no mathematically derived solution to how much Poisson spread scaling // you need for each split. Current improvisation combines 1) increasing spread for the near // splits; reducing spread for the far splits and 2) reducing spread for splits with larger // light-aligned areas; increasing spread for splits with smaller light-aligned areas m_poissonScaling [i] = m_resultLightBB [i].Diff().Xy / (100f * (float)Math.Pow(3.0, i - 1)); } // Combine all splits' BB into one and extend it to include shadow casters closer to light SSAABB castersLightBB = new SSAABB(float.PositiveInfinity, float.NegativeInfinity); for (int i = 0; i < c_numberOfSplits; ++i) { castersLightBB.Combine(ref m_resultLightBB [i]); } // extend Z of the AABB to cover shadow-casters closer to the light foreach (var obj in objects) { if (obj.renderState.toBeDeleted || obj.localBoundingSphereRadius <= 0f || !obj.renderState.visible || !obj.renderState.castsShadow) { continue; } else { Vector3 lightAlignedPos = Vector3.Transform(obj.worldBoundingSphereCenter, lightTransform); Vector3 rad = new Vector3(obj.worldBoundingSphereRadius); Vector3 localMin = lightAlignedPos - rad; Vector3 localMax = lightAlignedPos + rad; if (localMin.Z < castersLightBB.Min.Z) { if (OpenTKHelper.RectsOverlap(castersLightBB.Min.Xy, castersLightBB.Max.Xy, localMin.Xy, localMax.Xy)) { castersLightBB.Min.Z = localMin.Z; } } } } // Generate frustum culler from the BB extended towards light to include shadow casters Matrix4 frustumView, frustumProj; viewProjFromLightAlignedBB(ref castersLightBB, ref lightTransform, ref lightY, out frustumView, out frustumProj); Matrix4 frustumMatrix = frustumView * frustumProj; FrustumCuller = new FrustumCuller(ref frustumMatrix); }
private void init() { _frustumCuller = new FrustumCuller(); if (_command == null) { _command = new CommandBuffer(); } if (_compute == null) { _compute = new ComputeBuffer(24, 16); PosCol16[] points = new PosCol16[24]; points[0] = new PosCol16() { Position = new Vector3(-factor, -factor, -factor), Color = new Color32(255, 255, 255, 255), }; points[1] = new PosCol16() { Position = new Vector3(-factor, factor, -factor), Color = new Color32(255, 255, 255, 255), }; points[2] = new PosCol16() { Position = new Vector3(factor, factor, -factor), Color = new Color32(255, 255, 255, 255), }; points[3] = new PosCol16() { Position = new Vector3(factor, -factor, -factor), Color = new Color32(255, 255, 255, 255), }; points[4] = new PosCol16() { Position = new Vector3(-factor, factor, -factor), Color = new Color32(255, 255, 255, 255), }; points[5] = new PosCol16() { Position = new Vector3(factor, factor, -factor), Color = new Color32(255, 255, 255, 255), }; points[6] = new PosCol16() { Position = new Vector3(factor, -factor, -factor), Color = new Color32(255, 255, 255, 255), }; points[7] = new PosCol16() { Position = new Vector3(-factor, -factor, -factor), Color = new Color32(255, 255, 255, 255), }; points[8] = new PosCol16() { Position = new Vector3(-factor, -factor, factor), Color = new Color32(255, 255, 255, 255), }; points[9] = new PosCol16() { Position = new Vector3(-factor, factor, factor), Color = new Color32(255, 255, 255, 255), }; points[10] = new PosCol16() { Position = new Vector3(factor, factor, factor), Color = new Color32(255, 255, 255, 255), }; points[11] = new PosCol16() { Position = new Vector3(factor, -factor, factor), Color = new Color32(255, 255, 255, 255), }; points[12] = new PosCol16() { Position = new Vector3(-factor, factor, factor), Color = new Color32(255, 255, 255, 255), }; points[13] = new PosCol16() { Position = new Vector3(factor, factor, factor), Color = new Color32(255, 255, 255, 255), }; points[14] = new PosCol16() { Position = new Vector3(factor, -factor, factor), Color = new Color32(255, 255, 255, 255), }; points[15] = new PosCol16() { Position = new Vector3(-factor, -factor, factor), Color = new Color32(255, 255, 255, 255), }; points[16] = new PosCol16() { Position = new Vector3(-factor, -factor, -factor), Color = new Color32(255, 255, 255, 255), }; points[17] = new PosCol16() { Position = new Vector3(-factor, -factor, factor), Color = new Color32(255, 255, 255, 255), }; points[18] = new PosCol16() { Position = new Vector3(-factor, factor, -factor), Color = new Color32(255, 255, 255, 255), }; points[19] = new PosCol16() { Position = new Vector3(-factor, factor, factor), Color = new Color32(255, 255, 255, 255), }; points[20] = new PosCol16() { Position = new Vector3(factor, -factor, -factor), Color = new Color32(255, 255, 255, 255), }; points[21] = new PosCol16() { Position = new Vector3(factor, -factor, factor), Color = new Color32(255, 255, 255, 255), }; points[22] = new PosCol16() { Position = new Vector3(factor, factor, -factor), Color = new Color32(255, 255, 255, 255), }; points[23] = new PosCol16() { Position = new Vector3(factor, factor, factor), Color = new Color32(255, 255, 255, 255), }; _compute.SetData(points); if (_matProp == null) { _matProp = new MaterialPropertyBlock(); } _matProp.SetBuffer("_Vertices", _compute); } if (_mat == null) { _mat = new Material(Shader.Find("UnityPIC/CB_Unlit")); } if (_matProp == null) { _matProp = new MaterialPropertyBlock(); _matProp.SetBuffer("_Vertices", _compute); } }
private void ComputeProjections(List <SSObject> objects, SSLightBase light, Matrix4 cameraView, Matrix4 cameraProj) { if (light.GetType() != typeof(SSDirectionalLight)) { throw new NotSupportedException(); } SSDirectionalLight dirLight = (SSDirectionalLight)light; // light-aligned unit vectors Vector3 lightZ = dirLight.Direction.Normalized(); Vector3 lightX, lightY; OpenTKHelper.TwoPerpAxes(lightZ, out lightX, out lightY); // transform matrix from regular space into light aligned space Matrix4 lightTransform = new Matrix4( lightX.X, lightX.Y, lightX.Z, 0f, lightY.X, lightY.Y, lightY.Z, 0f, lightZ.X, lightZ.Y, lightZ.Z, 0f, 0f, 0f, 0f, 0f ); // Find AABB of frustum corners in light coordinates Matrix4 cameraViewProj = cameraView * cameraProj; SSAABB frustumLightBB = SSAABB.FromFrustum(ref lightTransform, ref cameraViewProj); bool shrink = false; SSAABB objsLightBB = new SSAABB(float.PositiveInfinity, float.NegativeInfinity); #if true // (optional) scene dependent optimization // Trim the light-bounding box by the shadow receivers (only in light-space x,y,maxz) FrustumCuller cameraFrustum = new FrustumCuller(ref cameraViewProj); foreach (var obj in objects) { // pass through all shadow casters and receivers if (obj.renderState.toBeDeleted || obj.localBoundingSphereRadius <= 0f || !obj.renderState.visible || !obj.renderState.receivesShadows) { continue; } else if (cameraFrustum.isSphereInsideFrustum(obj.worldBoundingSphere)) { // determine AABB in light coordinates of the objects so far shrink = true; Vector3 lightAlignedPos = Vector3.Transform(obj.worldBoundingSphereCenter, lightTransform); Vector3 rad = new Vector3(obj.worldBoundingSphereRadius); Vector3 localMin = lightAlignedPos - rad; Vector3 localMax = lightAlignedPos + rad; objsLightBB.UpdateMin(localMin); objsLightBB.UpdateMax(localMax); } } #endif // Optimize the light-frustum-projection bounding box by the object-bounding-box SSAABB resultLightBB = new SSAABB(float.PositiveInfinity, float.NegativeInfinity); if (shrink) { // shrink the XY & far-Z coordinates.. resultLightBB.Min.Xy = Vector2.ComponentMax(frustumLightBB.Min.Xy, objsLightBB.Min.Xy); resultLightBB.Min.Z = objsLightBB.Min.Z; resultLightBB.Max = Vector3.ComponentMin(frustumLightBB.Max, objsLightBB.Max); } else { resultLightBB = frustumLightBB; } // View and projection matrices, used by the scene later viewProjFromLightAlignedBB(ref resultLightBB, ref lightTransform, ref lightY, out m_shadowViewMatrix, out m_shadowProjMatrix); // Now extend Z of the result AABB to cover shadow-casters closer to the light inside the // original box foreach (var obj in objects) { if (obj.renderState.toBeDeleted || obj.localBoundingSphereRadius <= 0f || !obj.renderState.visible || !obj.renderState.castsShadow) { continue; } Vector3 lightAlignedPos = Vector3.Transform(obj.worldBoundingSphereCenter, lightTransform); Vector3 rad = new Vector3(obj.worldBoundingSphereRadius); Vector3 localMin = lightAlignedPos - rad; if (localMin.Z < resultLightBB.Min.Z) { Vector3 localMax = lightAlignedPos + rad; if (OpenTKHelper.RectsOverlap(resultLightBB.Min.Xy, resultLightBB.Max.Xy, localMin.Xy, localMax.Xy)) { resultLightBB.Min.Z = localMin.Z; } } } // Generate frustum culler from the BB extended towards light to include shadow casters Matrix4 frustumView, frustumProj; viewProjFromLightAlignedBB(ref resultLightBB, ref lightTransform, ref lightY, out frustumView, out frustumProj); Matrix4 frustumMatrix = frustumView * frustumProj; FrustumCuller = new FrustumCuller(ref frustumMatrix); }