Exemplo n.º 1
0
    public PostProcessingGraphicsScreen(IServiceLocator services)
      : base(services.GetInstance<IGraphicsService>())
    {
      _sampleFramework = services.GetInstance<SampleFramework>();

      _spriteBatch = new SpriteBatch(GraphicsService.GraphicsDevice);
      _clearGBufferRenderer = new ClearGBufferRenderer(GraphicsService);
      _rebuildZBufferRenderer = new RebuildZBufferRenderer(GraphicsService);
      _meshRenderer = new MeshRenderer();
      _skyRenderer = new SkyRenderer(GraphicsService);
      _billboardRenderer = new BillboardRenderer(GraphicsService, 2048);

      Scene = new Scene();
      PostProcessors = new PostProcessorChain(GraphicsService);

      // Use 2D texture for reticle.
      var contentManager = services.GetInstance<ContentManager>();
      _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),
      };
    }
Exemplo n.º 2
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;
    }
Exemplo n.º 3
0
        public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            ThrowIfDisposed();

              if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");

              int numberOfNodes = nodes.Count;
              if (numberOfNodes == 0)
            return;

              context.Validate(_effect);
              context.ThrowIfCameraMissing();

              float deltaTime = (float)context.DeltaTime.TotalSeconds;

              var graphicsService = context.GraphicsService;
              var graphicsDevice = graphicsService.GraphicsDevice;
              var renderTargetPool = graphicsService.RenderTargetPool;

              var cameraNode = context.CameraNode;
              Projection projection = cameraNode.Camera.Projection;
              Pose view = cameraNode.PoseWorld.Inverse;

              // Around the camera we push the waves down to avoid that the camera cuts the near plane.
              // Get largest vector from camera to near plane corners.
              float nearPlaneRadius =
            new Vector3F(Math.Max(Math.Abs(projection.Right), Math.Abs(projection.Left)),
                     Math.Max(Math.Abs(projection.Top), Math.Abs(projection.Bottom)),
                     projection.Near
                    ).Length;

              var originalSourceTexture = context.SourceTexture;

              // Update SceneNode.LastFrame for all visible nodes.
              int frame = context.Frame;
              cameraNode.LastFrame = frame;

              var savedRenderState = new RenderStateSnapshot(graphicsDevice);

              // Water surface is opaque.
              graphicsDevice.BlendState = BlendState.Opaque;

              #region ----- Common Effect Parameters -----

              _parameterView.SetValue(view);
              _parameterProjection.SetValue(projection);
              _parameterCameraParameters.SetValue(new Vector4(
            (Vector3)cameraNode.PoseWorld.Position,
            cameraNode.Camera.Projection.Far));

              var viewport = graphicsDevice.Viewport;
              _parameterViewportSize.SetValue(new Vector2(viewport.Width, viewport.Height));

              _parameterTime.SetValue((float)context.Time.TotalSeconds);

              // Query ambient and directional lights.
              var lightQuery = context.Scene.Query<GlobalLightQuery>(cameraNode, context);
              Vector3F ambientLight = Vector3F.Zero;
              if (lightQuery.AmbientLights.Count > 0)
              {
            var light = (AmbientLight)lightQuery.AmbientLights[0].Light;
            ambientLight = light.Color * light.Intensity * light.HdrScale;
              }

              _parameterAmbientLight.SetValue((Vector3)ambientLight);

              Vector3F directionalLightDirection = new Vector3F(0, -1, 0);
              Vector3F directionalLightIntensity = Vector3F.Zero;
              if (lightQuery.DirectionalLights.Count > 0)
              {
            var lightNode = lightQuery.DirectionalLights[0];
            var light = (DirectionalLight)lightNode.Light;
            directionalLightDirection = -lightNode.PoseWorld.Orientation.GetColumn(2);
            directionalLightIntensity = light.Color * light.SpecularIntensity * light.HdrScale;
              }

              _parameterDirectionalLightDirection.SetValue((Vector3)directionalLightDirection);
              _parameterDirectionalLightIntensity.SetValue((Vector3)directionalLightIntensity);

              _parameterGBuffer0.SetValue(context.GBuffer0);

              if (_parameterNoiseMap != null)
            _parameterNoiseMap.SetValue(_noiseMap);
              #endregion

              #region ----- Fog Parameters -----

              var fogNodes = context.Scene.Query<FogQuery>(cameraNode, context).FogNodes;
              SetFogParameters(fogNodes, cameraNode, directionalLightDirection);
              #endregion

              _parameterProjectedGridParameters.SetValue(new Vector3(
              ProjectedGridParameters.EdgeAttenuation,
              ProjectedGridParameters.DistanceAttenuationStart,
              ProjectedGridParameters.DistanceAttenuationEnd));

              for (int i = 0; i < numberOfNodes; i++)
              {
            var node = nodes[i] as WaterNode;
            if (node == null)
              continue;

            // Node is visible in current frame.
            node.LastFrame = frame;

            var data = node.RenderData as WaterRenderData;
            if (data == null)
            {
              data = new WaterRenderData();
              node.RenderData = data;
            }

            var water = node.Water;
            bool isCameraUnderwater = node.EnableUnderwaterEffect && node.IsUnderwater(cameraNode.PoseWorld.Position);

            #region ----- Wave bending -----

            // Waves should not cut the near plane. --> Bend waves up or down if necessary.

            // Limits
            float upperLimit; // Waves must not move above this value.
            float lowerLimit; // Waves must not move below this value.

            // Bending fades in over interval [bendStart, bendEnd]:
            //   distance ≤ bendStart ............. Wave is bent up or down.
            //   bendStart < distance < bendEnd ... Lerp between normal wave and bent wave.
            //   distance ≥ bendEnd ............... Normal wave.
            float bendStart = 1 * nearPlaneRadius;
            float bendEnd = 10 * nearPlaneRadius;

            if (!isCameraUnderwater)
            {
              // Bend waves down below the camera.
              upperLimit = cameraNode.PoseWorld.Position.Y - nearPlaneRadius;
              lowerLimit = -1e20f;

              if (node.EnableUnderwaterEffect)
              {
            if (node.Waves == null || node.Waves.DisplacementMap == null)
            {
              // No displacement. The wave bending stuff does not work because the surface
              // is usually not tessellated. We have to render the underwater geometry when
              // camera near plane might cut the water surface.
              if (node.Volume == null)
              {
                // Test water plane.
                isCameraUnderwater = (cameraNode.PoseWorld.Position.Y - nearPlaneRadius) < node.PoseWorld.Position.Y;
              }
              else
              {
                // Test water AABB.
                var aabb = node.Aabb;
                aabb.Minimum -= new Vector3F(nearPlaneRadius);
                aabb.Maximum += new Vector3F(nearPlaneRadius);
                isCameraUnderwater = GeometryHelper.HaveContact(aabb, cameraNode.PoseWorld.Position);
              }
            }
              }
            }
            else
            {
              // Camera is underwater, bend triangles up above camera.
              upperLimit = 1e20f;
              lowerLimit = cameraNode.PoseWorld.Position.Y + nearPlaneRadius;
            }

            _parameterCameraMisc.SetValue(new Vector4(upperLimit, lowerLimit, bendStart, bendEnd));
            #endregion

            // Update the submesh for the given water volume.
            data.UpdateSubmesh(graphicsService, node);

            #region ----- Scroll Normal Maps -----

            // We update the normal map offsets once(!) per frame.
            // Note: We could skip the offsets and compute all in the shader using absolute
            // time instead of deltaTime, but then the user cannot change the NormalMapVelocity
            // smoothly.
            if (data.LastNormalUpdateFrame != frame)
            {
              data.LastNormalUpdateFrame = frame;

              var baseVelocity = (node.Flow != null) ? node.Flow.BaseVelocity : Vector3F.Zero;

              // Increase offset.
              // (Note: We have to subtract value and divide by scale because if the normal
              // should scroll to the right, we have to move the texcoords in the other direction.)
              data.NormalMapOffset0.X -= (water.NormalMap0Velocity.X + baseVelocity.X) * deltaTime / water.NormalMap0Scale;
              data.NormalMapOffset0.Y -= (water.NormalMap0Velocity.Z + baseVelocity.Y) * deltaTime / water.NormalMap0Scale;
              data.NormalMapOffset1.X -= (water.NormalMap1Velocity.X + baseVelocity.X) * deltaTime / water.NormalMap1Scale;
              data.NormalMapOffset1.Y -= (water.NormalMap1Velocity.Z + baseVelocity.Y) * deltaTime / water.NormalMap1Scale;

              // Keep only the fractional part to avoid overflow.
              data.NormalMapOffset0.X = MathHelper.Frac(data.NormalMapOffset0.X);
              data.NormalMapOffset0.Y = MathHelper.Frac(data.NormalMapOffset0.Y);
              data.NormalMapOffset1.X = MathHelper.Frac(data.NormalMapOffset1.X);
              data.NormalMapOffset1.Y = MathHelper.Frac(data.NormalMapOffset1.Y);
            }
            #endregion

            _parameterSurfaceLevel.SetValue(node.PoseWorld.Position.Y);

            #region ----- Reflection Parameters -----

            if (node.PlanarReflection != null
            && node.PlanarReflection.ActualIsEnabled
            && node.PlanarReflection.RenderToTexture.Texture is Texture2D)
            {
              // Planar reflection.
              var renderToTexture = node.PlanarReflection.RenderToTexture;
              var texture = (Texture2D)renderToTexture.Texture;

              _parameterReflectionTypeParameters.SetValue(new Vector2(0, 1));
              _parameterReflectionMatrix.SetValue((Matrix)renderToTexture.TextureMatrix);
              _parameterReflectionTextureSize.SetValue(new Vector2(texture.Width, texture.Height));
              if (_parameterPlanarReflectionMap != null)
            _parameterPlanarReflectionMap.SetValue(texture);

              _parameterReflectionParameters.SetValue(new Vector4(
            (Vector3)water.ReflectionColor,
            water.ReflectionDistortion));
            }
            else if (node.SkyboxReflection != null)
            {
              // Cube map reflection.
              var rgbmEncoding = node.SkyboxReflection.Encoding as RgbmEncoding;
              float rgbmMax = 1;
              if (rgbmEncoding != null)
            rgbmMax = GraphicsHelper.ToGamma(rgbmEncoding.Max);
              else if (!(node.SkyboxReflection.Encoding is SRgbEncoding))
            throw new NotImplementedException("The reflected skybox must be encoded using sRGB or RGBM.");

              _parameterReflectionTypeParameters.SetValue(new Vector2(1, rgbmMax));

              // 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);
              Matrix33F orientation = node.SkyboxReflection.PoseWorld.Orientation;
              _parameterReflectionMatrix.SetValue((Matrix)(new Matrix44F(orientation, Vector3F.Zero) * mirrorZ));

              if (_parameterCubeReflectionMap != null)
            _parameterCubeReflectionMap.SetValue(node.SkyboxReflection.Texture);

              _parameterReflectionParameters.SetValue(new Vector4(
            (Vector3)(water.ReflectionColor * node.SkyboxReflection.Color),
            water.ReflectionDistortion));
            }
            else
            {
              // No reflection texture. The reflection shows only the ReflectionColor.
              _parameterReflectionTypeParameters.SetValue(new Vector2(-1, 1));
              _parameterReflectionParameters.SetValue(new Vector4(
            (Vector3)water.ReflectionColor,
            water.ReflectionDistortion));
            }
            #endregion

            #region ----- Refraction Parameters -----

            // If we do not have a source texture, resolve the current render target
            // and immediately rebuilt it.
            if (context.SourceTexture == null && context.RenderTarget != null)
            {
              // Get RebuildZBufferRenderer from RenderContext.
              RebuildZBufferRenderer rebuildZBufferRenderer = null;
              object obj;
              if (context.Data.TryGetValue(RenderContextKeys.RebuildZBufferRenderer, out obj))
            rebuildZBufferRenderer = obj as RebuildZBufferRenderer;

              // If we didn't find the renderer in the context, use a default instance.
              if (rebuildZBufferRenderer == null)
              {
            if (_defaultRebuildZBufferRenderer == null)
              _defaultRebuildZBufferRenderer = new RebuildZBufferRenderer(graphicsService);

            rebuildZBufferRenderer = _defaultRebuildZBufferRenderer;
              }

              context.SourceTexture = context.RenderTarget;
              context.RenderTarget = renderTargetPool.Obtain2D(new RenderTargetFormat(context.RenderTarget));
              graphicsDevice.SetRenderTarget(context.RenderTarget);
              graphicsDevice.Viewport = context.Viewport;
              rebuildZBufferRenderer.Render(context, context.SourceTexture);
            }

            _parameterRefractionTexture.SetValue(context.SourceTexture);
            _parameterRefractionParameters.SetValue(new Vector4(
              ((Vector3)water.RefractionColor),
              water.RefractionDistortion));
            #endregion

            #region ----- Other Water Effect Parameters -----

            if (water.NormalMap0 != null)
            {
              if (_parameterNormalMap0 != null)
            _parameterNormalMap0.SetValue(water.NormalMap0);

              _parameterNormalMap0Parameters.SetValue(new Vector4(
            1 / water.NormalMap0Scale,
            data.NormalMapOffset0.X,
            data.NormalMapOffset0.Y,
            water.NormalMap0Strength));
            }
            else
            {
              if (_parameterNormalMap0 != null)
            _parameterNormalMap0.SetValue(_graphicsService.GetDefaultNormalTexture());
              _parameterNormalMap0Parameters.SetValue(new Vector4(1, 0, 0, 0));
            }

            if (water.NormalMap1 != null)
            {
              if (_parameterNormalMap1 != null)
            _parameterNormalMap1.SetValue(water.NormalMap1);

              _parameterNormalMap1Parameters.SetValue(new Vector4(
            1 / water.NormalMap1Scale,
            data.NormalMapOffset1.X,
            data.NormalMapOffset1.Y,
            water.NormalMap1Strength));
            }
            else
            {
              if (_parameterNormalMap1 != null)
            _parameterNormalMap1.SetValue(_graphicsService.GetDefaultNormalTexture());
              _parameterNormalMap1Parameters.SetValue(new Vector4(1, 0, 0, 0));
            }

            _parameterSpecularParameters.SetValue(new Vector4((Vector3)water.SpecularColor, water.SpecularPower));
            _parameterUnderwaterFogParameters.SetValue((Vector3)water.UnderwaterFogDensity);
            _parameterFresnelParameters.SetValue(new Vector3(water.FresnelBias, water.FresnelScale, water.FresnelPower));
            _parameterIntersectionSoftness.SetValue(water.IntersectionSoftness);

            // We apply some arbitrary scale factors to the water and scatter colors to
            // move the values into a similar range from the user's perspective.
            _parameterWaterColor.SetValue((Vector3)water.WaterColor / 10);
            _parameterScatterColor.SetValue((Vector3)water.ScatterColor);

            if (_parameterFoamMap != null)
            {
              _parameterFoamMap.SetValue(water.FoamMap);
              _parameterFoamParameters0.SetValue(new Vector4(
            (Vector3)water.FoamColor,
            1 / water.FoamMapScale));

              _parameterFoamParameters1.SetValue(new Vector4(
            water.FoamDistortion,
            water.FoamShoreIntersection,
            // Enable crest foam only if we have waves.
            node.Waves != null ? water.FoamCrestMin : float.MaxValue,
            water.FoamCrestMax));
            }

            _parameterCausticsSampleCount.SetValue(water.CausticsSampleCount);
            _parameterCausticsParameters.SetValue(new Vector4(
              water.CausticsSampleOffset,
              water.CausticsDistortion,
              water.CausticsPower,
              water.CausticsIntensity));
            #endregion

            #region ----- Wave Map -----

            var waves = node.Waves;

            // The displacement map can be null but the normal map must not be null.
            if (waves != null && waves.NormalMap != null)
            {
              // Type: 0 = Tiling, 1 = Clamp.
              float waveType;
              if (waves.IsTiling)
            waveType = 0;
              else
            waveType = 1;

              _parameterWaveMapParameters.SetValue(new Vector4(
            1.0f / waves.TileSize,                          // Scale
            0.5f - waves.TileCenter.X / waves.TileSize,     // Offset X
            0.5f - waves.TileCenter.Z / waves.TileSize,     // Offset Y
            waveType));

              if (_parameterDisplacementTexture != null)
              {
            if (waves.DisplacementMap != null)
              _parameterDisplacementTexture.SetValue(waves.DisplacementMap);
            else
              _parameterDisplacementTexture.SetValue(graphicsService.GetDefaultTexture2DBlack4F());
              }

              _parameterWaveMapSize.SetValue(new Vector2(
            waves.NormalMap.Width,
            waves.NormalMap.Height));
              if (_parameterWaveNormalMap != null)
            _parameterWaveNormalMap.SetValue(waves.NormalMap);
            }
            else
            {
              _parameterWaveMapParameters.SetValue(new Vector4(0, 0, 0, 0));
            }
            #endregion

            #region ----- Flow -----

            if (node.Flow != null)
            {
              var flow = node.Flow;
              float flowMapSpeed = (flow.FlowMap != null) ? flow.FlowMapSpeed : 0;
              _parameterFlowParameters0.SetValue(new Vector4(flow.SurfaceSlopeSpeed, flowMapSpeed, flow.CycleDuration, flow.MaxSpeed));
              _parameterFlowParameters1.SetValue(new Vector3(flow.MinStrength, 1 / flow.NoiseMapScale, flow.NoiseMapStrength));

              if (_parameterFlowMap != null)
            _parameterFlowMap.SetValue(flow.FlowMap);

              // Get world space (x, z) to texture space matrix.
              Aabb aabb = node.Shape.GetAabb();
              Vector3F extent = aabb.Extent;
              Matrix44F m = Matrix44F.CreateScale(1 / extent.X, 1, 1 / extent.Z)
                        * Matrix44F.CreateTranslation(-aabb.Minimum.X, 0, -aabb.Minimum.Z)
                        * Matrix44F.CreateScale(1 / node.ScaleLocal.X, 1, 1 / node.ScaleLocal.Z)
                        * node.PoseWorld.Inverse;

              // We use a 3x3 2d scale/rotation/translation matrix, ignoring the y component.
              _parameterFlowMapTextureMatrix.SetValue(new Matrix(m.M00, m.M20, 0, 0,
                                                             m.M02, m.M22, 0, 0,
                                                             m.M03, m.M23, 1, 0,
                                                             0, 0, 0, 0));

              // Get local flow direction to world flow direction matrix.
              // We use a 2x2 2d rotation matrix, ignoring the y component.
              var r = node.PoseWorld.Orientation;
              _parameterFlowMapWorldMatrix.SetValue(new Matrix(r.M00, r.M20, 0, 0,
                                                           r.M02, r.M22, 0, 0,
                                                           0, 0, 0, 0,
                                                           0, 0, 0, 0));
            }
            else
            {
              _parameterFlowParameters0.SetValue(new Vector4(0, 0, 0, 0));
              _parameterFlowParameters1.SetValue(new Vector3(0, 0, 0));
            }
            #endregion

            if (isCameraUnderwater)
              RenderUnderwaterGeometry(node, cameraNode);

            RenderSurface(node, cameraNode, isCameraUnderwater);
              }

              // Reset texture effect parameters.
              _parameterGBuffer0.SetValue((Texture2D)null);
              _parameterRefractionTexture.SetValue((Texture2D)null);

              if (_parameterPlanarReflectionMap != null)
            _parameterPlanarReflectionMap.SetValue((Texture2D)null);

              if (_parameterCubeReflectionMap != null)
            _parameterCubeReflectionMap.SetValue((TextureCube)null);

              if (_parameterNormalMap0 != null)
            _parameterNormalMap0.SetValue((Texture2D)null);

              if (_parameterNormalMap1 != null)
            _parameterNormalMap1.SetValue((Texture2D)null);

              if (_parameterDisplacementTexture != null)
            _parameterDisplacementTexture.SetValue((Texture2D)null);

              if (_parameterNoiseMap != null)
            _parameterNoiseMap.SetValue((Texture2D)null);

              if (_parameterWaveNormalMap != null)
            _parameterWaveNormalMap.SetValue((Texture2D)null);

              if (_parameterFlowMap != null)
            _parameterFlowMap.SetValue((Texture2D)null);

              // This seems to be necessary because the Displacement Texture (vertex texture!)
              // is not automatically removed from the texture stage, and the WaterWavesRenderer
              // cannot write into it. XNA Bug!?
              _passProjectedGrid.Apply();

              savedRenderState.Restore();

              // Restore original render context.
              if (originalSourceTexture == null)
              {
            // Current render target has been resolved and used as source texture.
            // A new render target (from pool) has been set. (See region "Refraction Parameters".)
            // --> Previous render target needs to be recycled.
            renderTargetPool.Recycle(context.SourceTexture);
              }

              context.SourceTexture = originalSourceTexture;
        }