| 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 | < Назад | Оглавление | Далее > |