Render to Surface






How to render a mesh to a surface with Direct3D

Purpose of this tutorial is to introduce a seldom metioned technique:

with this technique you can avoid the creation of a texture, which is nomally used as a render target. When you use this technique your code will also be much simpler than by using a texture. But be aware that most pages mention that the rendering to a texture needs a lot less performance than this technique.

Even so I find this technique useful for example for situations where you normally display only one mesh on the screen, like for example in a game module where you describe mechanisms or game parts like technologies.

To understand this tutorial you should have managed to compile the Direct3D-EmptyProject from the Microsoft DirectX SDK, as this is the base of my tutorial.

For the general algorithm to render a mesh to a surface you need to solve the following steps:

  1. Generating your D3DDevice
  2. Loading of the mesh from a file
  3. Creating a surface as RenderTarget
  4. Drawing the mesh to your RenderTarget
  5. Drawing your surface on your D3DDevice

Generating your D3DDevice

For generating your D3DDevice we use the Direct3D-EmptyProject from the Microsoft DirectX SDK. There you will find all you need to go to the next step of this tutorial.

INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
    // Set the callback functions
    DXUTSetCallbackDeviceCreated( OnCreateDevice );
    DXUTSetCallbackDeviceReset( OnResetDevice );
    DXUTSetCallbackDeviceLost( OnLostDevice );
    DXUTSetCallbackDeviceDestroyed( OnDestroyDevice );
    DXUTSetCallbackMsgProc( MsgProc );
    DXUTSetCallbackFrameRender( OnFrameRender );
    DXUTSetCallbackFrameMove( OnFrameMove );
   
    // Initialize DXUT and create the desired Win32 window and Direct3D device for the application
    DXUTInit( true, true, true ); // Parse the command line, handle the default hotkeys, and show msgboxes
    DXUTSetCursorSettings( true, true ); // Show the cursor and clip it when in full screen
    DXUTCreateWindow( L"RenderToSurface" );
    DXUTCreateDevice( D3DADAPTER_DEFAULT, true, 800, 600, IsDeviceAcceptable, ModifyDeviceSettings );

    // Start the render loop
    DXUTMainLoop();

    return DXUTGetExitCode();
}

Loading of the mesh from a file

To display a mesh we need first to load it fom a file. In good style I have created a small class which loads and renders a mesh:

Creating a surface as RenderTarger

To render a mesh to a surface, those surface nees to be created as a RenderTarget.

Our first step will be to get the current RenderTarget with GetRenderTarget for two purposes. First to restore it after we are done with rendering our mesh and second to take some of its values to create our RenderTarget correctly. Those values we acquire by using the Method GetDesc of IDirect3DSurface9.

Now we can use CreateRenderTarget with its size, color format and the parameters taken from the original RenderTarget.

  IDirect3DSurface9* surface_ = NULL;
  IDirect3DSurface9* origTarget_ = NULL;
	
  // store orginal rendertarget
  pd3dDevice->GetRenderTarget( 0, &origTarget_ );
  D3DSURFACE_DESC desc;
  origTarget_->GetDesc( &desc );

  // create our surface as render target
  pd3dDevice->CreateRenderTarget( 320, 240, D3DFMT_X8R8G8B8, 
                                  desc.MultiSampleType, desc.MultiSampleQuality,
                                  false, &surface_, NULL );		

Drawing the mesh to your RenderTarget

Next we set our surface as the RenderTarget. Then we first clear it. After that we start to set a rotation matrix, viewing vectors, perspective projection and finally an ambient light source.

  // now all rendering will be directed to our surface
  pd3dDevice->SetRenderTarget( 0, surface_ );
	
  // clear surface
  pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,30), 1, 0  );

  // Prepare Transformations so that our mesh rotates slowly
  D3DXMATRIXA16 trans;
  D3DXMatrixRotationY( &trans, timeGetTime()/5000.0f );
  D3DXMATRIXA16 trans2;
  D3DXMatrixRotationZ( &trans2, timeGetTime()/5000.0f );
  trans *= trans2;

  // our whole world is rotating around the mesh
  pd3dDevice->SetTransform( D3DTS_WORLD, &trans );

  // prepare view vectors
  D3DXVECTOR3 vEyePt( 0.0f, 3.0f,-5.0f );
  D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
  D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
  D3DXMATRIXA16 matView;
  D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
  // set view vectors
  pd3dDevice->SetTransform( D3DTS_VIEW, &matView );

  // prepare projection matrix
  D3DXMATRIXA16 matProj;
  D3DXMatrixPerspectiveFovLH( &matProj, 
                              D3DX_PI/4, 1.0f, 1.0f, 100.0f  );
  pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
  
  // finally we need some light
  pd3dDevice->SetRenderState( D3DRS_AMBIENT, D3DXCOLOR( 1.0, 1.0, 1.0, 1.0 ) );

By this we have prepared the world on our RenderTarget so that we can now draw our mesh.

The mesh is here for simplicity realised by a static variable.

  // load mesh, here for convenience into a static variable
  static D3DMesh mesh = D3DMesh( "tiger.x" );
  static bool loaded = false;
  if (!loaded ) 
  {
    mesh.loadFromFile( pd3dDevice );
    loaded = true;
  }
  mesh.draw( pd3dDevice );

Note: That through the set rotation matrix our mesh will slowly rotate around it's X and Z axis.

Drawing your surface on your D3DDevice

After all this we restore the original RenderTarget, so that we can draw our surface onto it.

  //restore original render target
  pd3dDevice->SetRenderTarget( 0, origTarget_ );	

Now we get the Backbuffer of the current RenderTarget. Create a rectangle as target for our surface and draw then our surface to the Backbuffer.

  // target rect for our surface
  RECT DstRect;
  DstRect.left = 40;
  DstRect.top = 30;
  DstRect.right = 360;
  DstRect.bottom = 270;				

  IDirect3DSurface9* backBuffer = NULL;

  // draw our our surface to screen
  if( !FAILED( pd3dDevice->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &backBuffer ) ) )
  {
    pd3dDevice->StretchRect( surface_, NULL, backBuffer, &DstRect, D3DTEXF_NONE );
  }

On EndScene the Backbuffer will then be drawn onto the screen and we are done.

  V( pd3dDevice->EndScene() );

Conclusion

So intead of a creating a texture you just create a surface as RenderTarget.

Instead of drawing a dummy object for the texture you draw the suface directly to the Backbuffer.





Startseite

Impressum



Bei Problemen mit diesen Internetseiten schreiben sie bitte eine Email an mich!