MULTIMEDIA

Programming with DirectX : Textures in Direct3D 10 (part 2)

1/15/2011 9:51:11 AM

Texture Filtering

Texture filtering is algorithms used by graphics hardware that affect how the mapped image’s contents appear on the surface. They are commonly supported by graphics hardware. By using the right texture filtering, you can improve the quality of textured mapped surfaces. Because images are not displayed to the rendered screen at a 1:1 ratio, artifacts can appear on textured mapped surfaces as objects move away from the camera, close to the camera, or are tilted at an angle. This means that every screen pixel is not shaded with an individual pixel in the image. As the surface with the texture moves away, a single screen pixel can actually have multiple image pixels fall within it. This can cause the images to distort slightly and display artifacts that can damage the rendered look. Among these are aliasing artifacts, which will be discussed in more detail later in this book.

The fastest filtering algorithm is called nearest-neighbor interpolation filtering, also commonly referred to as point-filtering. Point-filtering essentially selects the closest pixel to the point being sampled and uses that as the color. This is the fastest type of filtering because no additional equations need to be solved to compute the color. It simply uses the texture coordinates to find the closest pixel.

The second-fastest type of filtering is called bilinear interpolation, and the third-fastest is called trilinear interpolation. Bilinear interpolation averages each pixel with four surrounding pixels and displays the average instead of the original. This has the effect of slightly softening the images by using a very simple blur and helps reduce various artifacts such as aliasing artifacts, as shown in Figure 4. Trilinear interpolation does the same, but it also includes interpolation between mip maps (multi-resolution maps). Mip maps will be discussed in more detail in the upcoming section.

Figure 4. Aliasing artifacts.


Multi-Resolution Maps

A mip map is a multi-resolution map of a texture image. Let’s say you have a 2D texture that is 512 × 512. In computer graphics you can load the same texture at smaller resolutions and store them all in a single texture object so that the graphics hardware can choose which resolution of the image (hence multi-resolution map) to use. The reasoning for this is fairly straightforward. As objects move away from the camera, their high-resolution detail is not as visible as if you opened the image in an editor such as Adobe Photoshop or if you viewed the image up close. Therefore, if you have an image that is 1024 × 1024, and if that texture is being displayed on a surface that is so far away that the texture looks the way it would if it was 128 × 128, then why send the 1024 × 1024 image down the rendering pipeline? The larger the textures, the larger the bottleneck on the graphics hardware, along with other factors such as the amount of geometry. Figure 5 shows how detail from a far-away texture cannot be seen as easily.

Figure 5. Detail is lost as the object moves far away.


The purpose of mip maps is to reduce the amount of texture data that is processed and passed down the graphics hardware when rendering surfaces at a distance. When mip maps are enabled, the graphics hardware performs all operations and chooses the best resolution to display surfaces that are rendered in a scene. So when using an API such as OpenGL or Direct3D, all you have to do is supply the API with the mip maps or tell the API to generate them if you don’t have multiple resolutions. Reducing the amount of data that is passed down the graphics hardware can lead to better performance, and that is the purpose of mip maps.

Mip maps can be created by choosing a texture format that saves mip maps such as the .DDS image file format. The mip maps can be manually loading from individual images one at a time into a texture object, or they can be generated by the graphics hardware, which is an option OpenGL and Direct3D offer. Mip maps have resolutions that are a factor of 2, and every level of resolution is half the size as the one before it. So if you had a 1024 × 1024 texture and four levels of mip maps, the highest level would be 1024 × 1024, the second level would be 512 × 512, the third level would be 256 × 256, and the fourth level would be 128 × 128. An example of mip maps as they would look side-by-side is shown in Figure 6.

Figure 6. Example of mip maps (512 × 512, 256 × 256, 128 × 128, 64 × 64).


Loading Textures

There are three main ways to create a texture in Direct3D 10. Programmers have the option of loading a texture from a file, loading a texture from memory, or generating one in a pixel shader. The generation of a texture in a shader is called procedural texture generation, and these textures are created using an algorithm.

Throughout the rest of this book we focus on the function D3DX10CreateShaderResourceViewFromFile(). We will use this function to load 2D textures, but it also works on the other texture types discussed in the beginning of this article. The texture functions Direct3D offers include the following.

  • D3DX10CreateShaderResourceViewFromFile()

  • D3DX10CreateShaderResourceViewFromMemory()

  • D3DX10CreateShaderResourceViewFromResource()

  • D3DX10CreateTextureFromFile()

  • D3DX10CreateTextureFromMemory()

  • D3DX10CreateTextureFromResource()

  • D3DX10LoadTextureFromTexture()

  • D3DX10GetImageInfoFromFile()

  • D3DX10GetImageInfoFromMemory()

  • D3DX10GetImageInfoFromResource()

  • D3DX10CreateAsyncTextureInfoProcessor()

  • D3DX10CreateAsyncTextureProcessor()

  • D3DX10FilterTexture()

  • D3DX10ComputeNormalMap()

  • D3DX10SaveTextureToFile()

  • D3DX10SaveTextureToMemory()

To use textures we need a texture object and a shader resource view. The texture object is the texture itself, and the shader resource view is used to allow the shader to access the resource. The D3DX10CreateShaderResourceViewFromFile(), D3DX10CreateShaderResourceViewFromMemory(), and D3DX10CreateShaderResourceViewFromResource() functions are used to create a shader resource view along with a texture object. The result of calling this function is the shader resource view object that has the type ID3D10ShaderResourceView. If you use one of these three functions, you don’t have to create and load the texture object (of type ID3D10Texture2D for 2D textures) separately because these functions will do all of the work of preparing the shader resource view and texture object for you. Later, when we free the texture, we will see that when using this function, we must use the shader resource view to obtain a pointer to the texture so that the texture can be freed from the shader resource view.

The D3DX10CreateShaderResourceViewFromFile() function takes as parameters the Direct3D device object, the name of the texture file to load, an optional D3DX10_IMAGE_LOAD_INFO structure that is used to specify the characteristics of the texture, a thread pump used by multi-threading applications (beyond the scope of this book), the address to the shader resource view that will be created as a result of this function, and an optional address that will store the return value of the function in a multi-threading application if the thread pump was created. The function’s memory has slightly different parameters, offering the size and image pixel data instead of the file name. The resource version has a parameter that represents the resource’s name rather than a texture file name. The D3DX10CreateShaderResourceViewFromFile() function is the most commonly used function in this book.

The D3DX10CreateTextureFromFile(), D3DX10CreateTextureFromMemory(), and D3DX10CreateTextureFromResource() functions are used to create a texture object but do not create the shader resource view object. In contrast, other functions, including the D3DX10CreateShaderResourceViewFromFile() function, create the shader resource view and internally set the texture object to it. If you call one of the shader resource view creation functions, you do not need to call any of these functions that directly create the texture object since it is done automatically. These functions essentially create the texture without the shader resource view.

The D3DX10LoadTextureFromTexture() function is used to load a new texture from an existing texture and takes as parameters the source texture, a D3DX10_TEXTURE_LOAD_INFO descriptor, and an address at which to store the new texture.

The D3DX10GetImageInfoFromFile(), D3DX10GetImageInfoFromMemory(), and D3DX10GetImageInfoFromResource() functions are used to retrieve image information from a texture. This information is stored in a D3DX10_IMAGE_INFO object and includes the image’s width, height, depth, size in bytes, total mip map levels, file format, color format, dimensions, and miscellaneous flags. The resource dimensions can be any of the following:

  • D3D10_RESOURCE_DIMENSION_UNKNOWN

  • D3D10_RESOURCE_DIMENSION_BUFFER

  • D3D10_RESOURCE_DIMENSION_TEXTURE1D

  • D3D10_RESOURCE_DIMENSION_TEXTURE2D

  • D3D10_RESOURCE_DIMENSION_TEXTURE3D

The D3DX10CreateAsyncTextureInfoProcessor() and D3DX10CreateAsyncTextureProcessor() functions are used to create data processors that are used in multi-threaded applications to load a texture. This is an advanced topic that requires an understanding of multi-threading, which is beyond the scope of this book.

The next two texture functions are used to manipulate a texture that is already loaded. The D3DX10FilterTexture() function takes as parameters the texture object to filter, the mip map level of the original texture being filtered, and flags for the filter. The flags can be one of the following values:

  • D3DX10_DEFAULT

  • D3DX10_FILTER_NONE

  • D3DX10_FILTER_POINT (nearest neighbor filtering)

  • D3DX10_FILTER_LINEAR (bilinear filtering)

  • D3DX10_FILTER_TRIANGLE

  • D3DX10_FILTER_BOX

  • D3DX10_FILTER_MIRROR_U

  • D3DX10_FILTER_MIRROR_V

  • D3DX10_FILTER_MIRROR_W

  • D3DX10_FILTER_MIRROR (same as D3DX10_FILTER_MIRROR_U / D3DX10_FILTER_MIRROR_V / D3DX10_FILTER_MIRROR_W)

  • D3DX10_FILTER_DITHER

  • D3DX10_FILTER_DITHER_DIFFUSION

  • D3DX10_FILTER_SRGB_IN

  • D3DX10_FILTER_SRGB_OUT

  • D3DX10_FILTER_SRGB (same as D3DX10_FILTER_SRGB_IN / D3DX10_FILTER_SRGB_OUT)

The second function is the D3DXComputeNormalMap(), which is used to take a texture and to convert it to a normal map image.

The last texture functions are used to save a texture to a file or to memory. The D3DX10SaveTextureToFile() function takes as parameters the texture object that is to be saved, a D3DX10_IMAGE_FILE_FORMAT description object, and the name of the file that will be created. The second texture-saving function is called D3DX10SaveTextureToMemory(), and it takes as parameters the texture object to be saved, the image format description, an out address to a ID3D10BLOG object that will store the texture in memory, and optional flags.

If you want to create the texture and shader resource view separately, you can call D3DX10CreateTextureFromFile() to create the texture, or you can call one of the other texture creation functions—for example, CreateShaderResourceView()—to create only the resource view. The CreateShaderResourceView() takes as parameters the resource (such as the ID3D10Texture2D texture that is created by calling the D3DX10CreateTextureFromFile() function), the D3D10_SHADER_RESOURCE_VIEW_DESC, which is the descriptor object that specifies the characteristics of the shader resource view object is being created, and the address at which to store the shader resource view as an ID3D10ShaderResourceView object.

Applying Textures

Objects and surfaces are rendered in Direct3D 10 using effect shaders .The effect shaders themselves are objects of the ID3D10Effect type. Inside each shader there can be one or more techniques, which are essentially implementations of rendering effects.

To apply a texture to a technique so that a shader bound to that technique can access it, we need to create an ID3D10EffectShaderResourceVariable object. This variable will bind the application to the shader so that a value can be stored inside it and be accessed by the shaders. Tthis is done by calling the technique object’s GetVariableByName() function (or an equivalent access function) and calling AsShaderResource() on the returned object to gain access to the ID3D10EffectShaderResourceVariable object that will be used to set the variable or in this case the texture.

Once access to the shader variable is obtained, we set the texture by calling the SetResource() function on the ID3D10EffectShaderResourceVariable object. For example, if the ID3D10EffectShaderResourceVariable object was named g_decalEffectVar, and if we had a texture object named g_decal, we could set it like so:

g_decalEffectVar->SetResource(g_decal);

When initially creating the ID3D10EffectShaderResourceVariable object, we create it after the shader has been initially loaded like so:

HRESULT hr = D3DX10CreateEffectFromFile("TextureMap.fx",
NULL, NULL, "fx_4_0", shaderFlags, 0, g_d3dDevice, NULL, NULL,
&g_shader, NULL, NULL);

if(FAILED(hr))
return false;

g_effect = g_shader->GetTechniqueByName("TextureMapping");

g_decalEffectVar = g_shader->GetVariableByName(
"decal")->AsShaderResource();

When you access the shader variable, the name passed into GetVariableByName() must match the variable name that is defined inside the shader.

Freeing Textures

To free a texture, you can call Release() on the ID3D10Texture2D object. This will work if you manually created the texture object directly, but if you loaded the texture from a file using a Direct3D 10 helper function such as D3DX10CreateShaderResourceViewFromFile(), then instead of an ID3D10Texture2D object, you would have an ID3D10ShaderResource object. Even with the shader resource view object, you still have to release the ID3D10Texture2D object it contains. To do this, if you don’t have a pointer to the texture but have one to the shader resource view, you call the GetResource() function of the ID3D10ShaderResource object and pass to it an ID3D10Resource pointer that will point to the texture resource object.

Since ID3D10Resource is a base class of ID3D10Texture2D, you can call Release() on this returned base object to release the texture asset. You can then call Release() on the ID3D10ShaderResource object to release the shader resource view from memory. Keep in mind that releasing only the shader resource view will not release the texture object to which the shader resource view is attached, so this must be done as a separate task. An example of this is shown next, where g_decal is assumed to be the shader resource view that was created as a result of calling the D3DX10CreateShaderResourceViewFromFile() function.

if(g_decal)
{
ID3D10Resource *pRes;
g_decal->GetResource(&pRes);

pRes->Release();
g_decal->Release();
}

You only need to call GetResource() to obtain a pointer to the texture object if you don’t already have it. If you already have theID3D10Texture2D object, you can call Release() on that and Release() on any shader resource view that uses it.


Other  
  •  Programming with DirectX : Shading and Surfaces - Types of Textures
  •  iPhone 3D Programming : Adding Shaders to ModelViewer (part 2)
  •  iPhone 3D Programming : Adding Shaders to ModelViewer (part 1) - New Rendering Engine
  •  iPhone 3D Programming : Adding Depth and Realism - Shaders Demystified
  •  Programming with DirectX : Transformation Demo
  •  Programming with DirectX : View Transformations
  •  Programming with DirectX : World Transformations
  •  Programming with DirectX : Projection Transformations
  •  iPhone 3D Programming : Adding Depth and Realism - Lighting Up (part 2)
  •  iPhone 3D Programming : Adding Depth and Realism - Lighting Up (part 1)
  •  iPhone 3D Programming : Adding Depth and Realism - Surface Normals (part 2)
  •  iPhone 3D Programming : Adding Depth and Realism - Surface Normals (part 1)
  •  iPhone 3D Programming : Adding Depth and Realism - Filling the Wireframe with Triangles
  •  iPhone 3D Programming : Adding Depth and Realism - Creating and Using the Depth Buffer
  •  iPhone 3D Programming : Adding Depth and Realism - Examining the Depth Buffer
  •  iPhone 3D Programming : HelloCone with Fixed Function
  •  iPhone 3D Programming : Vector Beautification with C++
  •  jQuery 1.3 : An image carousel
  •  jQuery 1.3 : Headline rotator
  •  Silverlight : Print a Document
  •  
    Most View
    Rosewill Launches Armor Evolution Mid-Tower Case
    Keep Kids Online Safely (Part 1)
    Extending the Real-Time Communications Functionality of Exchange Server 2007 : Installing OCS 2007 (part 2)
    Buyer’s Guide: e-Readers That Fits Your Needs Best (Part 1)
    Thunderbolt External Drives (Part 1)
    Apple iPhone 5 - Fails To Return To The Top (Part 2)
    Syncing And Streaming (Part 2) - Apple TV, The remote app
    Preparing Your Windows 8 PC : Adding Devices in Windows 8 (part 1) - Viewing Installed Devices
    Group Test: Free Office Suites (Part 3) - LibreOffice
    Turn An Old Computer Into A Server Using Ubuntu (Part 2)
    Top 10
    The NZXT Kraken X40 Compact Liquid Cooler Review (Part 3)
    The NZXT Kraken X40 Compact Liquid Cooler Review (Part 2)
    T-Mobile’s Samsung Galaxy Note II Review (Part 6)
    T-Mobile’s Samsung Galaxy Note II Review (Part 5)
    T-Mobile’s Samsung Galaxy Note II Review (Part 4)
    T-Mobile’s Samsung Galaxy Note II Review (Part 3)
    T-Mobile’s Samsung Galaxy Note II Review (Part 2)
    T-Mobile’s Samsung Galaxy Note II Review (Part 1)
    Sony Cybershot DSC-TF1 - Affordable Water-Resistant Camera
    Buffalo MiniStation Slim 500GB External Hard Drive