netlib.narod.ru | < Назад | Оглавление | Далее > |
Пример приложения для данной главы демонстрирует мультитекстурирование с использованием пиксельных шейдеров. Пример формирует текстурированный квадрат, отмеченный на рис. 18.2 как «результат» путем смешивания текстуры ящика, текстуры прожектора и текстуры со строкой «Pixel Shader Sample».
Рис. 18.2. Комбинирование текстур. Пусть b, s и t — это цвета соответствующих текселей из текстуры ящика, текстуры прожектора и текстуры текста соответственно. Тогда цвет их комбинации определяется по формуле c = b s + t, где обозначает покомпонентное умножение
Данный пример может быть реализован и без пиксельных шейдеров. Однако, это более простой и прямолинейный способ реализации, позволяющий к тому же продемонстрировать написание, создание и использование пиксельных шейдеров без отвлечения на реализацию алгоритма какого-нибудь специального эффекта.
Хотя в рассматриваемом примере мы используем одновременно только три текстуры, весьма полезно узнать сколько объектов выборки могут одновремено использоваться в каждой из версий пиксельных шейдеров. Другими словами, как количество одновременно используемых текстур зависит от используемой версии пиксельных шейдеров.
Пиксельные шейдеры версий от ps_1_1 до ps_1_3 поддерживают до четырех выборок текстуры.
Пиксельные шейдеры версии ps_1_4 поддерживают до шести выборок тектсуры.
Пиксельные шейдеры версий от ps_2_0 до ps_3_0 поддерживают до 16 выборок текстуры.
Код пиксельного шейдера для реализации мультитекстурирования с использованием трех текстур выглядит следующим образом:
// // Файл : 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 | < Назад | Оглавление | Далее > |