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

Альфа-смешивание

Вообразите, что вы находитесь в одном из самых высоких в мире зданий, подходите к окну и смотрите на расстилающийся внизу город. Легкий синеватый оттенок стекла придает картине мирный вид, подобный утреннему небу.

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

Вы хотите получить не только замечательные эффекты, подобные упомянутым выше, но и такие веши, как копирование с учетом прозрачности (transparent blits) (то есть вы хотите рисовать полигоны отдельные фрагменты которых будут полностью прозрачными).

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

Такие эффекты можно реализовать с помощью техники называемой альфа-смешивание (alpha blending). Используя альфа-смешивание вы можете менять прозрачность полигонов, чтобы сквозь них можно было видеть. Если полигон окрашен, то его цвет будет смешиваться с цветами находящихся позади него объектов. Более того, вы можете даже использовать текстуру для полигона, чтобы создать ошеломляющие эффекты!

Степень прозрачности объекта называется альфа-значением (alpha value). Как вы, возможно, уже заметили, Direct3D использует альфа-значение различными способами. Например, используя текстуры вы можете задать формат в котором есть альфа-значения. Хранилище альфа-значений называется альфа-каналом (alpha channel).

ПРИМЕЧАНИЕ
Альфа-канал (alpha channel) — это значение, точно такое же, как цветовые компоненты (красный, зеленый и синий). Оно определяет степень прозрачности для каждого пикселя поверхности, имеющего собственный альфа-канал.
Альфа-канал может занимать от одного до восьми битов. Если у вас 8-разрядный альфа-канал, вы можете задать 256 альфа-значений (в диапазоне от 0 до 255). Четырехразрядный альфа-канал позволяет использовать 16 альфа-значений (от 0 до 15).

Включение альфа-смешивания

Включение альфа-смешивания в Direct3D осуществляется простой установкой необходимых режимов визуализации с помощью функции IDirect3DDevice9::SetRenderState. Первый режим визуализации, который собственно и включает альфа-смешивание, это D3DRS_ALPHABLENDENABLE:

// g_pD3DDevice = ранее инициализированный объект устройства

// Для включения альфа-смешивания используйте:
g_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);

// Установка типа альфа-смешивания
g_pD3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
g_pD3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

// Для выключения альфа-смешивания используйте:
g_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);

Обратите внимание на два дополнительных режима визуализации (D3DRS_SRCBLEND и D3DRS_DESTBLEND) в приведенном выше коде. Они сообщают Direct3D как надо использовать указанные альфа-значения при визуализации. Время от времени вы будете видеть, что для режима D3DRS_DESTBLEND устанавливается значение D3DBLEND_ONE, а не D3DBLEND_INVSRCALPHA. Я буду специально обращать ваше внимание на такие случаи.

Рисование с альфа-смешиванием

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

Приведенный ниже пример демонстрирует простую структуру данных вершины, хранящую трехмерные координаты и рассеиваемую составляющую цвета (которая теперь включает альфа-значение):

// Структура данных вершины и дескриптор
typedef struct {
    FLOAT    x, y, z;
    D3DCOLOR diffuse;
} sVertex;
#define VertexFVF (D3DFVF_XYZ | D3DFVF_DIFFUSE)

// Определяем массив из трех вершин
sVertex Verts = {
    {    0.0f,  100.0f, 0.0f, D3DCOLOR_RGBA(255,  0,  0, 64) },
    {  100.0f, -100.0f, 0.0f, D3DCOLOR_RGBA(  0,255,  0,128) },
    { -100.0f, -100.0f, 0.0f, D3DCOLOR_RGBA(  0,  0,255,255) }
};

Для первой вершины задан красный цвет и прозрачность 1/4 (1/4 цвета будет смешиваться). Вторая вершина зеленая с прозрачностью 1/2 (1/2 цвета будет смешиваться). Третья вершина синяя и полностью непрозрачна, а это значит, что для нее цвета не будут смешиваться.

Если вы добавляете координаты текстуры и устанавливаете текстуру, можно установить максимальные значения цветовых компонент (255 для зеленого, красного и синего) и затем задать альфа-значение для смешивания текстуры.

Копирование с учетом прозрачности и альфа-проверка

Альфа-проверка (alpha testing) — это техника, проверяющая альфа-значения пикселей перед их рисованием на экране. Те пиксели, альфа-значения которых не попадают в заданный диапазон, отвергаются и, следовательно, не влияют на результат визуализации. Подобно способу, которым в предыдущем разделе вы достигали эффекта полупрозрачности, вы можете использовать альфа-проверку для визуализации полигонов с полностью прозрачными фрагментами.

Воспользовавшись приведенным ранее примером «дырка в стене», вообразите, что стена — это полигон и вы хотите нарисовать отверстие в ее центре. Вам необходимо, чтобы полигон был полностью непрозрачным (сплошным), за исключением отверстия. При этом вы хотите, чтобы отверстие было полностью прозрачным. Чтобы достичь такого эффекта, используется техника, называемая копирование с учетом прозрачности (transparent blit), которая позволяет вам исключать части текстуры при визуализации и, таким образом, видеть находящиеся позади объекты сквозь эти исключенные фрагменты.

Секрет копирования с учетом прозрачности заключается в установке вашей текстуры и использованию одного из ее цветов в качестве цветового ключа. Цветовой ключ (color key) — это цвет, который не рисуется при визуализации полигона.

Например, если у вас есть текстура на которой изображен круг в центре, окруженный черным цветом (как показано на рис. 2.17), вы можете установить в качестве цветового ключа черный цвет. Когда текстура будет применена к полигону, а сам полигон нарисован, Direct3D не будет рисовать черные пиксели, так что отображен будет только круг в центре.


Рис. 2.17. Текстура с кругом в центре может использовать копирование с учетом прозрачности для исключения темной внешней области

Рис. 2.17. Текстура с кругом в центре может использовать копирование с учетом прозрачности для исключения темной внешней области


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

Как вы можете предположить, пиксели, соответствующие цветовому ключу имеют альфа-значение 0; а для всех остальных используется наибольшее альфа-значение.

Загрузка текстур с цветовым ключом

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

HRESULT D3DXCreateTextureFromFileEx(
    LPDIRECT3DDEVICE9  pDevice,     // ранее созданный объект устройства
    LPCSTR             pSrcFile,    // имя файла с загружаемой текстурой
    UINT               Width,       // D3DX_DEFAULT
    UINT               Height,      // D3DX_DEFAULT
    UINT               MipLevels,   // D3DX_DEFAULT
    DWORD              Usage,       // 0
    D3DFORMAT          Format,      // используемый формат цвета
    D3DPOOL            Pool,        // D3DPOOL_MANAGED
    DWORD              Filter,      // D3DX_FILTER_TRIANGLE
    DWORD              MipFilter,   // D3DX_FILTER_TRIANGLE
    D3DCOLOR           ColorKey,    // Используемый цветовой ключ!
    D3DXIMAGE_INFO     *pSrcInfo,   // NULL
    PALETTEENTRY       *pPalette,   // NULL
    LPDIRECT3DTEXTURE9 *ppTexture); // созданный объект текстуры

Для большинства параметров указываются представленные значения по умолчанию. Вам необходимо предоставить только имя файла с растровым изображением для загрузки, указатель на объект устройства, который будет использоваться для создания текстуры, формат цвета, используемый при загрузке текстуры (тип D3DFMT_* в котором используется альфа-значение, такой как D3DFMT_A8R8G8B8) и цветовой ключ (в формате D3DCOLOR).

Для указания значения цветового ключа, можете использовать макросы D3DCOLOR_RGBA или D3DCOLOR_COLORVALUE. Например, если вы хотите исключить из процесса рисования черный цвет, при загрузке текстуры используйте следующее значение цветового ключа:

D3DCOLOR_RGBA(0, 0, 0, 255);

Обратите внимание на значение 255 для альфа. Это очень важно! При загрузке файла формата .BMP вы должны указывать ля альфа-значения число 255. Если вы работаете с другими форматами файлов (такими, как .TGA), которые уже содержат значения альфа-канала, необходимо, чтобы альфа-значение цветового ключа соответствовало альфа-значению, которое уже хранится в файле изображения.

В этой книге я буду использовать только файлы формата .BMP, так что просто помните о необходимости использовать альфа-значение 255. После установки альфа-значения для каждого пикселя текстуры, остается только воспользоваться альфа-проверкой, чтобы исключить пиксели на основании их альфа-значений.

Включение альфа-проверки

Когда текстура загружена (и цветовой ключ и альфа-значения установлены) вы должны включить альфа-проверку, добавив следующие строки в код инициализации или в цикл визуализации:

// g_pD3DDevice = ранее инициализированный объект устройства
g_pD3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
g_pD3DDevice->SetRenderState(D3DRS_ALPHAREF, 0x08);
g_pD3DDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);

Главный волшебник здесь — режим D3DRS_ALPHAREF, поскольку именно он говорит Direct3D, какие альфа-значения допустимы (в диапазоне от 0 до 255). После выполнения трех показанных вызовов функций будут отвергаться все пиксели, у которых альфа-значение меньше 8. Если вы правильно установили ваш цветовой ключ, три вызова функций приведут к тому, что все фрагменты текстур, у которых альфа-значение равно нулю будут исключены из стадии визуализации, то есть станут прозрачными!

Пример копирования с учетом прозрачности

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

// g_pD3DDevice = ранее инициализированный объект устройства

// Структура данных вершины и дескриптор
typedef struct {
    FLOAT x, y, z, rhw; // Экранные координаты
    FLOAT u, v;         // Координаты текстуры
} sVertex;
#define VertexFVF (D3DFVF_XYZRHW | D3DFVF_TEX1)

// Буфер вершин и текстура
IDirect3DVertexBuffer9 *g_pVB      = NULL;
IDirect3DTexture9      *g_pTexture = NULL;

// Инициализация буфера вершин и текстуры
// подразумевает, что размер окна 400 x 400
BYTE *Ptr;
sVertex Verts[4] = {
    {   0.0f,   0.0f, 0.0f, 1.0f, 0.0f, 0.0f },
    { 399.0f,   0.0f, 0.0f, 1.0f, 1.0f, 0.0f },
    {   0.0f, 399.0f, 0.0f, 1.0f, 0.0f, 1.0f },
    { 399.0f, 399.0f, 0.0f, 1.0f, 1.0f, 1.0f }
};

// Создание буфера вершин и заполнение данными
g_pD3DDevice->CreateVertexBuffer(sizeof(sVertex) * 4, 0, 
           VertexFVF, D3DPOOL_MANAGED, &g_pVB, NULL)
g_pVB->Lock(0,0, (void**)&Ptr, 0)))
memcpy(Ptr, Verts, sizeof(Verts));
g_pVB->Unlock();

// Получение текстуры
D3DXCreateTextureFromFileEx(g_pD3DDevice, "button.bmp",
           D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0,
           D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, D3DX_FILTER_TRIANGLE,
           D3DX_FILTER_TRIANGLE, D3DCOLOR_RGBA(0,0,0,255), NULL,
           NULL, &g_pTexture);

// Установка альфа-проверки
g_pD3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
g_pD3DDevice->SetRenderState(D3DRS_ALPHAREF, 0x01);
g_pD3DDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);

// Очистка вторичного буфера устройства
g_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET,
                    D3DCOLOR_RGBA(0,128,128,255), 1.0f, 0);

if(SUCCEEDED(g_pD3DDevice->BeginScene())) {

    // Установка буфера вершин в качестве источника потоковых данных
    g_pD3DDevice->SetStreamSource(0, g_pVB, 0, sizeof(sVertex));

    // Установка вершинного шейдера
    g_pD3DDevice->SetFVF(VertexFVF);

    // Установка текстуры
    g_pD3DDevice->SetTexture(0, g_pTexture);

    // Рисование буфера вершин
    g_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
    g_pD3DDevice->EndScene();

    // Очистка текстуры
    g_pD3DDevice->SetTexture(0, NULL);

    // Выключение альфа-проверки
    g_pD3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
}

// Переключаем поверхности для отображения результата
g_pD3DDevice->Present(NULL, NULL, NULL, NULL);

Освещение

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

Фоновый свет (ambient light) — это постоянный источник света, который освещает все объекты сцены с одинаковой интенсивностью. Являясь частью аппаратуры, фоновый свет является единственным типом освещения, обрабатываемым отдельно от процесса расчета освещения.

Другие три типа источников света (изображенные на рис. 2.18) обладают уникальными свойствами. Точечный источник света (point light) освещает все вокруг (подобно электрической лампочке). Зональный свет (spotlight) распространяется в указанном направлении и испускает луч конической формы. Все внутри конуса освещено, а объекты находящиеся вне конуса не освещены. Источник направленного света (directional light) является упрощенной версией зонального света и просто испускает лучи света в указанном направлении.


Рис. 2.18. Точечный, зональный и направленный источники света испускают световые лучи по-разному

Рис. 2.18. Точечный, зональный и направленный источники света испускают световые лучи по-разному


Источники света размещаются на сцене точно так же, как и остальные трехмерные объекты — с помощью координат x, y и z. У некоторых источников света, таких как зональный свет, есть вектор направления, определяющий куда они направлены. У каждого источника света есть интенсивность, диапазон действия, коэффициент затухания и цвет. Верно, с Direct3D возможны даже цветные источники света!

За исключением фонового света, все остальные источники света используют для хранения своей уникальной информации структуру D3DLIGHT9. Определение этой структуры выглядит так:

typedef struct _D3DLIGHT9 {
    D3DLIGHTTYPE  Type;         // Тип источника света
    D3DCOLORVALUE Diffuse;      // Рассеиваемая составляющая цвета
    D3DCOLORVALUE Specular;     // Отражаемая составляющая цвета
    D3DCOLORVALUE Ambient;      // Фоновая составляющая цвета
    D3DVECTOR     Position;     // Местоположение источника
    D3DVECTOR     Direction;    // Направление испускаемых лучей
    float         Range;        // Дальность
    float         Falloff;      // Затухание зонального света
    float         Attenuation0; // Коэффициент затухания 0
    float         Attenuation1; // Коэффициент затухания 1
    float         Attenuation2; // Коэффициент затухания 2
    float         Theta;        // Угол внутреннего конуса
    float         Phi;          // Угол внешнего конуса
} D3DLIGHT9;

Ничего себе! Большая штуковина, но она содержит всю информацию, необходимую для описания источника света. Хотя источник света и не обязан использовать каждую переменную в структуре D3DLIGHT9, все источники света используют несколько полей.

Первая переменная, которую вы будете устанавливать, — Type, определяющая тип используемого источника света. Она может принимать значения D3DLIGHT_POINT для точечного источника света, D3DLIGHT_SPOT для зонального источника света или D3DIGHT_DIRECTIONAL для направленного источника света.

Следующие строки задают цвет освещения. Наиболее часто вы будете использовать поле Diffuse; оно определяет цвет испускаемого источником света. Обратите внимание, что отвечающие за цвет поля хранятся в формате D3DCOLORVAUE, который представляет собой следующую структуру:

typedef struct _D3DCOLORVALUE {
    float r;   // Красная составляющая (от 0.0 до 1.0)
    float g;   // Зеленая составляющая (от 0.0 до 1.0)
    float b;   // Синяя составляющая (от 0.0 до 1.0)
    float a;   // Альфа-значение (не используется)
} D3DCOLORVALUE;

Вы устанавливаете каждую цветовую составляющую в структуре, присваивая ей значение в диапазоне от 0.0 (выключено) до 1.0 (максимальная интенсивность). Для красного света значения будут r = 1.0, g = 0.0, b = 1.0, а для белого — r = 1.0, g = 1.0, b = 1.0. Поскольку мы имеем дело со светом, альфа-значение здесь не используется. Поля Specular и Ambient в структуре D3DLIGHT9 определяют цвет бликов и фоновый цвет, соответственно. Вы можете спокойно присвоить в обоих полях каждой цветовой составляющей значение 1.0 (за исключением Specular, где вы должны устанавливать значения 0.0, если не хотите использовать блики).

СОВЕТ
Вы можете использовать цветовые уровни света не только для того, чтобы освещать объект, но и чтобы затемнять его. Вместо положительных значений цветовых компонентов используйте отрицательные, и посмотрите на результат!

Как я упоминал ранее, каждый источник света размещается в сцене с помощью кооринах X, Y и Z (мировых координат), которые хранятся в векторе Position. Еще один вектор, Direction, используется для того, чтобы указать направление световых лучей. О применении вектора направления вы узнаете в разделе «Использование зонального света».

Переменная Range определяет насколько далеко распространяется свет до полного затухания. (значение Falloff используется, чтобы указать как быстро затухает свет в промежутке между внутренним и внешним конусами зонального источника; обычно используемое значение 1.0 обеспечивает плавный переход). Объекты, находящиеся дальше заданного расстояния не будут освещены источником света.

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

Использование точечного света

Работать с точечным источником света проще всего; вы просто указываете его местоположение, цветовые составляющие и дальность. Чтобы определить точечный источник света, создайте экземпляр структуры D3DLIGHT9 и заполните его необходимой информацией:

D3DLIGHT9 PointLight;

// Очищаем структуру
ZeroMemory(&PointLight, sizeof(D3DLIGHT9));

// Располагаем источник света в точке 0.0, 100.0, 200.0
PointLight.Position = D3DVECTOR3(0.0f, 100.0f, 200.0f);

// Делаем фоновую и рассеиваемую составляющие цвета белыми
PointLight.Diffuse.r = PointLight.Ambient.r = 1.0f;
PointLight.Diffuse.g = PointLight.Ambient.g = 1.0f;
PointLight.Diffuse.b = PointLight.Ambient.b = 1.0f;

// Устанавливаем дальность равной 1000 единиц
PointLight.Range = 1000.0f;

Использование зонального света

Зональный источник света отличается от других, потому что испускает лучи света в форме конуса, расходящегося от источника. Свет наиболее ярок в центре и постепенно затухает к краю конуса. За пределами конуса ничего не освещается.

ВНИМАНИЕ!
Зональный источник света требует наибольшего объема вычислений, поэтому не следует использовать в сцене много источников света такого типа.

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

Переменная Phi в структуре D3DLIGHT9 определяет размер внешнего конуса. Phi, также как и Theta, представляет угол (в радианах). Чем дальше лучи света уходят от источника, тем шире становится освещаемый ими круг. Программист задает значения этих переменных, а затем экспериментирует с ними, пока не получит желаемый результат.

Приведенный ниже код создает источник зонального света и задает для него местоположение, цвет, дальность, затухание и размер конусов:

D3DLIGHT9 Spotlight;

// Очистка данных
ZeroMemory(&SpotLight, sizeof(D3DLIGHT9));

// Источник света располагается в точке 0.0, 100.0, 200.0
SpotLight.Position = D3DVECTOR3(0.0f, 100.0f, 200.0f);

// Делаем фоновую и рассеиваемую составляющие цвета белыми
SpotLight.Diffuse.r = SpotLight.Ambient.r = 1.0f;
SpotLight.Diffuse.g = SpotLight.Ambient.g = 1.0f;
SpotLight.Diffuse.b = SpotLight.Ambient.b = 1.0f;

// Устанавливаем дальность
SpotLight.Range = 1000.0f;

// Задаем затухание внутри конуса
SpotLight.Falloff = 1.0f;

// Устанавливаем размеры конусов
Spotlight.Phi   = 0.3488;   // Внешний 20 градусов
Spotlight.Theta = 0.1744;   // Внутренний 10 градусов

Теперь вам надо направить источник света в заданном направлении. Библиотека D3DX снова приходит на помощь с парой функций, позволяющих указать точку, на которую направлен источник зонального света (да и любой другой источник света). Первая из функций — это перегруженный конструктор объекта D3DXVECTOR3, позволяющий задать три координаты.

Для этих трех значений вы используете координаты мирового пространства, описывающие расстояние от начала координат. Если у вас где-нибудь в сцене есть источник зонального света, и вы хотите направить его вверх на точку, находящуюся в 500 единицах над ним, необходимо для объекта вектора задать значения X = 0, Y = 500, Z = 0 (обратите внимание, что эти три координаты заданы относительно местоположения источника света). Значение вектора устанавливает следующий фрагмент кода:

D3DXVECTOR3 Direction = D3DXVECTOR3(0.0f, 500.0f, 0.0f);

В приведенном объявлении вектора Direction есть одна проблема: Direct3D ожидает нормализованный вектор, а это значит, что координаты должны быть в диапазоне от 0 до 1. Никаких проблем, вторая функция библиотеки D3DX, D3DXVec3Normalize сделает это за вас:

D3DXVECTOR3 *D3DXVec3Normalize(
    D3DXVECTOR3 *pOut,      // Нормализованный вектор
    CONST D3DXVECTOR3 *pV); // Исходный вектор

Вы передаете исходный вектор (например тот, объявление которого было показано выше, содержащий координаты X = 0, Y = 500, Z = 0) и указатель на новый вектор, а функция D3DXVec3Normalize преобразует координаты в значения из диапазона от 0 до 1. Новый вектор теперь содержит значение направления, которое можно использовать для поля направления источника света в структуре D3DLIGHT9.

Продолжая приведенный выше пример, зададим ориентацию зонального источника света, направив его вверх, нормализуем вектор и сохраним его в структуре D3DLIGHT9:

D3DXVECTOR3 Dir = D3DXVECTOR3(0.0f, 500.0f, 0.0f);
D3DXVec3Normalize((D3DXVECTOR3*)&Spotlight.Direction, &Dir);

Использование направленного света

В терминах производительности источник направленного света является самым быстрым из всех типов освещения. Он освещает каждый полигон, который обращен к нему. Чтобы подготовить источник направленного света к использованию вы должны задать в полях структуры D3DLIGHT9 направление и цветовые составляющие.

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

Вспомнив техники для двух предыдущих типов освещения, взгляните на приведенный ниже пример, где устанавливается желтый источник света, направленный вниз на сцену:

D3DLIGHT9 DirLight;

// Очищаем данные источника света
ZeroMemory(&DirLight, sizeof(D3DLIGHT9));

// Делаем рассеиваемую и фоновую составляющие желтыми
DirLight.Diffuse.r = DirLight.Ambient.r = 1.0f;
DirLight.Diffuse.g = DirLight.Ambient.g = 1.0f;
DirLight.Diffuse.b = DirLight.Ambient.b = 0.0f;

D3DXVECTOR3 Dir = D3DXVECTOR3(0.0f, 500.0f, 0.0f);
D3DXVec3Normalize((D3DXVECTOR3*)&Dirlight.Direction, &Dir);

 

ВНИМАНИЕ!
Вектор направления источника света должен содержать хотя бы одно отличное от нуля значение. Другими словами, вы не можете задать направление X = 0, Y = 0, Z = 0.

Фоновый свет

Фоновый свет — это единственный тип освещения, который обрабатывается Direct3D по-другому. Direct3D применяет фоновый свет ко всем полигонам, независимо от их углов или их источников света, так что затенения не происходит. Фоновый свет — это постоянный уровень освещенности и, так же как для других типов освещения (точечного, зонального и направленного), вы можете задавать его цвет.

Уровень фонового освещения задается путем установки режима визуализации D3DRS_AMBIENT и передачи ему значения D3DCOLOR (используйте макрос D3DCOLOR_COLORVALUE, в котором уровни красного, зеленого и синего задаются числами из диапазона от 0 до 1) определяющего желаемый цвет.

g_pD3DDevice->SetRenderState(D3DRS_AMBIENT,
         D3DCOLOR_COLORVALUE(0.0f, Red, Green, Blue));

Установка света

После того, как вы инициализировали структуру D3DLIGHT9, ее надо передать Direct3D с помощью функции IDirect3DDevice9::SetLight:

HRESULT IDirect3DDevice9::SetLight(
    DWORD           Index,    // Индекс устанавливаемого источника
    CONST D3DLIGHT9 *pLight); // Используемая структура D3DLIGHT9

Как видите, в параметре pLight передается структура D3DLIGHT9, а в поле Index — что-то другое. Direct3D позволяет устанавливать на сцене несколько источников света, так что Index — это просто порядковый номер устанавливаемого источника света (нумерация начинается с нуля). Например, если в сцене вы используете четыре источника света, то индекс первого из них будет 0, индекс второго — 1, индекс третьего — 2 и индекс четвертого — 3.

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

Использование нормалей

Чтобы Direct3D мог правильно рассчитать освещение грани установленными вами источниками света, необходимо для каждой вершины полигона указать ее нормаль. Нормаль (normal) — это трехмерный вектор, определяющий направление в котором обращен объект (такой, как вершина или полигон) к которому этот вектор присоединен. Нормали обычно используются в сложных вычислениях, определяющих сколько света получает объект от заданного источника.

Если вы взглянете на треугольную грань (например, на ту, которая изображена на рис. 2.19), то увидите, что для трех вершин нормали задают направление. Когда свет попадает на эти вершины, угол его отражения рассчитывается на основании нормалей. Использование нормалей гарантирует, что все грани будут правильно освещены и затенение будет выполняться в соответствии с углами относительно зрителя и источника света.


Рис. 2.19. У каждой вершины есть нормаль, указывающая в заданном направлении

Рис. 2.19. У каждой вершины есть нормаль, указывающая в заданном направлении. Вы используете угол относительно нормали, чтобы определить как свет отражается от грани и как выполнять вычисления затенения


Добавить к данным вершины нормаль также просто, как и указать информацию о текстуре. Вы просто вставляете поле для нормали типа D3DVECTOR3 и переопределяете дескриптор формата (добавив флаг D3DFVF_NORMAL), как показано ниже:

typedef struct {
    D3DVECTOR3 Position; // Вектор координат
    D3DVECTOR3 Normal;   // Нормаль
    D3DCOLOR   Color;    // Цвет
} sVertex;
#define VERTEXFMT (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_NORMAL)

Нормали вы вычисляете так же, как создавали вектор направления, работая с источниками света в разделе «Использование зонального света». Когда вы начнете работать с трехмерными моделями, задача вычисления нормалей больше не будет висеть на вас, поскольку программы трехмерного моделирования, используемые для создания моделей, обычно сами вычисляют нормали.

Приведенная ниже функция (позаимствованная из примеров DirectX SDK) создает цилиндр и присваивает каждой вершине нормаль, направленную от центра цилиндра (рис. 2.20).


Рис. 2.20. Функция GenerateCylinder создает цилиндр с нормалями, направленными от центра

Рис. 2.20. Функция GenerateCylinder создает цилиндр с нормалями, направленными от центра


// g_pD3DDevice = ранее инициализированный объект устройства
IDirect3DVertexBuffer9 *GenerateCylinder()
{
    IDirect3DVertexBuffer9 *pD3DVertexBuffer;
    sVertex *pVertex;
    DWORD i;
    FLOAT theta;

    // Создаем буфер вершин
    if(SUCCEEDED(g_pD3DDevice->CreateVertexBuffer(
                 50 * 2 * sizeof(sVertex), 0, VERTEXFMT,
                 D3DPOOL_MANAGED, &pD3DVertexBuffer, NULL))) {
        // Заполняем буфер вершин данными цилиндра
        if(SUCCEEDED(pD3DVertexBuffer->Lock(0, 0,
                                      (void**)&pVertex, 0))) {
            for(i = 0; i < 50; i++) {
                theta = (2 * D3DX_PI * i) / (50 - 1);
                pVertex[2*i+0].Position = D3DXVECTOR3(sinf(theta),
                                                 -1.0f, cosf(theta));
                pVertex[2*i+0].Normal = D3DXVECTOR3(sinf(theta),
                                                  0.0f, cosf(theta));
                pVertex[2*i+1].Position = D3DXVECTOR3(sinf(theta),
                                                  1.0f, cosf(theta));
                pVertex[2*i+1].Normal = D3DXVECTOR3(sinf(theta),
                                                  0.0f, cosf(theta));
            }
            pD3DVertexBuffer->Unlock();

            // Возвращаем указатель на новый буфер вершин
            return pD3DVertexBuffer;
        }
    }
    // Возвращаем NULL при ошибке
    return NULL;
}

Да будет свет!

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

// g_pD3DDevice = ранее инициализированный объект устройства
g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);

Чтобы выключить конвейер освещения используйте следующий код:

// g_pD3DDevice = ранее инициализированный объект устройства
g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

После активации конвейера освещения вы включаете или выключаете отдельные источники освещения с помощью функции IDirect3DDevice9::LightEnable. Вот ее прототип:

IDirect3DDevice9::LightEnable(
    DWORD LightIndex, // Индекс источника света
    BOOL  bEnable);   // TRUE для включения, FALSE для выключения

Если вы уже установили источник точечного света с индексом 0, то для его включения и выключения используется следующий код:

// g_pD3DDevice = ранее инициализированный объект устройства

// Включение источника света
g_pD3DDevice->LightEnable(0, TRUE);

// Выключение источника света
g_pD3DDevice->LightEnable(0, FALSE);

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


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

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