Introduction
This article explains how to simulate a mirror in a Managed DirectX application. This technique has many uses, but the best one I can think of right now is to make a rear-view mirror in a driving simulation.
Using the Code
The Mirror effect is achieved by rendering the same scene (generally with a different camera angle) into a texture, and then applying this texture to a triangle(s) or simply to a sprite. I am going to apply it to a sprite.
Rendering Into Surface
First of all, we need to declare these variables :
private Texture renderTexture = null;
private Surface renderSurface = null;
private RenderToSurface rts = null;
private const int renderSurfaceSize = 128;
renderTexture
is the texture we will be rendering to.
renderSurface
is the surface which we get from the texture. Technically we will be rendering to this surface.
rts
is an object of type RenderToSurface
, which is a helper class used to, yes you guessed right, render to the surface.
renderSurfaceSize
is the length of the side of my square mirror (You can easily create a rectangular mirror, as you will see later.)
Now we initialize the mirror variables. This initialization should be done after the device creation and before Application.Run()
. Here is the code:
rts = new RenderToSurface(device, RenderSurfaceSize, RenderSurfaceSize,
Format.X8R8G8B8, true, DepthFormat.D16);
renderTexture = new Texture(device, RenderSurfaceSize, RenderSurfaceSize,
1,Usage.RenderTarget, Format.X8R8G8B8, Pool.Default);
renderSurface = renderTexture.GetSurfaceLevel(0);
First we create the object rts
of the helper class. Note that the arguments used in its creation are similar to the arguments used in device creation. Then we create our texture and then the actual surface from the texture.
Assume that the only object to render is a Mesh
, mesh
. So the code for this is mesh.DrawSubset(0);
Here is the code for rendering into the surface :
private void RenderIntoSurface()
{
Viewport view = new Viewport();
view.Width = RenderSurfaceSize;
view.Height = RenderSurfaceSize;
view.MaxZ = 1.0f;
rts.BeginScene(renderSurface, view);
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkBlue, 1.0f,
0);
device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4,
this.Width / this.Height, 1.0f, 100.0f);
device.Transform.View = Matrix.LookAtLH(new Vector3(0,0, -3.0f),
new Vector3(0,0,0), new Vector3(0, 1,0));
mesh.DrawSubset(0);
rts.EndScene(Filter.None);
}
Note that this code is very similar to our normal rendering method. There is a begin call, an end call, camera transforms, and the Mesh
rendering. The view matrix specified here is the actual view angle of the mirror.
Rendering the Sprite
We will want to render to the surface before rendering to our main window, hence add this line as the first statement in your OnPaint
method.
RenderIntoSurface()
Now we need to render the sprite. Given below is the code to render the sprite using the surface. This code is to be added in between the device.BeginScene()
and device.EndScene()
call.
using (Sprite s = new Sprite(device))
{
s.Begin(SpriteFlags.None);
s.Draw(renderTexture, new Rectangle(0, 0, RenderSurfaceSize,
RenderSurfaceSize), new Vector3(0, 0, 0), new Vector3(0, 0, 1.0f),
Color.White);
s.End();
}
Just to clear the confusion, I will give the full OnPaint
code:
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
RenderIntoSurface();
device.Clear(ClearFlags.Target| ClearFlags.ZBuffer,
System.Drawing.Color.CornflowerBlue, 1.0f, 0);
device.BeginScene();
SetupCamera();
mesh.DrawSubset(0);
using (Sprite s = new Sprite(device))
{
s.Begin(SpriteFlags.None);
s.Draw(renderTexture, new Rectangle(0, 0, RenderSurfaceSize,
RenderSurfaceSize), new Vector3(0, 0, 0),
new Vector3(82, 5, 1.0f), Color.White);
s.End();
}
device.EndScene();
device.Present();
this.Invalidate();
}
References
I would like to mention that I have referred to the book "Managed DirectX 9 Kick Start: Graphics and Game Programming" for writing this article.
History
- 14th July, 2006: Initial post