internal static UpsampleFilter GetUpsampleFilter(this IGraphicsService graphicsService) { if (graphicsService == null) { throw new ArgumentNullException("graphicsService"); } const string Key = "__UpsampleFilter"; object obj; graphicsService.Data.TryGetValue(Key, out obj); var instance = obj as UpsampleFilter; if (instance == null) { instance = new UpsampleFilter(graphicsService); graphicsService.Data[Key] = instance; } return(instance); }
/// <inheritdoc/> public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order) { if (nodes == null) throw new ArgumentNullException("nodes"); if (context == null) throw new ArgumentNullException("context"); if (order != RenderOrder.UserDefined) throw new NotImplementedException("Render order must be 'UserDefined'."); if (context.CameraNode == null) throw new GraphicsException("Camera node needs to be set in render context."); if (context.GBuffer0 == null) throw new GraphicsException("GBuffer0 needs to be set in render context."); int numberOfNodes = nodes.Count; if (numberOfNodes == 0) return; var graphicsService = context.GraphicsService; var graphicsDevice = graphicsService.GraphicsDevice; var viewport = context.Viewport; int width = viewport.Width; int height = viewport.Height; var renderTargetPool = graphicsService.RenderTargetPool; var cameraNode = context.CameraNode; var projection = cameraNode.Camera.Projection; Pose view = cameraNode.PoseWorld.Inverse; Pose cameraPose = cameraNode.PoseWorld; float near = projection.Near; float far = projection.Far; int frame = context.Frame; cameraNode.LastFrame = frame; // Save render state. var originalRasterizerState = graphicsDevice.RasterizerState; var originalDepthStencilState = graphicsDevice.DepthStencilState; var originalBlendState = graphicsDevice.BlendState; graphicsDevice.RasterizerState = RasterizerState.CullNone; graphicsDevice.DepthStencilState = DepthStencilState.None; RenderTarget2D offscreenBuffer = null; Texture depthBufferHalf = null; if (!EnableOffscreenRendering || context.RenderTarget == null) { graphicsDevice.BlendState = BlendState.AlphaBlend; _parameterGBuffer0.SetValue(context.GBuffer0); } else { // Render at half resolution into off-screen buffer. width = Math.Max(1, width / 2); height = Math.Max(1, height / 2); graphicsDevice.BlendState = BlendStateOffscreen; offscreenBuffer = renderTargetPool.Obtain2D( new RenderTargetFormat(width, height, false, context.RenderTarget.Format, DepthFormat.None)); graphicsDevice.SetRenderTarget(offscreenBuffer); graphicsDevice.Clear(Color.Black); // Get half-res depth buffer. object obj; if (context.Data.TryGetValue(RenderContextKeys.DepthBufferHalf, out obj) && obj is Texture2D) { depthBufferHalf = (Texture2D)obj; _parameterGBuffer0.SetValue(depthBufferHalf); } else { string message = "Downsampled depth buffer is not set in render context. (The downsampled " + "depth buffer (half width and height) is required by the VolumetricLightRenderer " + "to use half-res off-screen rendering. It needs to be stored in " + "RenderContext.Data[RenderContextKeys.DepthBufferHalf].)"; throw new GraphicsException(message); } } // Set global effect parameters. _parameterViewportSize.SetValue(new Vector2(width, height)); var isHdrEnabled = context.RenderTarget != null && context.RenderTarget.Format == SurfaceFormat.HdrBlendable; for (int i = 0; i < numberOfNodes; i++) { var node = nodes[i] as VolumetricLightNode; if (node == null) continue; // VolumetricLightNode is visible in current frame. node.LastFrame = frame; // Effect parameters for volumetric light properties. _parameterColor.SetValue((Vector3)node.Color / node.NumberOfSamples); _parameterNumberOfSamples.SetValue(node.NumberOfSamples); _parameterLightTextureMipMap.SetValue((float)node.MipMapBias); // The volumetric light effect is created for the parent light node. var lightNode = node.Parent as LightNode; if (lightNode == null) continue; Pose lightPose = lightNode.PoseWorld; // Get start and end depth values of light AABB in view space. var lightAabbView = lightNode.Shape.GetAabb(lightNode.ScaleWorld, view * lightPose); var startZ = Math.Max(-lightAabbView.Maximum.Z, near) / far; var endZ = Math.Min(-lightAabbView.Minimum.Z / far, 1); _parameterDepthInterval.SetValue(new Vector2(startZ, endZ)); // Get a rectangle that covers the light in screen space. var rectangle = GraphicsHelper.GetScissorRectangle(cameraNode, new Viewport(0, 0, width, height), lightNode); var texCoordTopLeft = new Vector2F(rectangle.Left / (float)width, rectangle.Top / (float)height); var texCoordBottomRight = new Vector2F(rectangle.Right / (float)width, rectangle.Bottom / (float)height); GraphicsHelper.GetFrustumFarCorners(cameraNode.Camera.Projection, texCoordTopLeft, texCoordBottomRight, _frustumFarCorners); // Convert frustum far corners from view space to world space. for (int j = 0; j < _frustumFarCorners.Length; j++) _frustumFarCorners[j] = (Vector3)cameraPose.ToWorldDirection((Vector3F)_frustumFarCorners[j]); _parameterFrustumCorners.SetValue(_frustumFarCorners); Vector2 randomSeed = AnimateNoise ? new Vector2((float)MathHelper.Frac(context.Time.TotalSeconds)) : new Vector2(0); _parameterRandomSeed.SetValue(randomSeed); // Set light parameters and apply effect pass. if (lightNode.Light is PointLight) { var light = (PointLight)lightNode.Light; float hdrScale = isHdrEnabled ? light.HdrScale : 1; _parameterLightDiffuse.SetValue((Vector3)light.Color * light.DiffuseIntensity * hdrScale); _parameterLightPosition.SetValue((Vector3)(lightPose.Position - cameraPose.Position)); _parameterLightRange.SetValue(light.Range); _parameterLightAttenuation.SetValue(light.Attenuation); bool hasTexture = (light.Texture != null); if (hasTexture) { _parameterLightTexture.SetValue(light.Texture); // Cube maps are left handed --> Sample with inverted z. (Otherwise, the // cube map and objects or texts in it are mirrored.) var mirrorZ = Matrix44F.CreateScale(1, 1, -1); _parameterLightTextureMatrix.SetValue((Matrix)(mirrorZ * lightPose.Inverse)); } if (hasTexture) { if (light.Texture.Format == SurfaceFormat.Alpha8) _passPointLightTextureAlpha.Apply(); else _passPointLightTextureRgb.Apply(); } else { _passPointLight.Apply(); } } else if (lightNode.Light is Spotlight) { var light = (Spotlight)lightNode.Light; float hdrScale = isHdrEnabled ? light.HdrScale : 1; _parameterLightDiffuse.SetValue((Vector3)light.Color * light.DiffuseIntensity * hdrScale); _parameterLightPosition.SetValue((Vector3)(lightPose.Position - cameraPose.Position)); _parameterLightRange.SetValue(light.Range); _parameterLightAttenuation.SetValue(light.Attenuation); _parameterLightDirection.SetValue((Vector3)lightPose.ToWorldDirection(Vector3F.Forward)); _parameterLightAngles.SetValue(new Vector2(light.FalloffAngle, light.CutoffAngle)); bool hasTexture = (light.Texture != null); if (hasTexture) { _parameterLightTexture.SetValue(light.Texture); var proj = Matrix44F.CreatePerspectiveFieldOfView(light.CutoffAngle * 2, 1, 0.1f, 100); _parameterLightTextureMatrix.SetValue((Matrix)(GraphicsHelper.ProjectorBiasMatrix * proj * (lightPose.Inverse * new Pose(cameraPose.Position)))); } if (hasTexture) { if (light.Texture.Format == SurfaceFormat.Alpha8) _passSpotlightTextureAlpha.Apply(); else _passSpotlightTextureRgb.Apply(); } else { _passSpotlight.Apply(); } } else if (lightNode.Light is ProjectorLight) { var light = (ProjectorLight)lightNode.Light; float hdrScale = isHdrEnabled ? light.HdrScale : 1; _parameterLightDiffuse.SetValue((Vector3)light.Color * light.DiffuseIntensity * hdrScale); _parameterLightPosition.SetValue((Vector3)(lightPose.Position - cameraPose.Position)); _parameterLightRange.SetValue(light.Projection.Far); _parameterLightAttenuation.SetValue(light.Attenuation); _parameterLightTexture.SetValue(light.Texture); _parameterLightTextureMatrix.SetValue((Matrix)(GraphicsHelper.ProjectorBiasMatrix * light.Projection * (lightPose.Inverse * new Pose(cameraPose.Position)))); if (light.Texture.Format == SurfaceFormat.Alpha8) _passProjectorLightTextureAlpha.Apply(); else _passProjectorLightTextureRgb.Apply(); } else { continue; } // Draw a screen space quad covering the light. graphicsDevice.DrawQuad(rectangle); } _parameterGBuffer0.SetValue((Texture)null); _parameterLightTexture.SetValue((Texture)null); if (offscreenBuffer != null) { // ----- Combine off-screen buffer with scene. graphicsDevice.BlendState = BlendState.Opaque; // The previous scene render target is bound as texture. // --> Switch scene render targets! var sceneRenderTarget = context.RenderTarget; var renderTarget = renderTargetPool.Obtain2D(new RenderTargetFormat(sceneRenderTarget)); context.SourceTexture = offscreenBuffer; context.RenderTarget = renderTarget; // Use the UpsampleFilter, which supports "nearest-depth upsampling". // (Nearest-depth upsampling is an "edge-aware" method that tries to // maintain the original geometry and prevent blurred edges.) if (_upsampleFilter == null) { _upsampleFilter = new UpsampleFilter(graphicsService); _upsampleFilter.Mode = UpsamplingMode.NearestDepth; _upsampleFilter.RebuildZBuffer = true; } _upsampleFilter.DepthThreshold = DepthThreshold; context.SceneTexture = sceneRenderTarget; _upsampleFilter.Process(context); context.SceneTexture = null; context.SourceTexture = null; renderTargetPool.Recycle(offscreenBuffer); renderTargetPool.Recycle(sceneRenderTarget); } // Restore render states. graphicsDevice.RasterizerState = originalRasterizerState; graphicsDevice.DepthStencilState = originalDepthStencilState; graphicsDevice.BlendState = originalBlendState; }
private void PostProcess(RenderContext context, RenderTarget2D source, RenderTarget2D target) { Debug.Assert(source != null); Debug.Assert(target != null); var originalSourceTexture = context.SourceTexture; var originalRenderTarget = context.RenderTarget; var originalViewport = context.Viewport; if (Filter != null && Filter.Enabled) { context.SourceTexture = source; context.RenderTarget = source; context.Viewport = new Viewport(0, 0, source.Width, source.Height); Filter.Process(context); } bool doUpsampling = UseHalfResolution && Numeric.IsGreater(UpsampleDepthSensitivity, 0); Debug.Assert(doUpsampling && source != target || !doUpsampling && source == target); if (doUpsampling) { if (_upsampleFilter == null) _upsampleFilter = _graphicsService.GetUpsampleFilter(); var graphicsDevice = _graphicsService.GraphicsDevice; var originalBlendState = graphicsDevice.BlendState; graphicsDevice.BlendState = BlendState.Opaque; // The previous scene render target is bound as texture. // --> Switch scene render targets! context.SourceTexture = source; context.RenderTarget = target; context.Viewport = new Viewport(0, 0, target.Width, target.Height); _upsampleFilter.DepthSensitivity = UpsampleDepthSensitivity; _upsampleFilter.Mode = UpsamplingMode.Bilateral; _upsampleFilter.RebuildZBuffer = false; _upsampleFilter.Process(context); if (originalBlendState != null) graphicsDevice.BlendState = originalBlendState; } context.SourceTexture = originalSourceTexture; context.RenderTarget = originalRenderTarget; context.Viewport = originalViewport; }
internal static UpsampleFilter GetUpsampleFilter(this IGraphicsService graphicsService) { if (graphicsService == null) throw new ArgumentNullException("graphicsService"); const string Key = "__UpsampleFilter"; object obj; graphicsService.Data.TryGetValue(Key, out obj); var instance = obj as UpsampleFilter; if (instance == null) { instance = new UpsampleFilter(graphicsService); graphicsService.Data[Key] = instance; } return instance; }