/// <summary>Updates the reflected image on the water surface</summary> /// <param name="gameTime">Snapshot of the game's timing values</param> /// <param name="camera">Camera through which the scene is being viewed</param> /// <param name="reflectedSceneDrawer"> /// Delegate that will be called to draw the scene in its reflected state /// </param> /// <remarks> /// <para> /// When the delegate is called, the scene should be drawn normally using /// the provided game time and camera. The view matrix of the provided camera /// will have been adjusted to draw the scene upside-down and the graphics device /// will be configured to clip off anything that's below the water surface. /// </para> /// <para> /// Some adjustments can be made, like rendering the reflection with reduced /// detail, cheaper effects or even leaving our parts of the scene to improve /// performance since the reflection will not be clearly displayed anyway. /// </para> /// </remarks> public void UpdateReflection( GameTime gameTime, Camera camera, SceneDrawDelegate reflectedSceneDrawer ) { // Create a matrix that undoes the view and projection transforms. We don't // involve any world matrix here because the water plane exists in // the world coordinate frame, its world matrix is always the identity matrix. Matrix viewProjection = camera.View * camera.Projection; Matrix inverseViewProjection = Matrix.Invert(viewProjection); // The water plane in world coordinates. We want to clip away everything that's // below the water surface (as only things above it should be reflected) Vector4 worldWaterPlane = new Vector4(Vector3.Down, 0.0f); // The water plane as sent through the inverse view and projection transforms. // This is neccessary because the plane will be transformed by those two // matrices when it is applied to the scene. Don't ask me why. Vector4 projectedWaterPlane = Vector4.Transform( worldWaterPlane, Matrix.Transpose(inverseViewProjection) ); #if XNA_4 this.graphicsDevice.SetRenderTarget(this.reflectionRenderTarget); #else this.graphicsDevice.SetRenderTarget(0, this.reflectionRenderTarget); #endif try { // Set up a clipping plane that only draws those parts of the scene that // are above the water because that's the only thing we want to appear in // the reflection on the water surface. #if !XNA_4 // TODO: Move this to the shader for both XNA 3.1 and XNA 4.0 this.graphicsDevice.ClipPlanes[0].Plane = new Plane(projectedWaterPlane); this.graphicsDevice.ClipPlanes[0].IsEnabled = true; try { #endif Matrix reflectionMatrix = Matrix.CreateReflection(new Plane(worldWaterPlane)); this.reflectionCamera.View = reflectionMatrix * camera.View; this.reflectionCamera.Projection = camera.Projection; reflectedSceneDrawer(gameTime, this.reflectionCamera); #if !XNA_4 } finally { this.graphicsDevice.ClipPlanes[0].IsEnabled = false; } #endif } finally { #if XNA_4 this.graphicsDevice.SetRenderTarget(null); #else this.graphicsDevice.SetRenderTarget(0, null); this.reflectionTexture = reflectionRenderTarget.GetTexture(); #endif } }
/// <summary>Updates the reflected image on the water surface</summary> /// <param name="gameTime">Snapshot of the game's timing values</param> /// <param name="camera">Camera through which the scene is being viewed</param> /// <param name="reflectedSceneDrawer"> /// Delegate that will be called to draw the scene in its reflected state /// </param> /// <remarks> /// <para> /// When the delegate is called, the scene should be drawn normally using /// the provided game time and camera. The view matrix of the provided camera /// will have been adjusted to draw the scene upside-down and the graphics device /// will be configured to clip off anything that's below the water surface. /// </para> /// <para> /// Some adjustments can be made, like rendering the reflection with reduced /// detail, cheaper effects or even leaving our parts of the scene to improve /// performance since the reflection will not be clearly displayed anyway. /// </para> /// </remarks> public void UpdateReflection( GameTime gameTime, Camera camera, SceneDrawDelegate reflectedSceneDrawer ) { // Create a matrix that undoes the view and projection transforms. We don't // involve any world matrix here because the water plane exists in // the world coordinate frame, its world matrix is always the identity matrix. Matrix viewProjection = camera.View * camera.Projection; Matrix inverseViewProjection = Matrix.Invert(viewProjection); // The water plane in world coordinates. We want to clip away everything that's // below the water surface (as only things above it should be reflected) Vector4 worldWaterPlane = new Vector4(Vector3.Down, 0.0f); // The water plane as sent through the inverse view and projection transforms. // This is neccessary because the plane will be transformed by those two // matrices when it is applied to the scene. Don't ask me why. Vector4 projectedWaterPlane = Vector4.Transform( worldWaterPlane, Matrix.Transpose(inverseViewProjection) ); #if XNA_4 this.graphicsDevice.SetRenderTarget(this.reflectionRenderTarget); #else this.graphicsDevice.SetRenderTarget(0, this.reflectionRenderTarget); #endif try { // Set up a clipping plane that only draws those parts of the scene that // are above the water because that's the only thing we want to appear in // the reflection on the water surface. #if !XNA_4 // TODO: Move this to the shader for both XNA 3.1 and XNA 4.0 this.graphicsDevice.ClipPlanes[0].Plane = new Plane(projectedWaterPlane); this.graphicsDevice.ClipPlanes[0].IsEnabled = true; try { #endif Matrix reflectionMatrix = Matrix.CreateReflection(new Plane(worldWaterPlane)); this.reflectionCamera.View = reflectionMatrix * camera.View; this.reflectionCamera.Projection = camera.Projection; reflectedSceneDrawer(gameTime, this.reflectionCamera); #if !XNA_4 } finally { this.graphicsDevice.ClipPlanes[0].IsEnabled = false; } #endif } finally { #if XNA_4 this.graphicsDevice.SetRenderTarget(null); #else this.graphicsDevice.SetRenderTarget(0, null); this.reflectionTexture = reflectionRenderTarget.GetTexture(); #endif } }