| 
         Written by Robert
        Dunlop 
        Microsoft DirectX MVP  | 
        | 
     
   
  
 
  
  
  
    
      | Rendering of scenes in Direct3D is limited by the size of the frame 
      buffer, but in a production environment it is often necessary to render 
      the scene to larger formats.  A common example is providing artwork 
      for printed advertising - a normal screen shot blown up to poster size is 
      not a pretty thing.  In this article we will take a look at a 
      technique for rendering very large  images in Direct3D, with a sample 
      function designed to call an existing scene rendering function, for easy 
      integration with existing code. | 
      
       
         | 
     
   
 
Rather than trying to extend the limits of the frame buffer, 
which is limited by supported resolutions and the amount of available video 
memory, we can instead tackle this task by breaking it down into smaller pieces.  
By subdividing the screen to be rendered into a grid, and then rendering each 
region separately, we can generate a series of images that can be tiled onto a 
larger image surface to form a single, seamless image. 
To do this, we have to modify the projection matrix prior to 
rendering each tile, so that the desired portion of the grid will be expanded 
and and aligned with the viewport rectangle.  This requires the region to 
be scaled and offset such that X and Y values after projection will fall into a 
range of -1.0 to 1.0.  For example, if we wanted to break the image into 2 
x 2 tiles, resulting in an image twice the width and height of the original, we 
would have to render the following ranges: 
  
    
      | 
       -1.0 < X < 0.0 
      0.0 < Y < 1.0  | 
      
        
  | 
        | 
      
       0.0 < X < 1.0 
      0.0 < Y < 1.0  | 
     
    
      | 
       -1.0 < X < 0.0 
      -1.0 < Y < 0.0  | 
      
        
  | 
        | 
      
       0.0 < X <1.0 
      -1.0 < Y < 0.0  | 
     
   
 
The coordinates shown above for each quadrant represent the 
range of each section if they were transformed and projected as a complete 
scene.  To allow the individual tiles to be rendered to the entire 
viewport, two adjustments must be made to the projection matrix: 
  - 
Scaling must be applied to the X and Y coordinates, effectively 
zooming in on the scene.  Multiplying the _11 and _22 members of the 
projection matrix by the number of subdivisions will scale the image such that 
each tile is the size of the viewport. 
  
   
  - 
Prior to rendering each tile, an appropriate offset must be 
applied to the X and Y axis to align the tile with the viewport rectangle.  
Note that this offset is applied in the third row of the projection matrix so 
that the offset is multiplied by depth, resulting in the offset being preserved 
after projection. 
   
 
Note: These changes 
to the projection matrix will have no effect on the rendering of pre-transformed 
vertices.  Such will have to be dealt with as a special case, which is 
beyond the scope of this article. 
| 
 
 Before we begin 
rendering the tiles, an image surface is created that will contain the final 
image.  The system memory pool is used, so that image size is not limited 
by available video memory. 
After  each tile is rendered, the image on the back buffer is copied to the 
corresponding image on the image buffer.  The tiles combine to form a 
single continuous image, which is then saved as a bitmap file using the
D3DXSaveSurfaceToFile() introduced in DirectX 8.1. 
  
   |   
Below is a function that performs a tile based rendering using a rendering 
function provided by the application.  Provided that your application 
already has a rendering function that is separate from any movement or other 
changes to the scene, this should be fairly easy to integrate. 
  
HRESULT RenderTiled(LPCSTR fileName,         
// name out output file 
                    LPDIRECT3DDEVICE8 
pDev,   // D3D device 
                    int numTiles,            
// divisions per axis 
                   
HRESULT renderFunc())     // pointer 
to render function  
{ 
	   
	HRESULT hr; 
 
	   
	// get the backbuffer description 
	   
	LPDIRECT3DSURFACE8 backbuf; 
    
	if(FAILED(hr=pDev->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&backbuf)))  
		       
		return hr; 
	   
	D3DSURFACE_DESC desc; 
	   
	hr=backbuf->GetDesc(&desc); 
	   
	backbuf->Release(); 
	   
	if(FAILED(hr)) 
		       
		return hr; 
 
	   
	// calculate final image size 
	   
	int imageWidth=desc.Width*numTiles; 
	   
	int imageHeight=desc.Height*numTiles; 
 
	   
	// create the image buffer 
    	LPDIRECT3DSURFACE8 surf; 
    	if (FAILED(hr=pDev->CreateImageSurface(imageWidth, 
						                                           
						imageHeight, 
						                                           
						desc.Format, 
						                                           
						&surf)))  
		       
		return hr; 
 
	   
	// get the current projection matrix and save a copy 
    	D3DXMATRIX oldProj,newProj; 
	   
	pDev->GetTransform(D3DTS_PROJECTION,&oldProj); 
	   
	newProj=oldProj; 
 
	   
	// scale the projection matrix on x and y axis 
    	newProj._11*=numTiles; 
    	newProj._22*=numTiles; 
 
	   
	// loop through the tiles in X 
	   
	for (int i=0;i<numTiles;i++) { 
 
		       
		// offset x coordinates 
		        newProj._31=(numTiles-1)-i*2.0f; 
 
		       
		// loop through the tiles in Y 
		       
		for (int j=0;j<numTiles;j++) { 
 
			           
			// offset y coordinates 
			           
			newProj._32=-((numTiles-1)-j*2.0f); 
 
			           
			// set the modified projection matrix 
		       
		    	pDev->SetTransform(D3DTS_PROJECTION,&newProj); 
 
			           
			// call the rendering function 
			           
			if (FAILED(hr=renderFunc())) { 
				               
				surf->Release(); 
				               
				return hr; 
			           
			} 
 
			           
			// get the back buffer pointer 
		       
		    
	if(FAILED(hr=pDev->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&backbuf))) { 
				               
				surf->Release(); 
				               
				return hr; 
			           
			} 
 
			           
			// copy the tile to the image buffer and release the back buffer 
			           
			RECT destRect; 
			           
			destRect.left=desc.Width*i; 
			           
			destRect.right=destRect.left+desc.Width; 
			           
			destRect.top=desc.Height*j; 
			           
			destRect.bottom=destRect.top+desc.Height; 
			           
			hr=D3DXLoadSurfaceFromSurface(surf,NULL,&destRect, 
										                                           
										  backbuf,NULL,NULL,D3DX_FILTER_NONE,0); 
			           
			backbuf->Release(); 
			           
			if (FAILED(hr)) { 
				               
				surf->Release(); 
				               
				return hr; 
			           
			} 
 
			           
			// show current tile 
			           
			pDev->Present(0,0,0,0); 
		       
		} 
	   
	} 
 
    	// restore projection matrix 
    	pDev->SetTransform(D3DTS_PROJECTION,&oldProj); 
 
	    // save the image to specified file 
    
	hr=D3DXSaveSurfaceToFile(fileName,D3DXIFF_BMP,surf,NULL,NULL); 
	   
	surf->Release(); 
 
	   
	// return status of save to caller 
	   
	return hr; 
}  
  
 |