Crear recursos de textura (Direct3D 10)

Un recurso de textura es una colección estructurada de datos. Normalmente, los valores de color se almacenan en texturas y se accede a ellos durante la representación por la canalización en varias fases para la entrada y la salida. La creación de texturas y la definición de cómo se usarán es una parte importante de la representación de escenas interesantes en Direct3D 10.

Aunque las texturas normalmente contienen información de color, la creación de texturas con diferentes DXGI_FORMAT les permite almacenar diferentes tipos de datos. Después, esta canalización de Direct3D 10 puede aprovechar estos datos de maneras no tradicionales.

Todas las texturas tienen límites en la cantidad de memoria que consumen y cuántos elementos de textura contienen. Estos límites se especifican mediante constantes de recursos.

Crear una textura a partir de un archivo

Nota

La biblioteca de utilidades D3DX está en desuso para Windows 8 y no es compatible con las aplicaciones de la Tienda Windows.

 

Al crear una textura en Direct3D 10, también debes crear una vista. Una vista es un objeto que indica al dispositivo cómo se debe tener acceso a una textura durante la representación. La forma más común de acceder a una textura es leerla mediante un sombreador. Una vista de recursos de sombreador indicará a un sombreador cómo leer de una textura durante la representación. El tipo de vista que usará una textura se debe especificar al crearla.

La creación de una textura y la carga de sus datos iniciales se pueden realizar de dos maneras diferentes: crear una textura y una vista por separado, o crear la textura y la vista al mismo tiempo. La API proporciona ambas técnicas para que pueda elegir lo que mejor se adapte a sus necesidades.

Crear textura y ver por separado

La manera más fácil de crear una textura es cargarla desde un archivo de imagen. Para crear la textura, simplemente rellene una estructura y proporcione el nombre de la textura a 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 );

La función D3DX D3DX10CreateTextureFromFile hace tres cosas: en primer lugar, crea un objeto de textura Direct3D 10; segundo, lee el archivo de imagen de entrada; en tercer lugar, almacena los datos de imagen en el objeto de textura. En el ejemplo anterior, se carga un archivo BMP, pero la función puede cargar una variedad de tipos de archivo.

La marca de enlace indica que la textura se creará como un recurso de sombreador que permite que una fase del sombreador lea de la textura durante la representación.

En el ejemplo anterior no se especifican todos los parámetros de carga. De hecho, a menudo resulta beneficioso simplemente dejar cero los parámetros de carga, ya que esto permite a D3DX elegir los valores adecuados en función de la imagen de entrada. Si desea que la imagen de entrada determine todos los parámetros con los que se crea la textura, simplemente especifique NULL para el parámetro loadInfo de la siguiente manera:

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

Especificar NULL para la información de carga es un método abreviado sencillo pero eficaz.

Ahora que se ha creado una textura, debe crear una vista de recursos de sombreador para que la textura se pueda enlazar como entrada a un sombreador. Dado que D3DX10CreateTextureFromFile devuelve un puntero a un recurso y no un puntero a una textura, tiene que determinar el tipo exacto de recurso que se cargó y, a continuación, puede crear una vista de recursos de sombreador mediante 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 );

Aunque el ejemplo anterior crea una vista de recursos de sombreador 2D, el código para crear otros tipos de vista de recursos de sombreador es muy similar. Cualquier fase del sombreador ahora puede usar esta textura como entrada.

El uso de D3DX10CreateTextureFromFile y CreateShaderResourceView para crear una textura y su vista asociada es una manera de preparar una textura para enlazarse a una fase del sombreador. Otra manera de hacerlo es crear la textura y su vista al mismo tiempo, que se describe en la sección siguiente.

Crear textura y ver simultáneamente

Direct3D 10 requiere una textura y una vista de recursos de sombreador para leer desde una textura durante el tiempo de ejecución. Dado que la creación de una textura y una vista de recursos de sombreador es una tarea tan común, D3DX proporciona el D3DX10CreateShaderResourceViewFromFile para hacerlo automáticamente.

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

Con una sola llamada D3DX, se crean la textura y la vista de recursos del sombreador. La funcionalidad del parámetro loadInfo no cambia; Puede usarlo para personalizar cómo se crea la textura o derivar los parámetros necesarios del archivo de entrada especificando NULL para el parámetro loadInfo .

El objeto ID3D10ShaderResourceView devuelto por la función D3DX10CreateShaderResourceViewFromFile se puede usar posteriormente para recuperar la interfaz ID3D10Resource original si es necesario. Esto se puede hacer llamando al método GetResource .

D3DX proporciona una sola función para crear una vista de textura y de recursos de sombreador como una convieniencia; es necesario decidir qué método de creación de una textura y vista se adapta mejor a las necesidades de la aplicación.

Ahora que sabe cómo crear una textura y su vista de recursos de sombreador, en la sección siguiente se muestra (leer) de esa textura mediante un sombreador.

Crear texturas vacías

A veces, las aplicaciones querrán crear una textura y calcular los datos que se almacenarán en la textura, o usarán la canalización de gráficos para representarla en esta textura y, posteriormente, usarán los resultados en otro procesamiento. La canalización de gráficos o la propia aplicación podrían actualizar estas texturas, dependiendo del tipo de uso especificado para la textura cuando se creó.

Representación en una textura

El caso más común de crear una textura vacía que se va a rellenar con datos durante el tiempo de ejecución es el caso en el que una aplicación quiere representarse en una textura y, a continuación, usar los resultados de la operación de representación en un paso posterior. Las texturas creadas con este propósito deben especificar el uso predeterminado.

En el ejemplo de código siguiente se crea una textura vacía en la que la canalización se puede representar y, posteriormente, como entrada para un sombreador.

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

La creación de la textura requiere que la aplicación especifique información sobre las propiedades que tendrá la textura. El ancho y alto de la textura en elementos de textura se establece en 256. Para este destino de representación, todo lo que necesitamos es un único nivel de mapa mip. Solo se requiere un destino de representación, por lo que el tamaño de la matriz se establece en 1. Cada elemento de textura contiene cuatro valores de punto flotante de 32 bits, que se pueden usar para almacenar información muy precisa (vea DXGI_FORMAT). Una muestra por píxel es todo lo que se necesitará. El uso se establece en predeterminado porque esto permite la colocación más eficaz del destino de representación en la memoria. Por último, el hecho de que la textura se enlazará como destino de representación y se especifica un recurso de sombreador en distintos momentos en el tiempo.

Las texturas no se pueden enlazar para la representación en la canalización directamente; use una vista render-target como se muestra en el ejemplo de código siguiente.

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

El formato de la vista de destino de representación se establece simplemente en el formato de la textura original. La información del recurso debe interpretarse como una textura 2D y solo queremos usar el primer nivel de mapa MIP del destino de representación.

De forma similar a cómo se debe crear una vista de destino de representación para que el destino de representación se pueda enlazar para la salida a la canalización, se debe crear una vista de recursos de sombreador para que el destino de representación se pueda enlazar a la canalización como entrada. En el ejemplo de código siguiente se muestra esto.

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

Los parámetros de las descripciones de la vista sombreador-recurso son muy similares a los de las descripciones de la vista de destino de representación y se eligieron por las mismas razones.

Rellenar texturas manualmente

A veces, las aplicaciones desean calcular valores en tiempo de ejecución, colocarlos en una textura manualmente y, a continuación, hacer que la canalización de gráficos use esta textura en operaciones de representación posteriores. Para ello, la aplicación debe crear una textura vacía de forma que permita que la CPU acceda a la memoria subyacente. Para ello, se crea una textura dinámica y se obtiene acceso a la memoria subyacente mediante una llamada a un método determinado. En el ejemplo de código siguiente se muestra cómo utilizar este recurso.

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

Tenga en cuenta que el formato se establece en 32 bits por píxel, donde cada componente se define mediante 8 bits. El parámetro de uso se establece en dinámico mientras que un sombreador tendrá acceso a las marcas de enlace para especificar que un sombreador tendrá acceso a la textura. El resto de la descripción de la textura es similar a la creación de un destino de representación.

Llamar a Map permite a la aplicación acceder a la memoria subyacente de la textura. Después, el puntero recuperado se usa para rellenar la textura con datos. Esto se puede ver en el ejemplo de código siguiente.

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

Varios rendertargets

Se pueden enlazar hasta ocho vistas de destino de representación a la canalización a la vez (con OMSetRenderTargets). Para cada píxel (o cada muestra si el muestreo múltiple está habilitado), la combinación se realiza de forma independiente para cada vista de destino de representación. Dos de las variables de estado de mezcla ( BlendEnable y RenderTargetWriteMask) son matrices de ocho, cada miembro de matriz corresponde a una vista de destino de representación. Al usar varios destinos de representación, cada destino de representación debe ser el mismo tipo de recurso (búfer, textura 1D, matriz de texturas 2D, etc.) y debe tener la misma dimensión (ancho, alto, profundidad para texturas 3D y tamaño de matriz para matrices de texturas). Si los destinos de representación son multimuestreo, todos deben tener el mismo número de muestras por píxel.

Solo puede haber un búfer de galería de símbolos de profundidad activo, independientemente de cuántos destinos de representación estén activos. Al usar matrices de texturas como destinos de representación, todas las dimensiones de vista deben coincidir. Los destinos de representación no necesitan tener el mismo formato de textura.

Recursos (Direct3D 10)