private void EndBillboards(RenderContext context) { if (!_billboardMode) { return; } _billboardMode = false; // Reset texture to prevent "memory leak". SetTexture(null); #if !WP7 if (EnableOffscreenRendering) { // ----- Combine off-screen buffer with scene. if (_upsampleFilter == null) { _upsampleFilter = GraphicsService.GetUpsampleFilter(); } var graphicsDevice = GraphicsService.GraphicsDevice; graphicsDevice.BlendState = BlendState.Opaque; // The previous scene render target is bound as texture. // --> Switch scene render targets! var sceneTexture = context.RenderTarget; var renderTargetPool = GraphicsService.RenderTargetPool; var renderTarget = renderTargetPool.Obtain2D(new RenderTargetFormat(sceneTexture)); context.SourceTexture = _offscreenBuffer; context.SceneTexture = sceneTexture; context.RenderTarget = renderTarget; _upsampleFilter.Mode = UpsamplingMode; _upsampleFilter.DepthThreshold = DepthThreshold; _upsampleFilter.RebuildZBuffer = true; _upsampleFilter.Process(context); context.SourceTexture = null; context.SceneTexture = null; renderTargetPool.Recycle(_offscreenBuffer); renderTargetPool.Recycle(sceneTexture); _depthBufferHalf = null; _offscreenBuffer = null; } #endif }
/// <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((Vector3)_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 = Matrix.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(Vector3.Forward)); _parameterLightAngles.SetValue(new Vector2(light.FalloffAngle, light.CutoffAngle)); bool hasTexture = (light.Texture != null); if (hasTexture) { _parameterLightTexture.SetValue(light.Texture); var proj = Matrix.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; }