Textures in DirectX Graphics
On this page:
What are Textures?
In computer graphics, a texture is a data file (an image) used to add detail, color, or other surface properties to a 3D model or 2D sprite. Textures are mapped onto the surfaces of objects, allowing for realistic or stylized appearances without needing to model every minute detail. DirectX provides robust support for various texture types and operations.
Textures in DirectX can represent a wide range of data beyond just diffuse color:
- Diffuse Textures: Define the base color of a surface.
- Normal Maps: Store surface normal information to simulate intricate surface details and lighting effects.
- Specular Maps: Control the intensity and color of specular highlights.
- Height Maps (Displacement Maps): Provide displacement information to actually deform the geometry.
- Emissive Maps: Define areas of the surface that appear to emit light.
- Cubemaps: Spherical textures used for environment mapping, often representing reflections or skyboxes.
Texture Formats
DirectX supports a wide array of texture formats to optimize for memory, performance, and visual quality. These formats dictate how pixel data is stored, including color channels (RGBA), bit depth, and compression. Common formats include:
- Uncompressed Formats:
DXGI_FORMAT_R8G8B8A8_UNORM
: 32-bit RGBA, 8 bits per channel, normalized.DXGI_FORMAT_B8G8R8A8_UNORM
: Similar to above but in BGRA order.DXGI_FORMAT_R16G16B16A16_FLOAT
: 64-bit RGBA, 16 bits per channel, floating-point.
- Compressed Formats (DXT/BC): Provide significant memory and bandwidth savings with minimal visual loss.
DXGI_FORMAT_BC1_UNORM
(DXT1): 4 bits per pixel, no alpha.DXGI_FORMAT_BC3_UNORM
(DXT5): 8 bits per pixel, alpha.DXGI_FORMAT_BC7_UNORM
: High-quality compression for color textures with alpha.
- Depth-Stencil Formats: Used for depth and stencil buffers.
DXGI_FORMAT_D32_FLOAT_S8X24_UINT
: 32-bit float depth, 8-bit stencil.
Choosing the appropriate format is crucial for balancing visual fidelity, memory usage, and performance. The exact list of supported formats can vary depending on the hardware and DirectX feature level.
Texture Creation
Textures are typically created from image files (like .png, .jpg, .dds) or programmatically. In DirectX, you create a texture resource using methods that specify its dimensions, format, usage flags, and initial data.
For example, creating a 2D texture from memory:
ID3D11Device* pDevice = /* Get your D3D11 device */;
D3D11_TEXTURE2D_DESC texDesc;
ZeroMemory(&texDesc, sizeof(texDesc));
texDesc.Width = 256;
texDesc.Height = 256;
texDesc.MipLevels = 1; // Or calculate number of mip levels
texDesc.ArraySize = 1;
texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
texDesc.Usage = D3D11_USAGE_IMMUTABLE; // Or D3D11_USAGE_DEFAULT
texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
texDesc.CPUAccessFlags = 0;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
D3D11_SUBRESOURCE_DATA initialData;
ZeroMemory(&initialData, sizeof(initialData));
initialData.pSysMem = pPixelData; // Pointer to your pixel data
initialData.SysMemPitch = 256 * 4; // Bytes per row
initialData.SysMemSlicePitch = 0; // For 2D textures
ID3D11Texture2D* pTexture = nullptr;
HRESULT hr = pDevice->CreateTexture2D(&texDesc, &initialData, &pTexture);
if (SUCCEEDED(hr)) {
// Create shader resource view for use in shaders
ID3D11ShaderResourceView* pShaderResourceView = nullptr;
pDevice->CreateShaderResourceView(pTexture, nullptr, &pShaderResourceView);
// ... use pShaderResourceView
pTexture->Release();
}
The D3D11_USAGE
flag is important: D3D11_USAGE_IMMUTABLE
is for textures that won't change after creation (best performance), while D3D11_USAGE_DEFAULT
allows the GPU to update it. D3D11_USAGE_DYNAMIC
allows CPU updates. D3D11_BIND_SHADER_RESOURCE
indicates the texture will be bound to the shader pipeline.
Texture Sampling
To use a texture in a shader, you need to create a ID3D11ShaderResourceView
(SRV) from the texture resource. This view describes how the shader should access the texture, including which mip level to use and the format for reading.
Inside a shader (HLSL), textures are accessed using Texture2D
and SamplerState
objects:
Texture2D myTexture;
SamplerState mySampler;
// In the pixel shader
float4 texColor = myTexture.Sample(mySampler, texCoord);
The SamplerState
defines how the texture is sampled. This includes:
- Addressing Modes (U, V, W): How coordinates outside the [0, 1] range are handled (e.g.,
D3D11_TEXTURE_ADDRESS_WRAP
,D3D11_TEXTURE_ADDRESS_CLAMP
). - Filter Modes (Minification, Magnification, Mip-Level): How texels are interpolated when the texture is smaller or larger than the surface area it covers (e.g.,
D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT
). - Mipmap LOD Bias: An offset applied to the calculated mipmap level.
Texture Resources
Textures are a type of ID3D11Resource
. Different dimensions and purposes result in specific resource types:
ID3D11Texture1D
: For 1D textures.ID3D11Texture2D
: The most common type, for 2D textures and cubemaps (an array of 2D textures).ID3D11Texture3D
: For volume textures (e.g., medical imaging, complex effects).
When you bind a texture to the pipeline, you bind its ID3D11ShaderResourceView
. This abstraction allows for flexibility in how the underlying texture data is interpreted by shaders.
Example: Creating a Cubemap
Cubemaps are typically represented as an array of 6 ID3D11Texture2D
resources, or a single ID3D11Texture2D
with ArraySize = 6
. The SRV created for a cubemap will have ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE
.
// ... create device and texture descriptions ...
texDesc.ArraySize = 6; // For a cubemap
texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
// ... other properties
// Create the texture array
ID3D11Texture2D* pCubeTexture = nullptr;
pDevice->CreateTexture2D(&texDesc, nullptr, &pCubeTexture);
// Create the shader resource view for the cubemap
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(srvDesc));
srvDesc.Format = texDesc.Format;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
srvDesc.TextureCube.MipLevels = texDesc.MipLevels;
ID3D11ShaderResourceView* pCubeSRV = nullptr;
pDevice->CreateShaderResourceView(pCubeTexture, &srvDesc, &pCubeSRV);
// ... bind pCubeSRV to the shader pipeline ...
pCubeTexture->Release();