Exemplo n.º 1
0
        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;
        }