533955f1

Комбинирование текстур Пусть b



Рисунок 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); }


Содержание раздела