public OcclusionCullingScreen(IServiceLocator services) : base(services) { _sceneNodes = new List<SceneNode>(); // Create new occlusion buffer with default settings. OcclusionBuffer = new OcclusionBuffer(GraphicsService); OcclusionBuffer.ProgressiveShadowCasterCulling = true; EnableCulling = true; // Create a second camera for rendering a top-down view of the scene. var topDownPerspective = new PerspectiveProjection(); topDownPerspective.SetFieldOfView(MathHelper.ToRadians(90), 1, 1, 512); _topDownCameraNode = new CameraNode(new Camera(topDownPerspective)); _topDownCameraNode.PoseWorld = new Pose(new Vector3F(-10, 120, -10)); _topDownCameraNode.LookAt(new Vector3F(-10, 0, -10), Vector3F.UnitZ); _sceneQuery = new CustomSceneQuery(); _debugRenderer = new DebugRenderer(GraphicsService, null); // The DigitalRune Profiler is used to measure execution times. Profiler.SetFormat("Occlusion.Render", 1e3f, "[ms]"); Profiler.SetFormat("Occlusion.Query", 1e3f, "[ms]"); }
public OcclusionCullingScreen(IServiceLocator services) : base(services) { _sceneNodes = new List <SceneNode>(); // Create new occlusion buffer with default settings. OcclusionBuffer = new OcclusionBuffer(GraphicsService); OcclusionBuffer.ProgressiveShadowCasterCulling = true; EnableCulling = true; // Create a second camera for rendering a top-down view of the scene. var topDownPerspective = new PerspectiveProjection(); topDownPerspective.SetFieldOfView(MathHelper.ToRadians(90), 1, 1, 512); _topDownCameraNode = new CameraNode(new Camera(topDownPerspective)); _topDownCameraNode.PoseWorld = new Pose(new Vector3(-10, 120, -10)); _topDownCameraNode.LookAt(new Vector3(-10, 0, -10), Vector3.UnitZ); _sceneQuery = new CustomSceneQuery(); _debugRenderer = new DebugRenderer(GraphicsService, null); // The DigitalRune Profiler is used to measure execution times. Profiler.SetFormat("Occlusion.Render", 1e3f, "[ms]"); Profiler.SetFormat("Occlusion.Query", 1e3f, "[ms]"); }
private static bool IsCameraUnderwater(CustomSceneQuery query, CameraNode cameraNode) { var cameraPosition = cameraNode.PoseWorld.Position; foreach (var node in query.RenderableNodes) { var waterNode = node as WaterNode; if (waterNode != null && waterNode.IsUnderwater(cameraPosition)) { return(true); } } return(false); }
protected internal void RenderScene(CustomSceneQuery sceneQuery, RenderContext context, bool doPostProcessing, bool renderLensFlares, bool renderDebugOutput, bool renderReticle) { var renderTargetPool = GraphicsService.RenderTargetPool; var graphicsDevice = GraphicsService.GraphicsDevice; var originalRenderTarget = context.RenderTarget; var originalViewport = context.Viewport; var originalSourceTexture = context.SourceTexture; // All intermediate render targets have the size of the target viewport. int width = context.Viewport.Width; int height = context.Viewport.Height; context.Viewport = new Viewport(0, 0, width, height); // The render context can be used to share any data, for example: // Store a shared RebuildZBufferRenderer in the context. context.Data[RenderContextKeys.RebuildZBufferRenderer] = _rebuildZBufferRenderer; // ----- G-Buffer Pass // The GBufferRenderer creates context.GBuffer0 and context.GBuffer1. _gBufferRenderer.Render(sceneQuery.RenderableNodes, sceneQuery.DecalNodes, context); // ----- Shadow Pass // The ShadowMapRenderer renders the shadow maps which are stored in the light nodes. context.RenderPass = "******"; ShadowMapRenderer.Render(sceneQuery.Lights, context); context.RenderPass = null; // The ShadowMaskRenderer renders the shadows and stores them in one or more render // targets ("shadows masks"). ShadowMaskRenderer.Render(sceneQuery.Lights, context); RecycleShadowMaps(sceneQuery.Lights); // ----- Light Buffer Pass // The LightBufferRenderer creates context.LightBuffer0 (diffuse light) and // context.LightBuffer1 (specular light). LightBufferRenderer.Render(sceneQuery.Lights, context); // Normally, we do not need the shadow masks anymore - except if we want to // display them for debugging. if (!VisualizeIntermediateRenderTargets) { ShadowMaskRenderer.RecycleShadowMasks(); } // ----- Material Pass if (DebugMode == DeferredGraphicsDebugMode.None) { // In the material pass we render all meshes and decals into a single full-screen // render target. The shaders combine the material properties (diffuse texture, etc.) // with the light buffer info. context.RenderTarget = renderTargetPool.Obtain2D(new RenderTargetFormat(width, height, false, SurfaceFormat.HdrBlendable, DepthFormat.Depth24Stencil8)); graphicsDevice.SetRenderTarget(context.RenderTarget); context.Viewport = graphicsDevice.Viewport; graphicsDevice.Clear(new Color(3, 3, 3, 255)); graphicsDevice.DepthStencilState = DepthStencilState.Default; graphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise; graphicsDevice.BlendState = BlendState.Opaque; context.RenderPass = "******"; _opaqueMeshSceneRenderer.Render(sceneQuery.RenderableNodes, context); _decalRenderer.Render(sceneQuery.DecalNodes, context); context.RenderPass = null; } else { // For debugging: // Ignore the material pass. Keep rendering into one of the light buffers // to visualize only the lighting results. if (DebugMode == DeferredGraphicsDebugMode.VisualizeDiffuseLightBuffer) { context.RenderTarget = context.LightBuffer0; } else { context.RenderTarget = context.LightBuffer1; } } // The meshes rendered in the last step might use additional floating-point // textures (e.g. the light buffers) in the different graphics texture stages. // We reset the texture stages (setting all GraphicsDevice.Textures to null), // otherwise XNA might throw exceptions. graphicsDevice.ResetTextures(); // ----- Occlusion Queries if (renderLensFlares) { _lensFlareRenderer.UpdateOcclusion(sceneQuery.LensFlareNodes, context); } // ----- Sky _skyRenderer.Render(sceneQuery.SkyNodes, context); // ----- Fog _fogRenderer.Render(sceneQuery.FogNodes, context); // ----- Forward Rendering of Alpha-Blended Meshes and Particles graphicsDevice.DepthStencilState = DepthStencilState.DepthRead; graphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise; graphicsDevice.BlendState = BlendState.AlphaBlend; context.RenderPass = "******"; AlphaBlendSceneRenderer.Render(sceneQuery.RenderableNodes, context, RenderOrder.BackToFront); context.RenderPass = null; graphicsDevice.ResetTextures(); renderTargetPool.Recycle(context.SourceTexture); context.SourceTexture = null; _underwaterPostProcessor.Enabled = IsCameraUnderwater(sceneQuery, context.CameraNode); // ----- Post Processors context.SourceTexture = context.RenderTarget; context.RenderTarget = originalRenderTarget; context.Viewport = originalViewport; if (doPostProcessing) { // The post-processors modify the scene image and the result is written into // the final render target - which is usually the back buffer (but this could // also be another off-screen render target used in another graphics screen). PostProcessors.Process(context); } else { // Only copy the current render target to the final render target without post-processing. graphicsDevice.SetRenderTarget(originalRenderTarget); graphicsDevice.Viewport = originalViewport; SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone); SpriteBatch.Draw(context.SourceTexture, new Rectangle(0, 0, originalViewport.Width, originalViewport.Height), Color.White); SpriteBatch.End(); } renderTargetPool.Recycle((RenderTarget2D)context.SourceTexture); context.SourceTexture = null; // ----- Lens Flares if (renderLensFlares) { _lensFlareRenderer.Render(sceneQuery.LensFlareNodes, context); } // ----- Debug Output if (renderDebugOutput) { // ----- Optional: Restore the Z-Buffer // Currently, the hardware depth buffer is not initialized with useful data because // every time we change the render target, XNA deletes the depth buffer. If we want // the debug rendering to use correct depth buffer, we can restore the depth buffer // using the RebuildZBufferRenderer. If we remove this step, then the DebugRenderer // graphics will overlay the whole 3D scene. _rebuildZBufferRenderer.Render(context, true); // Render debug info added by game objects. DebugRenderer.Render(context); // Render intermediate render targets for debugging. // We do not use the public DebugRenderer here because the public DebugRenderer // might not be cleared every frame (the game logic can choose how it wants to // use the public renderer). if (VisualizeIntermediateRenderTargets) { _internalDebugRenderer.DrawTexture(context.GBuffer0, new Rectangle(0, 0, 200, 200)); _internalDebugRenderer.DrawTexture(context.GBuffer1, new Rectangle(200, 0, 200, 200)); _internalDebugRenderer.DrawTexture(context.LightBuffer0, new Rectangle(400, 0, 200, 200)); _internalDebugRenderer.DrawTexture(context.LightBuffer1, new Rectangle(600, 0, 200, 200)); for (int i = 0; i < ShadowMaskRenderer.ShadowMasks.Count; i++) { var shadowMask = ShadowMaskRenderer.ShadowMasks[i]; if (shadowMask != null) { _internalDebugRenderer.DrawTexture(shadowMask, new Rectangle((i) * 200, 200, 200, 200)); } } _internalDebugRenderer.Render(context); _internalDebugRenderer.Clear(); } } // ----- Draw Reticle if (renderReticle && _sampleFramework.IsGuiVisible) { SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend); SpriteBatch.Draw( _reticle, new Vector2(originalViewport.Width / 2 - _reticle.Width / 2, originalViewport.Height / 2 - _reticle.Height / 2), Color.Black); SpriteBatch.End(); } // ----- Clean-up // It is very important to give every intermediate render target back to the // render target pool! renderTargetPool.Recycle(context.GBuffer0); context.GBuffer0 = null; renderTargetPool.Recycle(context.GBuffer1); context.GBuffer1 = null; renderTargetPool.Recycle((RenderTarget2D)context.Data[RenderContextKeys.DepthBufferHalf]); context.Data.Remove(RenderContextKeys.DepthBufferHalf); if (DebugMode != DeferredGraphicsDebugMode.VisualizeDiffuseLightBuffer) { renderTargetPool.Recycle(context.LightBuffer0); } context.LightBuffer0 = null; if (DebugMode != DeferredGraphicsDebugMode.VisualizeSpecularLightBuffer) { renderTargetPool.Recycle(context.LightBuffer1); } context.LightBuffer1 = null; ShadowMaskRenderer.RecycleShadowMasks(); context.Data.Remove(RenderContextKeys.RebuildZBufferRenderer); context.SourceTexture = originalSourceTexture; }
// Renders the graphics screen. - This method is called in GraphicsManager.Render(). protected override void OnRender(RenderContext context) { if (ActiveCameraNode == null) { return; } // Our scene and the camera must be set in the render context. This info is // required by many renderers. context.Scene = Scene; context.CameraNode = ActiveCameraNode; // LOD (level of detail) settings are also specified in the context. context.LodCameraNode = ActiveCameraNode; context.LodHysteresis = 0.5f; context.LodBias = EnableLod ? 1.0f : 0.0f; context.LodBlendingEnabled = false; // ----- Preprocessing // For some scene nodes we have to update some off-screen render targets before the // actual scene is rendered. // // We only have to do this for the scene nodes which are visible // by the camera frustum: var preprocessingQuery = Scene.Query <PreprocessingSceneQuery>(context.CameraNode, context); #if !XBOX360 // TODO: _terrainClipmapRenderer.Render(preprocessingQuery.TerrainNodes, context); #endif // Generate cloud maps. // Only necessary if LayeredCloudMaps are used. If the cloud maps are static // and the settings do not change, it is not necessary to generate the // cloud maps in every frame. But in the SkySample we use animated cloud maps. // The CloudMapRenderer can be called several times per frame, it will only // do the work once per frame. // See also SkySample. _cloudMapRenderer.Render(preprocessingQuery.CloudLayerNodes, context); // Compute ocean waves. // Only necessary if WaterNodes with OceanWaves are used. _waterWavesRenderer.Render(preprocessingQuery.WaterNodes, context); // Perform render-to-texture operations. // Only necessary if SceneCaptureNodes are used. // See also SceneCapture2DSample. SceneCaptureRenderer.Render(preprocessingQuery.SceneCaptureNodes, context); // Render reflections. // Only necessary if PlanarReflectionNodes are used. // See also PlanarReflectionSample. _planarReflectionRenderer.Render(preprocessingQuery.PlanarReflectionNodes, context); // ----- Scene Rendering // Get all scene nodes which overlap the camera frustum. CustomSceneQuery sceneQuery = Scene.Query <CustomSceneQuery>(context.CameraNode, context); // Render the scene nodes of the sceneQuery. RenderScene(sceneQuery, context, true, true, true, DrawReticle); // ----- Clean-up context.Scene = null; context.CameraNode = null; context.LodCameraNode = null; context.LodHysteresis = 0; }
//-------------------------------------------------------------- #region Creation & Cleanup //-------------------------------------------------------------- public DeferredGraphicsScreen(IServiceLocator services) : base(services.GetInstance <IGraphicsService>()) { _sampleFramework = services.GetInstance <SampleFramework>(); var contentManager = services.GetInstance <ContentManager>(); SpriteBatch = GraphicsService.GetSpriteBatch(); // Let's create the necessary scene node renderers: #if !XBOX360 TerrainRenderer = new TerrainRenderer(GraphicsService); #endif MeshRenderer = new MeshRenderer(); // The _opaqueMeshSceneRenderer combines all renderers for opaque // (= not alpha blended) meshes. _opaqueMeshSceneRenderer = new SceneRenderer(); #if !XBOX360 _opaqueMeshSceneRenderer.Renderers.Add(TerrainRenderer); #endif _opaqueMeshSceneRenderer.Renderers.Add(MeshRenderer); _decalRenderer = new DecalRenderer(GraphicsService); _billboardRenderer = new BillboardRenderer(GraphicsService, 2048) { EnableSoftParticles = true, // If you have an extreme amount of particles that cover the entire screen, // you can turn on offscreen rendering to improve performance. //EnableOffscreenRendering = true, }; // The AlphaBlendSceneRenderer combines all renderers for transparent // (= alpha blended) objects. AlphaBlendSceneRenderer = new SceneRenderer(); AlphaBlendSceneRenderer.Renderers.Add(MeshRenderer); AlphaBlendSceneRenderer.Renderers.Add(_billboardRenderer); AlphaBlendSceneRenderer.Renderers.Add(new WaterRenderer(GraphicsService)); AlphaBlendSceneRenderer.Renderers.Add(new FogSphereRenderer(GraphicsService)); AlphaBlendSceneRenderer.Renderers.Add(new VolumetricLightRenderer(GraphicsService)); #if !XBOX360 // Update terrain clipmaps. (Only necessary if TerrainNodes are used.) _terrainClipmapRenderer = new TerrainClipmapRenderer(GraphicsService); #endif // Renderer for cloud maps. (Only necessary if LayeredCloudMaps are used.) _cloudMapRenderer = new CloudMapRenderer(GraphicsService); // Renderer for SceneCaptureNodes. See also SceneCapture2DSample. // In the constructor we specify a method which is called in SceneCaptureRenderer.Render() // when the scene must be rendered for the SceneCaptureNodes. SceneCaptureRenderer = new SceneCaptureRenderer(context => { // Get scene nodes which are visible by the current camera. CustomSceneQuery sceneQuery = Scene.Query <CustomSceneQuery>(context.CameraNode, context); // Render scene (with post-processing, with lens flares, no debug rendering, no reticle). RenderScene(sceneQuery, context, true, true, false, false); }); // Renderer for PlanarReflectionNodes. See also PlanarReflectionSample. // In the constructor we specify a method which is called in PlanarReflectionRenderer.Render() // to create the reflection images. _planarReflectionRenderer = new PlanarReflectionRenderer(context => { // Get scene nodes which are visible by the current camera. CustomSceneQuery sceneQuery = Scene.Query <CustomSceneQuery>(context.CameraNode, context); var planarReflectionNode = (PlanarReflectionNode)context.ReferenceNode; // Planar reflections are often for WaterNodes. These nodes should not be rendered // into their own reflection map because when the water surface is displaced by waves, // some waves could be visible in the reflection. // --> Remove the water node from the renderable nodes. (In our samples, the water // node is the parent of the reflection node.) if (planarReflectionNode.Parent is WaterNode) { var index = sceneQuery.RenderableNodes.IndexOf(planarReflectionNode.Parent); if (index >= 0) { sceneQuery.RenderableNodes[index] = null; } } // Render scene (no post-processing, no lens flares, no debug rendering, no reticle). RenderScene(sceneQuery, context, false, false, false, false); }); _waterWavesRenderer = new WaterWavesRenderer(GraphicsService); // The shadow map renderer renders a depth image from the viewpoint of the light and // stores it in LightNode.Shadow.ShadowMap. ShadowMapRenderer = new ShadowMapRenderer(context => { var query = context.Scene.Query <ShadowCasterQuery>(context.CameraNode, context); if (query.ShadowCasters.Count == 0) { return(false); } _opaqueMeshSceneRenderer.Render(query.ShadowCasters, context); return(true); }); // The shadow mask renderer evaluates the shadow maps, does shadow filtering // and stores the resulting shadow factor in a screen space image //(see LightNode.Shadow.ShadowMask/ShadowMaskChannel). ShadowMaskRenderer = new ShadowMaskRenderer(GraphicsService, 2); // Optionally, we can blur the shadow mask to make the shadows smoother. var blur = new Blur(GraphicsService) { IsAnisotropic = false, IsBilateral = true, EdgeSoftness = 0.05f, Scale = 1f, Enabled = false, // Disable blur by default. }; blur.InitializeGaussianBlur(11, 3, true); ShadowMaskRenderer.Filter = blur; // Renderers which create the intermediate render targets: // Those 2 renderers are implemented in this sample. Those functions could // be implemented directly in this class but we have created separate classes // to make the code more readable. _gBufferRenderer = new GBufferRenderer(GraphicsService, _opaqueMeshSceneRenderer, _decalRenderer); LightBufferRenderer = new LightBufferRenderer(GraphicsService); // Other specialized renderers: _lensFlareRenderer = new LensFlareRenderer(GraphicsService); _skyRenderer = new SkyRenderer(GraphicsService); _fogRenderer = new FogRenderer(GraphicsService); _internalDebugRenderer = new DebugRenderer(GraphicsService, null); _rebuildZBufferRenderer = new RebuildZBufferRenderer(GraphicsService); Scene = new Scene(); // This screen needs a HDR filter to map high dynamic range values back to // low dynamic range (LDR). PostProcessors = new PostProcessorChain(GraphicsService); PostProcessors.Add(new HdrFilter(GraphicsService) { EnableBlueShift = true, BlueShiftCenter = 0.0004f, BlueShiftRange = 0.5f, //BlueShiftColor = new Vector3F(1.05f / 4f, 0.97f / 4f, 1.27f / 4f), // Default physically-based blue-shift BlueShiftColor = new Vector3F(0.25f, 0.25f, 0.7f), // More dramatic blue-shift MinExposure = 0, MaxExposure = 10, BloomIntensity = 1, BloomThreshold = 0.6f, }); _underwaterPostProcessor = new UnderwaterPostProcessor(GraphicsService, contentManager); PostProcessors.Add(_underwaterPostProcessor); // Use 2D texture for reticle. _reticle = contentManager.Load <Texture2D>("Reticle"); // Use the sprite font of the GUI. var uiContentManager = services.GetInstance <ContentManager>("UIContent"); var spriteFont = uiContentManager.Load <SpriteFont>("UI Themes/BlendBlue/Default"); DebugRenderer = new DebugRenderer(GraphicsService, spriteFont) { DefaultColor = new Color(0, 0, 0), DefaultTextPosition = new Vector2F(10), }; EnableLod = true; }
private static bool IsCameraUnderwater(CustomSceneQuery query, CameraNode cameraNode) { var cameraPosition = cameraNode.PoseWorld.Position; foreach (var node in query.RenderableNodes) { var waterNode = node as WaterNode; if (waterNode != null && waterNode.IsUnderwater(cameraPosition)) return true; } return false; }
protected internal void RenderScene(CustomSceneQuery sceneQuery, RenderContext context, bool doPostProcessing, bool renderLensFlares, bool renderDebugOutput, bool renderReticle) { var renderTargetPool = GraphicsService.RenderTargetPool; var graphicsDevice = GraphicsService.GraphicsDevice; var originalRenderTarget = context.RenderTarget; var originalViewport = context.Viewport; var originalSourceTexture = context.SourceTexture; // All intermediate render targets have the size of the target viewport. int width = context.Viewport.Width; int height = context.Viewport.Height; context.Viewport = new Viewport(0, 0, width, height); // The render context can be used to share any data, for example: // Store a shared RebuildZBufferRenderer in the context. context.Data[RenderContextKeys.RebuildZBufferRenderer] = _rebuildZBufferRenderer; // ----- G-Buffer Pass // The GBufferRenderer creates context.GBuffer0 and context.GBuffer1. _gBufferRenderer.Render(sceneQuery.RenderableNodes, sceneQuery.DecalNodes, context); // ----- Shadow Pass // The ShadowMapRenderer renders the shadow maps which are stored in the light nodes. context.RenderPass = "******"; ShadowMapRenderer.Render(sceneQuery.Lights, context); context.RenderPass = null; // The ShadowMaskRenderer renders the shadows and stores them in one or more render // targets ("shadows masks"). ShadowMaskRenderer.Render(sceneQuery.Lights, context); RecycleShadowMaps(sceneQuery.Lights); // ----- Light Buffer Pass // The LightBufferRenderer creates context.LightBuffer0 (diffuse light) and // context.LightBuffer1 (specular light). LightBufferRenderer.Render(sceneQuery.Lights, context); // Normally, we do not need the shadow masks anymore - except if we want to // display them for debugging. if (!VisualizeIntermediateRenderTargets) ShadowMaskRenderer.RecycleShadowMasks(); // ----- Material Pass if (DebugMode == DeferredGraphicsDebugMode.None) { // In the material pass we render all meshes and decals into a single full-screen // render target. The shaders combine the material properties (diffuse texture, etc.) // with the light buffer info. context.RenderTarget = renderTargetPool.Obtain2D(new RenderTargetFormat(width, height, false, SurfaceFormat.HdrBlendable, DepthFormat.Depth24Stencil8)); graphicsDevice.SetRenderTarget(context.RenderTarget); context.Viewport = graphicsDevice.Viewport; graphicsDevice.Clear(new Color(3, 3, 3, 255)); graphicsDevice.DepthStencilState = DepthStencilState.Default; graphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise; graphicsDevice.BlendState = BlendState.Opaque; context.RenderPass = "******"; _opaqueMeshSceneRenderer.Render(sceneQuery.RenderableNodes, context); _decalRenderer.Render(sceneQuery.DecalNodes, context); context.RenderPass = null; } else { // For debugging: // Ignore the material pass. Keep rendering into one of the light buffers // to visualize only the lighting results. if (DebugMode == DeferredGraphicsDebugMode.VisualizeDiffuseLightBuffer) context.RenderTarget = context.LightBuffer0; else context.RenderTarget = context.LightBuffer1; } // The meshes rendered in the last step might use additional floating-point // textures (e.g. the light buffers) in the different graphics texture stages. // We reset the texture stages (setting all GraphicsDevice.Textures to null), // otherwise XNA might throw exceptions. graphicsDevice.ResetTextures(); // ----- Occlusion Queries if (renderLensFlares) _lensFlareRenderer.UpdateOcclusion(sceneQuery.LensFlareNodes, context); // ----- Sky _skyRenderer.Render(sceneQuery.SkyNodes, context); // ----- Fog _fogRenderer.Render(sceneQuery.FogNodes, context); // ----- Forward Rendering of Alpha-Blended Meshes and Particles graphicsDevice.DepthStencilState = DepthStencilState.DepthRead; graphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise; graphicsDevice.BlendState = BlendState.AlphaBlend; context.RenderPass = "******"; AlphaBlendSceneRenderer.Render(sceneQuery.RenderableNodes, context, RenderOrder.BackToFront); context.RenderPass = null; graphicsDevice.ResetTextures(); renderTargetPool.Recycle(context.SourceTexture); context.SourceTexture = null; _underwaterPostProcessor.Enabled = IsCameraUnderwater(sceneQuery, context.CameraNode); // ----- Post Processors context.SourceTexture = context.RenderTarget; context.RenderTarget = originalRenderTarget; context.Viewport = originalViewport; if (doPostProcessing) { // The post-processors modify the scene image and the result is written into // the final render target - which is usually the back buffer (but this could // also be another off-screen render target used in another graphics screen). PostProcessors.Process(context); } else { // Only copy the current render target to the final render target without post-processing. graphicsDevice.SetRenderTarget(originalRenderTarget); graphicsDevice.Viewport = originalViewport; SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone); SpriteBatch.Draw(context.SourceTexture, new Rectangle(0, 0, originalViewport.Width, originalViewport.Height), Color.White); SpriteBatch.End(); } renderTargetPool.Recycle((RenderTarget2D)context.SourceTexture); context.SourceTexture = null; // ----- Lens Flares if (renderLensFlares) _lensFlareRenderer.Render(sceneQuery.LensFlareNodes, context); // ----- Debug Output if (renderDebugOutput) { // ----- Optional: Restore the Z-Buffer // Currently, the hardware depth buffer is not initialized with useful data because // every time we change the render target, XNA deletes the depth buffer. If we want // the debug rendering to use correct depth buffer, we can restore the depth buffer // using the RebuildZBufferRenderer. If we remove this step, then the DebugRenderer // graphics will overlay the whole 3D scene. _rebuildZBufferRenderer.Render(context, true); // Render debug info added by game objects. DebugRenderer.Render(context); // Render intermediate render targets for debugging. // We do not use the public DebugRenderer here because the public DebugRenderer // might not be cleared every frame (the game logic can choose how it wants to // use the public renderer). if (VisualizeIntermediateRenderTargets) { _internalDebugRenderer.DrawTexture(context.GBuffer0, new Rectangle(0, 0, 200, 200)); _internalDebugRenderer.DrawTexture(context.GBuffer1, new Rectangle(200, 0, 200, 200)); _internalDebugRenderer.DrawTexture(context.LightBuffer0, new Rectangle(400, 0, 200, 200)); _internalDebugRenderer.DrawTexture(context.LightBuffer1, new Rectangle(600, 0, 200, 200)); for (int i = 0; i < ShadowMaskRenderer.ShadowMasks.Count; i++) { var shadowMask = ShadowMaskRenderer.ShadowMasks[i]; if (shadowMask != null) _internalDebugRenderer.DrawTexture(shadowMask, new Rectangle((i) * 200, 200, 200, 200)); } _internalDebugRenderer.Render(context); _internalDebugRenderer.Clear(); } } // ----- Draw Reticle if (renderReticle && _sampleFramework.IsGuiVisible) { SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend); SpriteBatch.Draw( _reticle, new Vector2(originalViewport.Width / 2 - _reticle.Width / 2, originalViewport.Height / 2 - _reticle.Height / 2), Color.Black); SpriteBatch.End(); } // ----- Clean-up // It is very important to give every intermediate render target back to the // render target pool! renderTargetPool.Recycle(context.GBuffer0); context.GBuffer0 = null; renderTargetPool.Recycle(context.GBuffer1); context.GBuffer1 = null; renderTargetPool.Recycle((RenderTarget2D)context.Data[RenderContextKeys.DepthBufferHalf]); context.Data.Remove(RenderContextKeys.DepthBufferHalf); if (DebugMode != DeferredGraphicsDebugMode.VisualizeDiffuseLightBuffer) renderTargetPool.Recycle(context.LightBuffer0); context.LightBuffer0 = null; if (DebugMode != DeferredGraphicsDebugMode.VisualizeSpecularLightBuffer) renderTargetPool.Recycle(context.LightBuffer1); context.LightBuffer1 = null; ShadowMaskRenderer.RecycleShadowMasks(); context.Data.Remove(RenderContextKeys.RebuildZBufferRenderer); context.SourceTexture = originalSourceTexture; }
protected override void OnRender(RenderContext context) { // This screen expects two cameras. if (ActiveCameraNode == null || ActiveCameraNodeB == null) { return; } var renderTargetPool = GraphicsService.RenderTargetPool; var graphicsDevice = GraphicsService.GraphicsDevice; var originalRenderTarget = context.RenderTarget; var fullViewport = context.Viewport; // Get a render target for the first camera. Use half the width because we split // the screen horizontally. var format = new RenderTargetFormat(context.RenderTarget) { Width = fullViewport.Width / 2 }; var renderTargetA = renderTargetPool.Obtain2D(format); context.Scene = Scene; context.LodHysteresis = 0.5f; context.LodBias = 1.0f; context.LodBlendingEnabled = true; for (int i = 0; i < 2; i++) { if (i == 0) { // The first camera renders into renderTargetA. context.CameraNode = ActiveCameraNode; context.Viewport = new Viewport(0, 0, fullViewport.Width / 2, fullViewport.Height); context.RenderTarget = renderTargetA; } else { // The second camera renders into the right half of the final render target. context.CameraNode = ActiveCameraNodeB; context.Viewport = new Viewport(fullViewport.X + fullViewport.Width / 2, fullViewport.Y, fullViewport.Width / 2, fullViewport.Height); context.RenderTarget = originalRenderTarget; } context.LodCameraNode = context.CameraNode; // Get all scene nodes which overlap the camera frustum. CustomSceneQuery sceneQuery = Scene.Query <CustomSceneQuery>(context.CameraNode, context); // Render the scene nodes of the sceneQuery. RenderScene(sceneQuery, context, true, true, true, true); // ----- Copy image of first camera. if (i == 1) { // Copy the upper screen from the temporary render target back into the back buffer. context.Viewport = fullViewport; graphicsDevice.Viewport = fullViewport; SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone); SpriteBatch.Draw( renderTargetA, new Rectangle(0, 0, fullViewport.Width / 2, fullViewport.Height), Color.White); SpriteBatch.End(); renderTargetPool.Recycle(renderTargetA); } } // Clean-up context.Scene = null; context.CameraNode = null; context.LodCameraNode = null; context.RenderPass = null; }