建立紋理資源 (Direct3D 10)
紋理 資源是結構化的數據集合。 通常,顏色值會儲存在紋理中,然後在輸入和輸出的各種階段由 管線 存取以進行渲染。 建立紋理並定義其使用方式,是轉譯 Direct3D 10 中有趣場景的重要部分。
即使紋理通常包含色彩資訊,但使用不同的 DXGI_FORMAT 建立紋理可讓紋理儲存不同類型的數據。 然後,Direct3D 10 管線就可以以非傳統方式利用這項數據。
所有紋理在記憶體耗用和包含的紋素數量上都有其限制。 這些限制是由 資源常數所指定。
從檔案建立紋理
注意
D3DX 公用程式連結庫 在 Windows 8 中已被不推薦使用,且不支援 Windows 市集應用程式。
當您在 Direct3D 10 中建立紋理時,也需要建立 檢視。 視圖是一種物件,告訴裝置在渲染過程中應如何使用紋理。 存取紋理的方法中,最常見的是使用 著色器來讀取紋理。 著色器資源檢視會告訴著色器如何在渲染期間從紋理中讀取數據。 當您建立紋理時,必須指定紋理所使用的檢視類型。
您可以透過兩種不同的方式建立紋理並載入其初始數據:分別建立紋理和檢視,或同時建立紋理和檢視。 API 提供這兩種技術,讓您選擇更符合您的需求。
分別建立紋理和視圖
建立紋理最簡單的方式是從圖像檔載入它。 若要建立紋理,只要填入一個結構,並提供紋理的名稱來 D3DX10CreateTextureFromFile。
ID3D10Device *pDevice = NULL;
// Initialize D3D10 device...
D3DX10_IMAGE_LOAD_INFO loadInfo;
ZeroMemory( &loadInfo, sizeof(D3DX10_IMAGE_LOAD_INFO) );
loadInfo.BindFlags = D3D10_BIND_SHADER_RESOURCE;
ID3D10Resource *pTexture = NULL;
D3DX10CreateTextureFromFile( pDevice, L"sample.bmp", &loadInfo, NULL, &pTexture, NULL );
D3DX 函式 D3DX10CreateTextureFromFile 執行三件事:首先,它會建立 Direct3D 10 紋理物件:第二,它會讀取輸入圖像檔;第三,它會將影像數據儲存在紋理物件中。 在上述範例中,會載入 BMP 檔案,但函式可以載入各種文件類型。
系結旗標表示,紋理將被建立為著色器讀取的資源,允許著色器階段在渲染期間從紋理中讀取。
上述範例未指定所有載入參數。 事實上,只要將載入參數歸零,通常很有幫助,因為這可讓 D3DX 根據輸入影像選擇適當的值。 如果您想要讓輸入影像決定所有用於建立紋理的參數,您只要為 loadInfo 參數指定 NULL,如下所示:
D3DX10CreateTextureFromFile( pDevice, L"sample.bmp", NULL, NULL, &pTexture, NULL );
為載入資訊指定 NULL 是簡單但功能強大的快捷方式。
現在已建立紋理,您必須建立一個著色器資源視圖,以便將紋理系結為著色器的輸入。 由於 D3DX10CreateTextureFromFile 會傳回資源的指標,而不是紋理的指標,因此您必須判斷載入的確切資源類型,然後使用 createShaderResourceView建立著色器資源檢視。
D3D10_SHADER_RESOURCE_VIEW_DESC srvDesc;
D3D10_RESOURCE_DIMENSION type;
pTexture->GetType( &type );
switch( type )
{
case D3D10_RESOURCE_DIMENSION_BUFFER:
//...
break;
case D3D10_RESOURCE_DIMENSION_TEXTURE1D:
//...
break;
case D3D10_RESOURCE_DIMENSION_TEXTURE2D:
{
D3D10_TEXTURE2D_DESC desc;
ID3D10Texture2D *pTexture2D = (ID3D10Texture2D*)pTexture;
pTexture2D->GetDesc( &desc );
srvDesc.Format = desc.Format;
srvDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = desc.MipLevels;
srvDesc.Texture2D.MostDetailedMip = desc.MipLevels -1;
}
break;
case D3D10_RESOURCE_DIMENSION_TEXTURE3D:
//...
break;
default:
//...
break;
}
ID3D10ShaderResourceView *pSRView = NULL;
pDevice->CreateShaderResourceView( pTexture, &srvDesc, &pSRView );
雖然上述範例會建立 2D 著色器資源檢視,但建立其他著色器資源檢視類型的程式代碼非常類似。 任何著色器階段現在都可以使用此紋理作為輸入。
使用 D3DX10CreateTextureFromFile 和 CreateShaderResourceView 來建立紋理及其相關聯的檢視,是準備紋理以系結至著色器階段的其中一種方式。 另一種方法是同時建立紋理和其視圖,這會在下一節中討論。
同時建立紋理和檢視
Direct3D 10 需要材質和著色器資源檢視,才能在執行過程中從材質中讀取。 由於建立紋理和著色器資源檢視是如此常見,因此 D3DX 會提供 D3DX10CreateShaderResourceViewFromFile 來為您執行此動作。
D3DX10_IMAGE_LOAD_INFO loadInfo;
ZeroMemory( &loadInfo, sizeof(D3DX10_IMAGE_LOAD_INFO) );
loadInfo.BindFlags = D3D10_BIND_SHADER_RESOURCE;
loadInfo.Format = DXGI_FORMAT_BC1_UNORM;
ID3D10ShaderResourceView *pSRView = NULL;
D3DX10CreateShaderResourceViewFromFile( pDevice, L"sample.bmp", &loadInfo, NULL, &pSRView, NULL );
使用單一 D3DX 呼叫時,會建立紋理和著色器資源檢視。 loadInfo 參數的功能保持不變;您可以使用此參數來自定義紋理的生成方式,或透過為 loadInfo 參數指定 NULL,從輸入檔案中衍生出必要的參數。
D3DX10CreateShaderResourceViewFromFile 函式所傳回的 ID3D10ShaderResourceView 物件稍後可用來擷取原始 ID3D10Resource 介面。 呼叫 GetResource 方法即可完成此作業。
D3DX 提供一個方便的函式,用以建立紋理和著色器資源檢視;您可以自行決定哪一種方法最適合您的應用程式需求來建立紋理和檢視。
既然您已瞭解如何建立紋理及其著色器資源檢視,下一節將示範如何使用著色器從該紋理取樣(讀取)。
建立空白紋理
有時候應用程式會想要建立紋理,並計算要儲存在紋理中的數據,或使用圖形 管線 轉譯至此紋理,稍後再使用結果在其他處理中。 圖形管線或應用程式本身可以更新這些紋理,視建立紋理時所指定的 使用量 類型而定。
轉譯至紋理
在運行時間期間建立空白紋理以填滿數據的最常見案例,就是應用程式想要轉譯為紋理,然後在後續的傳遞中使用轉譯作業的結果。 使用此用途建立的紋理應指定預設 使用方式。
下列程式代碼範例會建立空紋理,允許管線進行渲染,並在後續作為 著色器的輸入使用。
// Create the render target texture
D3D10_TEXTURE2D_DESC desc;
ZeroMemory( &desc, sizeof(desc) );
desc.Width = 256;
desc.Height = 256;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
desc.SampleDesc.Count = 1;
desc.Usage = D3D10_USAGE_DEFAULT;
desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
ID3D10Texture2D *pRenderTarget = NULL;
pDevice->CreateTexture2D( &desc, NULL, &pRenderTarget );
建立紋理需要應用程式指定紋理將擁有之屬性的一些資訊。 紋理在材質中以像素為單位的寬度和高度設定為 256。 針對此渲染目標,我們只需要單一 Mipmap 層級。 只需要一個轉譯目標,因此將陣列大小設為 1。 每個紋理像素包含四個32位元浮點數值,可用來儲存非常精細的資訊(請參閱 DXGI_FORMAT)。 每個像素只需一個樣本就足夠了。 使用量會設定為預設值,因為這允許在記憶體中最有效率地放置轉譯目標。 最後,規定了紋理將在不同時間點系結為渲染目標和著色器資源的事實。
紋理無法系結以直接轉譯至管線;使用轉譯目標檢視,如下列程式代碼範例所示。
D3D10_RENDER_TARGET_VIEW_DESC rtDesc;
rtDesc.Format = desc.Format;
rtDesc.ViewDimension = D3D10_RTV_DIMENSION_TEXTURE2D;
rtDesc.Texture2D.MipSlice = 0;
ID3D10RenderTargetView *pRenderTargetView = NULL;
pDevice->CreateRenderTargetView( pRenderTarget, &rtDesc, &pRenderTargetView );
轉譯目標檢視的格式只會設定為原始紋理的格式。 資源中的資訊應該解譯為 2D 紋理,而我們只想要使用轉譯目標的第一個 Mipmap 層級。
類似於必須如何建立轉譯目標檢視,以便將轉譯目標系結至管線輸出,必須建立著色器資源檢視,以便將轉譯目標系結至管線做為輸入。 下列程式代碼範例示範這一點。
// Create the shader-resource view
D3D10_SHADER_RESOURCE_VIEW_DESC srDesc;
srDesc.Format = desc.Format;
srDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
srDesc.Texture2D.MostDetailedMip = 0;
srDesc.Texture2D.MipLevels = 1;
ID3D10ShaderResourceView *pShaderResView = NULL;
pDevice->CreateShaderResourceView( pRenderTarget, &srDesc, &pShaderResView );
著色器資源檢視描述的參數與轉譯目標檢視描述的參數非常類似,而且基於相同原因而選擇。
手動填滿紋理
有時候應用程式想要在運行時間計算值、手動將它們放入紋理中,然後讓圖形 管線 在稍後的轉譯作業中使用這個紋理。 若要這樣做,應用程式必須以這樣的方式建立空紋理,以允許CPU存取基礎記憶體。 這是藉由建立動態紋理,並藉由呼叫特定方法來取得基礎記憶體的存取權來完成。 下列程式代碼範例示範如何執行這項作。
D3D10_TEXTURE2D_DESC desc;
desc.Width = 256;
desc.Height = 256;
desc.MipLevels = desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D10_USAGE_DYNAMIC;
desc.BindFlags = D3D10_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
ID3D10Texture2D *pTexture = NULL;
pd3dDevice->CreateTexture2D( &desc, NULL, &pTexture );
請注意,此格式會設定為每個圖元 32 位,其中每個元件都由 8 位定義。 使用方式參數設定為動態,而系結旗標則設定為紋理將由著色器存取。 紋理描述的其餘部分類似於建立渲染目標。
呼叫 Map 可讓應用程式存取紋理的基礎記憶體。 然後,擷取的指標會用來以數據填滿紋理。 這可以在下列程式代碼範例中看到。
D3D10_MAPPED_TEXTURE2D mappedTex;
pTexture->Map( D3D10CalcSubresource(0, 0, 1), D3D10_MAP_WRITE_DISCARD, 0, &mappedTex );
UCHAR* pTexels = (UCHAR*)mappedTex.pData;
for( UINT row = 0; row < desc.Height; row++ )
{
UINT rowStart = row * mappedTex.RowPitch;
for( UINT col = 0; col < desc.Width; col++ )
{
UINT colStart = col * 4;
pTexels[rowStart + colStart + 0] = 255; // Red
pTexels[rowStart + colStart + 1] = 128; // Green
pTexels[rowStart + colStart + 2] = 64; // Blue
pTexels[rowStart + colStart + 3] = 32; // Alpha
}
}
pTexture->Unmap( D3D10CalcSubresource(0, 0, 1) );
多重渲染目標
一次最多可以將八個轉譯目標檢視系結至管線(使用 OMSetRenderTargets)。 針對每個圖元(或在啟用多重取樣時的每個樣本),獨立針對每個轉譯目標視圖執行混合。 其中兩個混合狀態變數 - BlendEnable 和 RenderTargetWriteMask - 是擁有八個成員的陣列,每個陣列成員都對應至一個轉譯目標檢視。 使用多個轉譯目標時,每個轉譯目標都必須是相同的 資源類型(緩衝區、1D 紋理、2D 紋理陣列等),而且必須具有相同維度(3D 紋理的寬度、高度、深度,以及紋理數位的數位大小)。 如果轉譯目標為多重取樣,則每個像素的樣本數目都必須相同。
不論有多少渲染目標活躍中,只能有一個深度模板緩衝區活躍中。 使用紋理陣列做為轉譯目標時,所有檢視維度都必須相符。 轉譯目標不需要具有相同的紋理格式。
相關主題