Mipmaps

[Previous] [Next]

One advanced texturing capability Direct3D provides is mipmapping. A mipmap consists of a series of textures, each containing a progressively lower resolution of an image (bitmap) that represents the texture. Each level in the mipmap sequence has a height and width that is half of the height and width of the previous level. You can specify that the textures be either square or rectangular. The mipmap technique is a good way to ensure that textures retain their realism and quality as you move closer or further away from them. Direct3D will automatically pick the appropriate mipmap level to texture from. Mipmaps also provide an effective way of reducing memory traffic for textures so that you don't have to render a single texture for all resolutions. When a texture appears at a distance, Direct3D is required to work with a small texture (the texture might be only 2 × 2 at a distance) rather than mapping the complete, full-size image (which might contain 256 × 256 texels) down to the 2 × 2 screen space the polygon requires.

Mipmapping yields a much more realistic scene than simply using a single texture at any resolution. Although properly filtering the whole 256 × 256 texels into 2 × 2 pixels would look great, it would take too long. To maximize speed, Direct3D samples a small number of texels for each pixel from various places in the texture map. Because the result looks fairly poor when the texture map is much bigger than the rendered primitive, you're better off sticking with mipmapping.

Direct3D picks the mipmap level (or levels) for which the texel-to-pixel ratio is closest to 1. For example, if a polygon is approximately 128 × 128 pixels, Direct3D uses the 128 × 128 mipmap level. In general, as you approach the object, Direct 3D replaces smaller textures with larger ones. Or, if the object's resolution falls between two mipmap levels, Direct3D can combine texels from the two mipmap levels to determine the final colors to render.

Direct 3D stores the mipmap you produce as a chain of attached surfaces. The texture with the highest resolution is stored as the first element—the head—of the chain. The texture with the next highest resolution is then attached to the head of the chain. Each successive texture is attached to the previous texture until you reach the lowest resolution image.

Figure 8-1 illustrates a mipmap texture set containing mipmap levels of the following sizes: 256 × 256, 128 × 128, 64 × 64, 32 × 32, 16 × 16, 8 × 8, 4 × 4.

click to view at full size.

Figure 8-1 Mipmap texture set

The texture shown in Figure 8-1 is one you'd apply to create a brick wall. The first image has the highest resolution in the chain. Each successive image is scaled down to the next power of 2 in both width and height. When you're as far from the wall as possible, Direct3D uses the smallest texture in the mipmap chain (4 × 4 in this set).

The sections that follow describe the steps you need to take to work with a set of mipmaps.

Creating a Mipmap Texture

You can create a surface that represents the levels of a mipmap by using the DDSCAPS_MIPMAP and DDSCAPS_COMPLEX flags in the DDSURFACEDESC2 structure. You also need to use the DDSCAPS_TEXTURE flag because all mipmaps are textures. The DDSURFACEDESC2 structure is then passed to the IDirectDraw7::CreateSurface method. This call will automatically create all the mipmap levels from the original texture.

Direct3D uses dimensions that are powers of 2 (1, 2, 4, 8, and so on) for all mipmap textures but does not generally require a texture's width to equal its height (although a few Direct3D devices do require this—check the D3DPTEXTURECAPS_SQUAREONLY caps bit). The following code builds a chain of mipmap levels (256 × 256, 128 × 128, 64 × 64, 32 × 32, 16 ×16, 8 × 8, 4 × 4, 2 × 2, and 1 × 1) using the IDirectDraw7::CreateSurface method:

 // Get size information for the top-level bitmap. BITMAP bm;  GetObject( m_hbmBitmap[0], sizeof(BITMAP), &bm );  // Set up and create the mipmap surface. DDSURFACEDESC2 ddsd; ZeroMemory( &ddsd, sizeof(DDSURFACEDESC2) ); ddsd.dwSize          = sizeof(DDSURFACEDESC2); ddsd.dwFlags         = DDSD_CAPS | DDSD_MIPMAPCOUNT |                        DDSD_WIDTH | DDSD_HEIGHT |                        DDSD_PIXELFORMAT; ddsd.ddsCaps.dwCaps  = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP |                        DDSCAPS_COMPLEX; ddsd.dwMipMapCount   = m_dwMipMapCount; ddsd.dwWidth         = bm.bmWidth; ddsd.dwHeight        = bm.bmHeight; // Get the device caps. D3DDEVICEDESC7 ddDesc; DWORD         dwDeviceCaps; if( FAILED( pd3dDevice->GetCaps( &ddDesc ) ) )     return E_FAIL; dwDeviceCaps = ddDesc.dpcTriCaps.dwTextureCaps; // Turn on texture management for hardware devices. if( IsEqualIID(ddDesc.deviceGUID, IID_IDirect3DHALDevice) ||      IsEqualIID(ddDesc.deviceGUID, IID_IDirect3DTnLHalDevice) )     ddsd.ddsCaps.dwCaps2 = DDSCAPS2_TEXTUREMANAGE; else     ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; // Enumerate a good texture format. Search for a 16-bit // format first. DDSURFACEDESC2 ddsdSearch; ddsdSearch. ddpfPixelFormat.dwRGBBitCount = 16; pd3dDevice->EnumTextureFormats( TextureSearchCallback,                                 &ddsdSearch );      // If a 16-bit format wasn't found, check for a 32-bit format. if( 16 != ddsdSearch.ddpfPixelFormat.dwRGBBitCount ) {     ddsdSearch. ddpfPixelFormat.dwRGBBitCount = 32;     pd3dDevice->EnumTextureFormats( TextureSearchCallback,                                         &ddsdSearch );     if( 32 != ddsdSearch.ddpfPixelFormat.dwRGBBitCount )         return E_FAIL; } // If a good texture format is found, use it to create // the surface. memcpy( &ddsd.ddpfPixelFormat, &ddsdSearch.ddpfPixelFormat,         sizeof(DDPIXELFORMAT) ); // Get a DDraw pointer (from the device's render target) for  // creating surfaces. The Release calls just serve to decrement  // the reference count, but the pointers are still valid. LPDIRECTDRAWSURFACE7 pddsRender; LPDIRECTDRAW7        pDD  = NULL; pd3dDevice->GetRenderTarget( &pddsRender ); pddsRender->GetDDInterface( (VOID**)&pDD ); pddsRender->Release(); // Create the mipmap surface. if( FAILED( pDD->CreateSurface( &ddsd, &m_pddsSurface, NULL ) ) ) {     pDD->Release();     return E_FAIL; } // Done with DirectDraw. pDD->Release(); 

You can set the number of mipmap levels using the dwMipMapCount member. If you don't specify a dwMipMapCount value, mipmap levels will be created all the way down to 1x1. The call to IDirectDraw7::CreateSurface will create a chain of surfaces, the first of which the method will size according to the width and height the code specifies in the ddsd.dwWidth and ddsd.dwHeight members. Each of the remaining surfaces in the chain is a power of 2 smaller than the previous surface.

Accessing the Mipmap Levels

After creating the DirectDraw surface, you can traverse the mipmap surface chain by using the IDirectDrawSurface7::GetAttachedSurface methods and specifying the DDSCAPS_MIPMAP and DDSCAPS_TEXTURE flags in the DDSCAPS2 structure.

The following code segment illustrates how to traverse a mipmap chain from the highest to the lowest resolution.

 // Loop through each surface in the mipmap, copying the bitmap  // to the temporary surface and then blitting the temporary // surface to the real one. LPDIRECTDRAWSURFACE7 pddsDest = m_pddsSurface; for( WORD wNum=0; wNum < m_dwMipMapCount; wNum++ ) {     // Copy the bitmap image to the surface.     BITMAP bm;      GetObject( m_hbmBitmap[wNum], sizeof(BITMAP), &bm );      // Create a DC and set up the bitmap.     HDC hdcBitmap = CreateCompatibleDC( NULL );     if( NULL == hdcBitmap )         return E_FAIL;     SelectObject( hdcBitmap, m_hbmBitmap[wNum] );     HDC hdcSurface;     if( SUCCEEDED( pddsDest->GetDC( &hdcSurface ) ) )     {         BitBlt( hdcSurface, 0, 0, bm.bmWidth, bm.bmHeight,                  hdcBitmap, 0, 0, SRCCOPY );         pddsDest->ReleaseDC( hdcSurface );     }     DeleteDC( hdcBitmap );     // Get the next surface in the chain. Do a Release call to     // avoid increasing the reference counts on the surfaces.     DDSCAPS2 ddsCaps;     ddsCaps.dwCaps  = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP;     ddsCaps.dwCaps2 = 0;     ddsCaps.dwCaps3 = 0;     ddsCaps.dwCaps4 = 0;     if( SUCCEEDED( pddsDest->GetAttachedSurface( &ddsCaps,                                                  &pddsDest ) ) )         pddsDest->Release(); } 

Applications need to manually traverse a mipmap chain to load bitmap data into each surface in the chain. This is typically the only reason to traverse the chain.

Direct3D explicitly stores the number of levels in a mipmap chain. When an application obtains the surface description of a mipmap (by calling the IDirectDrawSurface7::Lock or IDirectDrawSurface7::GetSurfaceDesc method), the dwMipMapCount member of the DDSURFACEDESC2 structure contains the number of levels in the mipmap, including the top level. For levels other than the top level in the mipmap, the dwMipMapCount member specifies the number of levels from that mipmap to the smallest mipmap in the chain.

Setting the Mipmap LOD Bias

Before we conclude our discussion of mipmaps, let's examine one more helpful texturing technique. Setting the mipmap level of detail (LOD) bias allows you to perform a few special filtering effects. If you set a positive bias on a mipmap texture, the resulting image will be sharper but more aliased. If you set a negative bias, the texture image will look blurred. You can control the LOD bias by setting the D3DRENDERSTATE_MIPMAPLODBIAS state.



Inside Direct3D
Inside Direct3D (Dv-Mps Inside)
ISBN: 0735606137
EAN: 2147483647
Year: 1999
Pages: 131

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net