Wednesday, December 07, 2005

Render to Surface/Texture

ACarPC project but, understandably, he was too busy at the time. I think I have enough confidence in DirectX now to create a pretty 3d interface for the CarPC. Perhaps if I have time I might have a quick go, although I'm not entirely sure at the moment what I want the final look to be.The program has only had 500 downloads since June 2005 and besides it's not as if I can use it, as i've got no car!

Since August I've been trying to learn DirectX and my long time friend above recommended a book to me, it's the only book I've bought on DirectX: Managed DirectX 9 (Kick Start) by Tom Miller. I've not read it fully yet, I've only been using as a reference.

Rather than read a book and then code, I prefer to just dive straight in and see where I end up. If I run into any problems or I want to know how to do something, then I'll look it up. After all why read the manual on how to program a VCR when you can play with the buttons and figure it out. After all these items "should" be designed to be (relatively) intuitive and if they're not then perhaps one needs to think like the creator and have a logical engineering style mind, either way I should be ok!

A couple of days ago I implemented a feature to render the scene to a texture. It was quite simple to acheive. Hopefully this will mean I can later manipulate that texture, a blur effect or some other filter. It will mean I can show a different view of the battle in the corner of the screen, like a rear view mirror in a driving game.
A question does crop up in my mind, will this be of some real benefit to the user, or have I just added it to demonstrate what can be done in directx?
Well if I can apply filters then it will be of benefit, as the game will look more pleasurable, however if it's just the "rear view" then it may not be of much use in this game. Having this extra camera view might be of some use when the player dies, we could focus/zoom in on the enemy that killed the player, or if a missile was launched we could use this extra camera view to show the missile as it moves to it's target.

I've not posted any code yet in this blog, mostly because my code tends to link around to different classes and occasionally calls methods in public modules, plus it doesn't look to good in plain black and white!

So here's how I did it.
First thing to do was to declare some variables:
---
'Render to surface vars
Private RenderSurfaceSize As Size = New Size(64, 48) 'size of the texture to be drawn
Private renderTexture As Texture = Nothing 'texture to hold the rendered scene
Private bRenderIntoASurface As Boolean = True 'are we going to render it?
Private rts As RenderToSurface = Nothing 'the render to surface object
Private renderSurface As Surface = Nothing 'the surface rts will use
Private spriteSurfaceToRender As DX9_Sprite ' the sprite that will handle the drawing of the texture onto the screen
---

DX9_Sprite is a custom class of mine, it basically just wraps up the Direct3D.Sprite object. Sets the size, position and loads the texture.

I have a routine that initialises directx stuff, this is called when the device is reset (OnDeviceReset event). Into this, the following code was added:
---
'create surface to render into
rts = New RenderToSurface(d3dDevice, RenderSurfaceSize.Width, RenderSurfaceSize.Height, _
Format.X8R8G8B8, True, DepthFormat.D16)

renderTexture = New Texture(d3dDevice, RenderSurfaceSize.Width, RenderSurfaceSize.Height, 1, _
Usage.RenderTarget, Format.X8R8G8B8, Pool.Default)

renderSurface = renderTexture.GetSurfaceLevel(0)

spriteSurfaceToRender = New DX9_Sprite(renderTexture, New Vector2(0, 0), New Size(RenderSurfaceSize.Width, RenderSurfaceSize.Height))
---

So I can now tell if we want to render this surface by checking the bRenderIntoASurface var. I can tell what size, I have surfaces and a texture as well as a sprite to render the final texture output to the screen.
I then proceeded to add the routine that would actually render to a surface. This code is basically from the books mentioned above.
---

Sub RenderIntoSurface()

' Render to this surface
Dim view As Viewport = New Viewport()
view.Width = RenderSurfaceSize.Width
view.Height = RenderSurfaceSize.Height
view.MaxZ = 1.0F

rts.BeginScene(renderSurface, view)

'clear
d3dDevice.Clear(ClearFlags.Target Or ClearFlags.ZBuffer, Color.DarkBlue, 1.0F, 0)

'setup the view
d3dDevice.Transform.Projection = Matrix.PerspectiveFovLH(Math.PI / 4, _
cEngine.ScreenWidth / cEngine.ScreenHeight, 1.0F, 10000.0F)
d3dDevice.Transform.View = Matrix.LookAtLH(New Vector3(0, 0, -7.0F), _
New Vector3(), New Vector3(0, 1, 0))

Render() 'renders a single frame

rts.EndScene(Filter.None)

End Sub
---

Render() is a method that will call the render method for whatever "scene"/state (game, menu, splash) is currently active. In essence this just renders the scene for a single frame. At the moment the problem with this, is that the d3dDevice view and projection lines above are just ignored as the Render method will overwrite them and set them to whatever the camera class specifies. So I need to sort that out.


For testing purposes I just added the code into my render loop, so I now have the following code:
---
If bRenderIntoASurface Then RenderIntoSurface() 'This will crete the surface

cEngine.BeginRender(Not bUseblur) ' Begin the DirectX part of the rendering

Render() ' Render the graphics

If bRenderIntoASurface Then
d3dDevice.RenderState.AlphaBlendEnable = False
spriteSurfaceToRender.Render() 'This will actually render the surface to the screen
End If

cEngine.EndRender() ' End the DirectX part of the rendering and present the scene
---

cEngine.BeginRender calls the Direct3dDevice "Clear" and "BeginScene" methods.
As you can see the "RenderIntoSurface" method is called before cEngine.BeginRender.

That is pretty much all it took. Now I know the code works I have to extract the code and inject it into a seperate class. That way I can have my level show the "rear view" but the main menu won't!



Update: I've now moved the code into my Level class and fixed the few issues, only took about an hour! The following is the effect the render to surface technique produces:

If I move the main camera around, the render to surface view in the top left is static to whatever I set it to, so the main view changes but the render to surface view doesn't.

I had to add a prerender sub to my render loop. I also modified the Draw routine - it's now
"Sub Draw(Optional ByVal ISPreRender As Boolean = False)"- to check if its being called for a prerender (i.e. being called to render the scene to a texture), if it isn't a prerender it will update the cameras view and projection and render the prerendered sprite otherwise it won't.

0 Comments:

Post a Comment

<< Home


___