public GBufferRenderer(IGraphicsService graphicsService, SceneNodeRenderer sceneNodeRenderer, DecalRenderer decalRenderer) { _clearGBufferRenderer = new ClearGBufferRenderer(graphicsService); _sceneNodeRenderer = sceneNodeRenderer; _decalRenderer = decalRenderer; // This filter is used to downsample the depth buffer (GBuffer0). _downsampleFilter = PostProcessHelper.GetDownsampleFilter(graphicsService); }
/// <summary> /// Initializes a new instance of the <see cref="ShadowMapRenderer"/> class using the specified /// scene node renderer. /// </summary> /// <param name="sceneNodeRenderer"> /// The renderer for shadow-casting objects. A <see cref="RenderCallback"/> is created /// automatically which calls the specified renderer. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="sceneNodeRenderer"/> is <see langword="null"/>. /// </exception> public ShadowMapRenderer(SceneNodeRenderer sceneNodeRenderer) { if (sceneNodeRenderer == null) { throw new ArgumentNullException("sceneNodeRenderer"); } _renderCallback = context => { var query = context.Scene.Query <ShadowCasterQuery>(context.CameraNode, context); if (query.ShadowCasters.Count == 0) { return(false); } sceneNodeRenderer.Render(query.ShadowCasters, context); return(true); }; AddDefaultRenderers(_renderCallback); }
/// <summary> /// Initializes a new instance of the <see cref="ShadowMapRenderer"/> class using the specified /// scene node renderer. /// </summary> /// <param name="sceneNodeRenderer"> /// The renderer for shadow-casting objects. A <see cref="RenderCallback"/> is created /// automatically which calls the specified renderer. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="sceneNodeRenderer"/> is <see langword="null"/>. /// </exception> public ShadowMapRenderer(SceneNodeRenderer sceneNodeRenderer) { if (sceneNodeRenderer == null) throw new ArgumentNullException("sceneNodeRenderer"); _renderCallback = context => { var query = context.Scene.Query<ShadowCasterQuery>(context.CameraNode, context); if (query.ShadowCasters.Count == 0) return false; sceneNodeRenderer.Render(query.ShadowCasters, context); return true; }; AddDefaultRenderers(_renderCallback); }
public void Render(IList<SceneNode> occluders, LightNode lightNode, SceneNodeRenderer renderer, RenderContext context) { if (context == null) throw new ArgumentNullException("context"); context.ThrowIfCameraMissing(); // ----- Sort occluders by type: IOcclusionProxy vs. SceneNode SortOccluders(occluders, renderer, context); Statistics.Occluders = _occlusionProxies.Count + _sceneNodes.Count; // ----- Update all IOcclusionProxy in background. if (_occlusionProxies.Count > 0) { if (EnableMultithreading) _updateTask = Parallel.Start(_updateOcclusionProxies); else UpdateOcclusionProxies(); } // ----- Backup render state. var originalRenderTarget = context.RenderTarget; var originalViewport = context.Viewport; var graphicsService = context.GraphicsService; var graphicsDevice = graphicsService.GraphicsDevice; var originalRenderState = new RenderStateSnapshot(graphicsDevice); // ----- Camera properties var cameraNode = context.CameraNode; Matrix cameraView = (Matrix)cameraNode.View; var cameraProjection = cameraNode.Camera.Projection; Matrix cameraViewProjection = cameraView * cameraProjection; if (lightNode == null) { _lightHzbAvailable = false; } else { // ----- Render light HZB. _lightHzbAvailable = true; var shadow = lightNode.Shadow as CascadedShadow; if (shadow == null) throw new ArgumentException("LightNode expected to have a CascadedShadow.", "lightNode"); // Set up orthographic camera similar to CascadedShadowMapRenderer. context.CameraNode = _orthographicCameraNode; // Part of camera frustum covered by shadow map. var maxShadowDistance = shadow.Distances[shadow.NumberOfCascades - 1]; _splitVolume.SetFieldOfView(cameraProjection.FieldOfViewY, cameraProjection.AspectRatio, cameraProjection.Near, Math.Min(cameraProjection.Far, maxShadowDistance)); // Find the bounding sphere of the camera frustum. Vector3F center; float radius; GetBoundingSphere(_splitVolume, out center, out radius); Matrix33F orientation = lightNode.PoseWorld.Orientation; Vector3F lightBackward = orientation.GetColumn(2); var orthographicProjection = (OrthographicProjection)_orthographicCameraNode.Camera.Projection; // Create a tight orthographic frustum around the cascade's bounding sphere. orthographicProjection.SetOffCenter(-radius, radius, -radius, radius, 0, 2 * radius); center = cameraNode.PoseWorld.ToWorldPosition(center); Vector3F cameraPosition = center + radius * lightBackward; Pose frustumPose = new Pose(cameraPosition, orientation); // For rendering the shadow map, move near plane back by MinLightDistance // to catch occluders in front of the cascade. orthographicProjection.Near = -shadow.MinLightDistance; _orthographicCameraNode.PoseWorld = frustumPose; Pose lightView = frustumPose.Inverse; Matrix lightViewProjection = (Matrix)lightView * orthographicProjection; _parameterCameraViewProj.SetValue(lightViewProjection); _parameterCameraNear.SetValue(orthographicProjection.Near); _parameterCameraFar.SetValue(orthographicProjection.Far); RenderOccluders(renderer, context); CreateDepthHierarchy(_lightHzb, context); // Set effect parameters for use in Query(). _lightAabb = _orthographicCameraNode.Aabb; _parameterLightViewProj.SetValue(lightViewProjection); _parameterLightToCamera.SetValue(Matrix.Invert(lightViewProjection) * cameraViewProjection); context.CameraNode = cameraNode; } // ----- Render camera HZB. // Set camera parameters. (These effect parameters are also needed in Query()!) _cameraAabb = cameraNode.Aabb; _parameterCameraViewProj.SetValue(cameraViewProjection); _parameterCameraNear.SetValue(cameraProjection.Near); _parameterCameraFar.SetValue(cameraProjection.Far); var lodCameraNode = context.LodCameraNode; if (lodCameraNode != null) { // Enable distance culling. _parameterCameraPosition.SetValue((Vector3)lodCameraNode.PoseWorld.Position); float yScale = Math.Abs(lodCameraNode.Camera.Projection.ToMatrix44F().M11); _parameterNormalizationFactor.SetValue(1.0f / yScale * cameraNode.LodBias * context.LodBias); } else { // Disable distance culling. _parameterCameraPosition.SetValue(new Vector3()); _parameterNormalizationFactor.SetValue(0); } RenderOccluders(renderer, context); CreateDepthHierarchy(_cameraHzb, context); _sceneNodes.Clear(); _occlusionProxies.Clear(); // Restore render state. graphicsDevice.SetRenderTarget(null); originalRenderState.Restore(); context.RenderTarget = originalRenderTarget; context.Viewport = originalViewport; }
public void Render(IList<SceneNode> occluders, SceneNodeRenderer renderer, RenderContext context) { Render(occluders, null, renderer, context); }
/// <summary> /// Sorts occluders by type (<see cref="IOcclusionProxy"/> vs. <see cref="SceneNode"/>). /// </summary> /// <param name="nodes">The nodes.</param> /// <param name="renderer">The renderer.</param> /// <param name="context">The context.</param> private void SortOccluders(IList<SceneNode> nodes, SceneNodeRenderer renderer, RenderContext context) { Debug.Assert(_occlusionProxies.Count == 0, "The list of IOcclusionProxy has not been cleared."); Debug.Assert(_sceneNodes.Count == 0, "The list of SceneNodes has not been cleared."); if (nodes == null) return; int numberOfNodes = nodes.Count; if (renderer == null) { // Search only for IOcclusionProxy. for (int i = 0; i < numberOfNodes; i++) { var node = nodes[i]; if (node == null) continue; var occlusionProxy = node as IOcclusionProxy; if (occlusionProxy != null && occlusionProxy.HasOccluder) _occlusionProxies.Add(occlusionProxy); } } else { // Search for IOcclusionProxy and renderable scene nodes. for (int i = 0; i < numberOfNodes; i++) { var node = nodes[i]; if (node == null) continue; var occlusionProxy = node as IOcclusionProxy; if (occlusionProxy != null && occlusionProxy.HasOccluder) _occlusionProxies.Add(occlusionProxy); else if (renderer.CanRender(node, context)) _sceneNodes.Add(node); } } }
/// <summary> /// Renders the occluders. /// </summary> /// <param name="renderer"> /// Optional: A <see cref="SceneNodeRenderer"/> for rendering custom scene nodes into the /// occlusion buffer. /// </param> /// <param name="context">The context.</param> private void RenderOccluders(SceneNodeRenderer renderer, RenderContext context) { // ----- Clear occlusion buffer. var graphicsService = context.GraphicsService; var graphicsDevice = graphicsService.GraphicsDevice; graphicsDevice.SetRenderTarget(_hzbLevels[0]); context.RenderTarget = _hzbLevels[0]; context.Viewport = graphicsDevice.Viewport; graphicsDevice.Clear(Color.White); // ----- Render scene nodes using custom scene node renderer. if (renderer != null && _sceneNodes.Count > 0) { graphicsDevice.DepthStencilState = DepthStencilState.Default; graphicsDevice.RasterizerState = RasterizerState.CullNone; graphicsDevice.BlendState = BlendState.Opaque; renderer.Render(_sceneNodes, context); } // ----- Render all IOcclusionProxy. if (EnableMultithreading) _updateTask.Wait(); graphicsDevice.DepthStencilState = DepthStencilState.Default; graphicsDevice.RasterizerState = RasterizerState.CullNone; graphicsDevice.BlendState = BlendState.Opaque; _effect.CurrentTechnique = _techniqueOccluder; _techniqueOccluder.Passes[0].Apply(); // Go through list of occluders and submit triangles to render batch. int numberOfOccluders = _occlusionProxies.Count; for (int i = 0; i < numberOfOccluders; i++) { var data = _occlusionProxies[i].GetOccluder(); int vertexBufferIndex, indexBufferIndex; _renderBatch.Submit(PrimitiveType.TriangleList, data.Vertices.Length, data.Indices.Length, out vertexBufferIndex, out indexBufferIndex); // Copy triangle vertices. data.Vertices.CopyTo(_renderBatch.Vertices, vertexBufferIndex); // Adjust and copy triangle indices. for (int j = 0; j < data.Indices.Length; j++, indexBufferIndex++) _renderBatch.Indices[indexBufferIndex] = (ushort)(vertexBufferIndex + data.Indices[j]); } _renderBatch.Flush(); }