netlib.narod.ru< Назад | Оглавление | Далее >

18.5. Пример приложения: мультитекстурирование в пиксельном шейдере

Пример приложения для данной главы демонстрирует мультитекстурирование с использованием пиксельных шейдеров. Пример формирует текстурированный квадрат, отмеченный на рис. 18.2 как «результат» путем смешивания текстуры ящика, текстуры прожектора и текстуры со строкой «Pixel Shader Sample».


Рис. 18.2. Комбинирование текстур

Рис. 18.2. Комбинирование текстур. Пусть b, s и t — это цвета соответствующих текселей из текстуры ящика, текстуры прожектора и текстуры текста соответственно. Тогда цвет их комбинации определяется по формуле c = b  s + t, где обозначает покомпонентное умножение


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

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

Код пиксельного шейдера для реализации мультитекстурирования с использованием трех текстур выглядит следующим образом:

//
// Файл    : ps_multitex.txt
// Описание: Пиксельный шейдер, выполняющий мультитекстурирование
//

//
// Глобальные переменные
//

sampler BaseTex;
sampler SpotLightTex;
sampler StringTex;

//
// Структуры
//

struct PS_INPUT
{
     float2 base      : TEXCOORD0;
     float2 spotlight : TEXCOORD1;
     float2 text      : TEXCOORD2;
};

struct PS_OUTPUT
{
     vector diffuse : COLOR0;
};

//
// Точка входа
//

PS_OUTPUT Main(PS_INPUT input)
{
     // Обнуляем члены выходной структуры
     PS_OUTPUT output = (PS_OUTPUT)0;

     // Выборка данных из соответствующих текстур
     vector b = tex2D(BaseTex, input.base);
     vector s = tex2D(SpotLightTex, input.spotlight);
     vector t = tex2D(StringTex, input.text);

     // Комбинирование цветов текселей
     vector c = b * s + t;

     // Слегка увеличиваем яркость пикселя
     c += 0.1f;

     // Сохраняем результатирующий цвет
     output.diffuse = c;

     return output;
}

Сперва в пиксельном шейдере мы объявляем три объекта выборки — по одному для каждой, участвующей в смешивании текстуры. Затем мы описываем входную и выходную структуры. Обратите внимание, что в пиксельный шейдер не передается никаких значений цветов; это вызвано тем, что для текстурирования и освещения объекта применяются только текстуры. Так, BaseTex хранит цвета нашей поверхности, а SpotLightTex — карту освещения. Пиксельный шейдер возвращает единственное значение цвета, которое определяет вычисленный нами цвет данного пикселя.

Функция Main выполняет выборку значений для трех текстур с помощью функции tex2D. Таким образом, мы получаем отображаемые на обрабатываемый в данный момент пиксель тексели каждой из текстур, определяемые на основе заданных координат текстуры и объекта выборки. Затем мы комбинируем цвета текселей согласно формуле c = b * s + t. После этого мы слегка осветляем полученный пиксель, добавляя 0.1f к каждой из его компонент. И, наконец, мы сохраняем цвет полученного в результате пикселя и взвращаем его.

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

IDirect3DPixelShader9* MultiTexPS = 0;
ID3DXConstantTable* MultiTexCT    = 0;

IDirect3DVertexBuffer9* QuadVB    = 0;

IDirect3DTexture9* BaseTex        = 0;
IDirect3DTexture9* SpotLightTex   = 0;
IDirect3DTexture9* StringTex      = 0;

D3DXHANDLE BaseTexHandle          = 0;
D3DXHANDLE SpotLightTexHandle     = 0;
D3DXHANDLE StringTexHandle        = 0;

D3DXCONSTANT_DESC BaseTexDesc;
D3DXCONSTANT_DESC SpotLightTexDesc;
D3DXCONSTANT_DESC StringTexDesc;

Структура данных вершины для примера мультитекстурирования выглядит следующим образом:

struct MultiTexVertex
{
     MultiTexVertex(float x, float y, float z,
                    float u0, float v0,
                    float u1, float v1,
                    float u2, float v2)
     {
          _x = x; _y = y; _z = z;
          _u0 = u0; _v0 = v0;
          _u1 = u1; _v1 = v1;
          _u2 = u2, _v2 = v2;
     }

     float _x, _y, _z;
     float _u0, _v0;
     float _u1, _v1;
     float _u2, _v2;

     static const DWORD FVF;
};
const DWORD MultiTexVertex::FVF = D3DFVF_XYZ | D3DFVF_TEX3;

Обратите внимание, что она содержит три набора координат текстур.

Функция Setup выполняет следующие действия:

bool Setup()
{
     HRESULT hr = 0;

     //
     // Создание квадрата
     //

     Device->CreateVertexBuffer(
          6 * sizeof(MultiTexVertex),
          D3DUSAGE_WRITEONLY,
          MultiTexVertex::FVF,
          D3DPOOL_MANAGED,
          &QuadVB,
          0);

     MultiTexVertex* v = 0;
     QuadVB->Lock(0, 0, (void**)&v, 0);

     v[0] = MultiTexVertex(-10.0f, -10.0f, 5.0f,
                           0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
     v[1] = MultiTexVertex(-10.0f, 10.0f, 5.0f,
                           0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
     v[2] = MultiTexVertex( 10.0f, 10.0f, 5.0f,
                           1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);

     v[3] = MultiTexVertex(-10.0f, -10.0f, 5.0f,
                           0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
     v[4] = MultiTexVertex( 10.0f, 10.0f, 5.0f,
                           1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);
     v[5] = MultiTexVertex( 10.0f, -10.0f, 5.0f,
                           1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f);

     QuadVB->Unlock();

//
// Компиляция шейдера
//

     ID3DXBuffer* shader = 0;
     ID3DXBuffer* errorBuffer = 0;

     hr = D3DXCompileShaderFromFile(
          "ps_multitex.txt",
          0,
          0,
          "Main", // имя точки входа
          "ps_1_1",
          D3DXSHADER_DEBUG,
          &shader,
          &errorBuffer,
          &MultiTexCT);

     // Выводим любые сообщения об ошибках
     if(errorBuffer)
     {
          ::MessageBox(0, (char*)errorBuffer->GetBufferPointer(), 0, 0);
          d3d::Release<ID3DXBuffer*>(errorBuffer);
     }

     if(FAILED(hr))
     {
          ::MessageBox(0, "D3DXCompileShaderFromFile() - FAILED", 0, 0);
          return false;
     }

     //
     // Создание пиксельного шейдера
     //

     hr = Device->CreatePixelShader(
          (DWORD*)shader->GetBufferPointer(),
          &MultiTexPS);

     if(FAILED(hr))
     {
          ::MessageBox(0, "CreateVertexShader - FAILED", 0, 0);
          return false;
     }

     d3d::Release<ID3DXBuffer*>(shader);

     //
     // Загрузка текстур
     //

     D3DXCreateTextureFromFile(Device, "crate.bmp", &BaseTex);
     D3DXCreateTextureFromFile(Device, "spotlight.bmp", &SpotLightTex);
     D3DXCreateTextureFromFile(Device, "text.bmp", &StringTex);

     //
     // Установка матрицы проекции
     //

     D3DXMATRIX P;
     D3DXMatrixPerspectiveFovLH(
          &P, D3DX_PI * 0.25f,
          (float)Width / (float)Height, 1.0f, 1000.0f);

     Device->SetTransform(D3DTS_PROJECTION, &P);

     //
     // Запрещение освещения
     //

     Device->SetRenderState(D3DRS_LIGHTING, false);

     //
     // Получение дескрипторов
     //

     BaseTexHandle = MultiTexCT->GetConstantByName(0, "BaseTex");
     SpotLightTexHandle = MultiTexCT->GetConstantByName(0, "SpotLightTex");
     StringTexHandle = MultiTexCT->GetConstantByName(0, "StringTex");

     //
     // Получение описания констант
     //

     UINT count;
     MultiTexCT->GetConstantDesc(
          BaseTexHandle,
          &BaseTexDesc,
          &count);
     MultiTexCT->GetConstantDesc(
          SpotLightTexHandle,
          &SpotLightTexDesc,
          &count);
     MultiTexCT->GetConstantDesc(
          StringTexHandle,
          &StringTexDesc,
          &count);

     MultiTexCT->SetDefaults(Device);

     return true;
}

Функция Display устанавливает пиксельный шейдер, разрешает использование трех текстур и устанавливает для них требуемые режимы выборки перед визуализацией квадрата.

bool Display(float timeDelta)
{
     if(Device)
     {
          //
          // ...код обновления камеры пропущен
          //

          //
          // Визуализация
          //

          Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
                        0xffffffff, 1.0f, 0);
          Device->BeginScene();

          // Установка пиксельного шейдера
          Device->SetPixelShader(MultiTexPS);
          Device->SetFVF(MultiTexVertex::FVF);
          Device->SetStreamSource(0, QuadVB, 0, sizeof(MultiTexVertex));

          // Базовая текстура
          Device->SetTexture(BaseTexDesc.RegisterIndex, BaseTex);
          Device->SetSamplerState(BaseTexDesc.RegisterIndex,
                                  D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
          Device->SetSamplerState(BaseTexDesc.RegisterIndex,
                                  D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
          Device->SetSamplerState(BaseTexDesc.RegisterIndex,
                                  D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

          // Текстура прожектора
          Device->SetTexture(SpotLightTexDesc.RegisterIndex, SpotLightTex);
          Device->SetSamplerState(SpotLightTexDesc.RegisterIndex,
                                  D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
          Device->SetSamplerState(SpotLightTexDesc.RegisterIndex,
                                  D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
          Device->SetSamplerState(SpotLightTexDesc.RegisterIndex,
                                  D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

          // Текстура с текстом
          Device->SetTexture( StringTexDesc.RegisterIndex, StringTex);
          Device->SetSamplerState(StringTexDesc.RegisterIndex,
                                  D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
          Device->SetSamplerState(StringTexDesc.RegisterIndex,
                                  D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
          Device->SetSamplerState(StringTexDesc.RegisterIndex,
                                  D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

          // Рисуем квадрат
          Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);

          Device->EndScene();
          Device->Present(0, 0, 0, 0);
     }
     return true;
}

И, конечно, следует помнить о необходимости освобождения полученных интерфейсов в функции Cleanup:

void Cleanup()
{
     d3d::Release<IDirect3DVertexBuffer9*>(QuadVB);

     d3d::Release<IDirect3DTexture9*>(BaseTex);
     d3d::Release<IDirect3DTexture9*>(SpotLightTex);
     d3d::Release<IDirect3DTexture9*>(StringTex);

     d3d::Release<IDirect3DPixelShader9*>(MultiTexPS);
     d3d::Release<ID3DXConstantTable*>(MultiTexCT);
}

netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz