Erstellen von Texturressourcen (Direct3D 10)

Eine Texturressource ist eine strukturierte Sammlung von Daten. In der Regel werden Farbwerte in Texturen gespeichert und während des Renderings durch die Pipeline in verschiedenen Phasen sowohl für eingabe als auch für die Ausgabe zugegriffen. Das Erstellen von Texturen und das Definieren ihrer Verwendung ist ein wichtiger Bestandteil des Renderns von interessanten Szenen in Direct3D 10.

Obwohl Texturen in der Regel Farbinformationen enthalten, können texturen mit unterschiedlichen DXGI_FORMAT unterschiedliche Arten von Daten gespeichert werden. Diese Daten können dann von der Direct3D 10-Pipeline auf nicht herkömmliche Weise genutzt werden.

Für alle Texturen gelten Grenzwerte für die Menge an Arbeitsspeicher, die sie verbrauchen und wie viele Texel sie enthalten. Diese Grenzwerte werden durch Ressourcenkonstanten angegeben.

Erstellen einer Textur aus einer Datei

Hinweis

Die D3DX-Hilfsprogrammbibliothek ist für Windows 8 veraltet und wird für Windows Store-Apps nicht unterstützt.

 

Wenn Sie eine Textur in Direct3D 10 erstellen, müssen Sie auch eine Ansicht erstellen. Eine Ansicht ist ein Objekt, das dem Gerät mitteilt, wie beim Rendern auf eine Textur zugegriffen werden soll. Die gängigste Methode für den Zugriff auf eine Textur besteht darin, mithilfe eines Shaders daraus zu lesen. Eine Shader-Ressourcenansicht teilt einem Shader mit, wie beim Rendern aus einer Textur gelesen werden soll. Die Art der Ansicht, die eine Textur verwendet, muss beim Erstellen angegeben werden.

Das Erstellen einer Textur und das Laden der anfänglichen Daten können auf zwei verschiedene Arten erfolgen: Erstellen sie eine Textur und eine Ansicht separat, oder erstellen Sie gleichzeitig die Textur und die Ansicht. Die API bietet beide Techniken, sodass Sie auswählen können, welche Ihren Anforderungen am besten entspricht.

Separates Erstellen von Textur und Ansicht

Die einfachste Möglichkeit zum Erstellen einer Textur besteht darin, sie aus einer Bilddatei zu laden. Um die Textur zu erstellen, füllen Sie einfach eine Struktur aus, und geben Sie den Namen der Textur an 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 );

Die D3DX-Funktion D3DX10CreateTextureFromFile führt drei Aufgaben aus: Erstens erstellt sie ein Direct3D 10-Texturobjekt; zweitens liest es die Eingabebilddatei; drittens werden die Bilddaten im Texturobjekt gespeichert. Im obigen Beispiel wird eine BMP-Datei geladen, die Funktion kann jedoch eine Vielzahl von Dateitypen laden.

Das Bindungsflag gibt an, dass die Textur als Shaderressource erstellt wird, die es einer Shaderstufe ermöglicht, während des Renderings aus der Textur zu lesen.

Im obigen Beispiel werden nicht alle Ladeparameter angegeben. In der Tat ist es oft vorteilhaft, die Ladeparameter einfach auf null zu setzen, da D3DX auf der Grundlage des Eingabebilds geeignete Werte auswählen kann. Wenn das Eingabebild alle Parameter bestimmen soll, mit denen die Textur erstellt wird, geben Sie einfach null für den loadInfo-Parameter wie folgt an:

D3DX10CreateTextureFromFile( pDevice, L"sample.bmp", NULL, NULL, &pTexture, NULL );

Die Angabe von NULL für die Ladeinformationen ist eine einfache, aber leistungsstarke Verknüpfung.

Nachdem nun eine Textur erstellt wurde, müssen Sie eine Shader-Ressourcenansicht erstellen, damit die Textur als Eingabe an einen Shader gebunden werden kann. Da D3DX10CreateTextureFromFile einen Zeiger auf eine Ressource und keinen Zeiger auf eine Textur zurückgibt, müssen Sie den genauen Typ der geladenen Ressource bestimmen, und anschließend können Sie mithilfe von CreateShaderResourceView eine Shaderressourcenansicht erstellen.

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 );

Obwohl im obigen Beispiel eine 2D-Shader-Ressourcenansicht erstellt wird, ist der Code zum Erstellen anderer Shader-Ressourcenansichtstypen sehr ähnlich. Jede Shaderphase kann diese Textur jetzt als Eingabe verwenden.

Die Verwendung von D3DX10CreateTextureFromFile und CreateShaderResourceView zum Erstellen einer Textur und der zugehörigen Ansicht ist eine Möglichkeit, eine Textur vorzubereiten, die an eine Shaderphase gebunden werden soll. Eine weitere Möglichkeit besteht darin, die Textur und ihre Ansicht gleichzeitig zu erstellen, was im nächsten Abschnitt erläutert wird.

Gleichzeitiges Erstellen von Textur und Ansicht

Direct3D 10 erfordert sowohl eine Textur als auch eine Shaderressourcenansicht, um während der Laufzeit aus einer Textur zu lesen. Da die Erstellung einer Textur und einer Shaderressourcenansicht eine solche häufige Aufgabe ist, stellt D3DX das D3DX10CreateShaderResourceViewFromFile bereit, um dies für Sie zu tun.

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 );

Mit einem einzelnen D3DX-Aufruf werden sowohl die Textur- als auch die Shader-Ressourcenansicht erstellt. Die Funktionalität des loadInfo-Parameters ist unverändert. Sie können damit anpassen, wie die Textur erstellt wird, oder die erforderlichen Parameter aus der Eingabedatei ableiten, indem Sie NULL für den loadInfo-Parameter angeben.

Das id3D10ShaderResourceView-Objekt , das von der D3DX10CreateShaderResourceViewFromFile-Funktion zurückgegeben wird, kann später verwendet werden, um die ursprüngliche ID3D10Resource-Schnittstelle abzurufen, wenn sie benötigt wird. Dies kann durch Aufrufen der GetResource-Methode erfolgen.

D3DX bietet eine einzelne Funktion zum Erstellen einer Textur- und Shaderressourcensicht als Konvienience. Es liegt an Ihnen, zu entscheiden, welche Methode zum Erstellen einer Textur und Ansicht den Anforderungen Ihrer Anwendung am besten entspricht.

Nachdem Sie nun wissen, wie Sie eine Textur und deren Shader-Ressourcenansicht erstellen, erfahren Sie im nächsten Abschnitt, wie Sie mit einem Shader Stichproben aus dieser Textur lesen können.

Erstellen leerer Texturen

Manchmal möchten Anwendungen eine Textur erstellen und die Daten berechnen, die in der Textur gespeichert werden sollen, oder die Grafikpipeline verwenden, um diese Textur zu rendern und die Ergebnisse später in einer anderen Verarbeitung zu verwenden. Diese Texturen können von der Grafikpipeline oder von der Anwendung selbst aktualisiert werden, je nachdem, welche Art von Verwendung für die Textur bei der Erstellung angegeben wurde.

Rendern in einer Textur

Der häufigste Fall, dass eine leere Textur erstellt wird, die während der Laufzeit mit Daten gefüllt werden soll, ist der Fall, wenn eine Anwendung in einer Textur rendern und dann die Ergebnisse des Renderingvorgangs in einem nachfolgenden Durchlauf verwenden möchte. Texturen, die zu diesem Zweck erstellt wurden, sollten die Standardverwendung angeben.

Im folgenden Codebeispiel wird eine leere Textur erstellt, die die Pipeline in rendern und anschließend als Eingabe für einen Shader verwenden kann.

// 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 );

Zum Erstellen der Textur muss die Anwendung einige Informationen zu den Eigenschaften der Textur angeben. Die Breite und Höhe der Textur in Texels ist auf 256 festgelegt. Für dieses Renderziel benötigen wir lediglich eine einzelne Mipmap-Ebene. Es ist nur ein Renderziel erforderlich, damit die Arraygröße auf 1 festgelegt ist. Jedes Texel enthält vier 32-Bit-Gleitkommawerte, mit denen sehr genaue Informationen gespeichert werden können (siehe DXGI_FORMAT). Ein Beispiel pro Pixel ist alles, was benötigt wird. Die Verwendung ist auf den Standardwert festgelegt, da dies die effizienteste Platzierung des Renderziels im Arbeitsspeicher ermöglicht. Schließlich wird die Tatsache angegeben, dass die Textur zu unterschiedlichen Zeitpunkten als Renderziel und als Shaderressource gebunden wird.

Texturen können nicht für das direkte Rendern in der Pipeline gebunden werden. verwenden Sie eine Renderzielansicht, wie im folgenden Codebeispiel gezeigt.

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 );

Das Format der Renderzielansicht wird einfach auf das Format der ursprünglichen Textur festgelegt. Die Informationen in der Ressource sollten als 2D-Textur interpretiert werden, und wir möchten nur die erste Mipmap-Ebene des Renderziels verwenden.

Ähnlich wie eine Renderzielansicht erstellt werden muss, damit das Renderziel für die Ausgabe an die Pipeline gebunden werden kann, muss eine Shaderressourcensicht erstellt werden, damit das Renderziel als Eingabe an die Pipeline gebunden werden kann. Im folgenden Codebeispiel wird dies veranschaulicht.

// 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 );

Die Parameter der Beschreibungen der Shader-Ressourcenansicht ähneln denen von Renderzielansichtsbeschreibungen und wurden aus den gleichen Gründen ausgewählt.

Manuelles Füllen von Texturen

Manchmal möchten Anwendungen Werte zur Laufzeit berechnen, manuell in eine Textur einfügen und diese dann von der Grafikpipeline in späteren Renderingvorgängen verwenden lassen. Dazu muss die Anwendung eine leere Textur so erstellen, dass die CPU auf den zugrunde liegenden Arbeitsspeicher zugreifen kann. Dazu erstellen Sie eine dynamische Textur und erhalten Zugriff auf den zugrunde liegenden Speicher, indem Sie eine bestimmte Methode aufrufen. Die erforderliche Vorgehensweise wird im folgenden Codebeispiel veranschaulicht:

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 );

Beachten Sie, dass das Format auf 32 Bits pro Pixel festgelegt ist, wobei jede Komponente durch 8 Bits definiert ist. Der Verwendungsparameter wird auf dynamisch festgelegt, während die Bindungsflags festgelegt sind, um anzugeben, dass auf die Textur von einem Shader zugegriffen wird. Der Rest der Texturbeschreibung ähnelt dem Erstellen eines Renderziels.

Durch Aufrufen von Map kann die Anwendung auf den zugrunde liegenden Arbeitsspeicher der Textur zugreifen. Der abgerufene Zeiger wird dann verwendet, um die Textur mit Daten zu füllen. Dies ist im folgenden Codebeispiel zu sehen.

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) );

Mehrere Rendertargets

Bis zu acht Renderzielansichten können gleichzeitig an die Pipeline gebunden werden (mit OMSetRenderTargets). Für jedes Pixel (oder jedes Beispiel, wenn Multisampling aktiviert ist) erfolgt die Überblendung unabhängig für jede Renderzielansicht. Zwei der Blendzustandsvariablen – BlendEnable und RenderTargetWriteMask – sind Arrays von acht, wobei jedes Arraymember einer Renderzielansicht entspricht. Wenn Sie mehrere Renderziele verwenden, muss jedes Renderziel denselben Ressourcentyp aufweisen (Puffer, 1D-Textur, 2D-Texturarray usw.) und die gleiche Dimension (Breite, Höhe, Tiefe für 3D-Texturen und Arraygröße für Texturarrays). Wenn die Renderziele multisampled sind, müssen sie alle die gleiche Anzahl von Stichproben pro Pixel aufweisen.

Unabhängig davon, wie viele Renderziele aktiv sind, kann nur ein Tiefenschablonenpuffer aktiv sein. Wenn Texturarrays als Renderziele verwendet werden, müssen alle Ansichtsdimensionen übereinstimmen. Die Renderziele müssen nicht das gleiche Texturformat aufweisen.

Ressourcen (Direct3D 10)