Создание ресурсов текстур (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 выбирать подходящие значения на основе входного изображения. Если вы хотите, чтобы входное изображение определяло все параметры, с помощью которого создается текстура, просто укажите NULL для параметра loadInfo следующим образом:

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

Хотя в приведенном выше примере создается представление ресурсов двухмерного шейдера, код для создания других типов представления ресурсов шейдера очень похож. Теперь эта текстура может использоваться в качестве входных данных на любом этапе шейдера.

Использование 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 не изменилась; Его можно использовать для настройки способа создания текстуры или получения необходимых параметров из входного файла, указав значение NULL для параметра loadInfo .

Объект ID3D10ShaderResourceView , возвращенный функцией D3DX10CreateShaderResourceViewFromFile , позже можно использовать для получения исходного интерфейса 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. Для этого целевого объекта отрисовки нам потребуется один уровень MIP-карты. Требуется только один целевой объект отрисовки, поэтому размер массива равен 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 );

Формат представления цели отрисовки просто устанавливается в формат исходной текстуры. Информация в ресурсе должна интерпретироваться как двухd-текстура, и мы хотим использовать только первый уровень MIP-карты целевого объекта отрисовки.

Аналогично созданию представления целевого объекта отрисовки, чтобы целевой объект отрисовки можно было привязать к выходным данным в конвейере, необходимо создать представление ресурса шейдера, чтобы целевой объект отрисовки можно было привязать к конвейеру в качестве входных данных. Это демонстрируется в следующем примере кода.

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

Параметры описаний представлений шейдер-ресурсов очень похожи на параметры описания представлений целевого представления отрисовки и были выбраны по тем же причинам.

Заполнение текстур вручную

Иногда приложениям требуется вычислять значения во время выполнения, помещать их в текстуру вручную, а затем использовать эту текстуру в последующих операциях отрисовки в графическом конвейере . Для этого приложение должно создать пустую текстуру таким образом, чтобы обеспечить ЦП доступ к базовой памяти. Это делается путем создания динамической текстуры и получения доступа к базовой памяти путем вызова определенного метода. В следующем образце кода показано, как это сделать.

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 битами. Параметр использования имеет значение dynamic, а флаги привязки задаются для указания того, к текстуре будет обращаться шейдер. Остальная часть описания текстуры аналогична созданию целевого объекта отрисовки.

Вызов 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 — являются массивами из восьми, каждый элемент массива соответствует представлению целевого объекта отрисовки. При использовании нескольких целевых объектов отрисовки каждый целевой объект отрисовки должен иметь одинаковый тип ресурса (буфер, одномерная текстура, массив двухмерных текстур и т. д.) и иметь одинаковое измерение (ширина, высота, глубина для трехмерных текстур и размер массива для массивов текстур). Если целевые объекты отрисовки имеют несколько выборок, все они должны иметь одинаковое количество выборок на пиксель.

Может быть активен только один буфер трафарета глубины, независимо от количества активных целевых объектов отрисовки. При использовании массивов текстур в качестве целевых объектов отрисовки все измерения представления должны совпадать. Целевые объекты отрисовки не должны иметь одинаковый формат текстуры.

Ресурсы (Direct3D 10)