DirectX Graphics - начнем снова
Часть 2 - Рендеринг более существенных вещей
[Предыдущая часть]
[Следующая часть] [Приложения
к этой части]
В прошлой части мы учились создавать объекты Direct3D и даже почти что кое
что отрендерили. В этой части туториала мы сосредоточим все свое внимание на
придании нашей программе функциональности, а именно отрендерим то, без чего
любая игра жить не может - треугольник.
Треугольник у нас будет хоть и плоский, зато разноцветный. Приложение опять
будет работать в окне. Когда-нть доьберемся и до полноэкранного :) Вобщем, хватит
болтать, давайте начинать программировать!
Создайте новый пустой Win32 проект и добавьте в него 1 файл - dgtut2.cpp Не
забудьте подключить библиотеку d3d8.lib
Сначала по плану у нас как всегда объявления:
#include <d3d8.h>
//Объекты Direct3D
LPDIRECT3D8 g_pD3D = NULL; //Объект Direct3D
LPDIRECT3DDEVICE8 g_pd3dDevice = NULL; // Устройство
рендеринга
LPDIRECT3DVERTEXBUFFER8 g_pVB = NULL; // Буфер для вершин.
Первые два объекта вам уже знакомы, а вот по третьему пару слов: g_pVB - это
буфер для хранения трехмерных вершин. Весь дальнейший разговор будет построен
вокруг трехмерных вершин. Как вы увидите дальше, их формат можно будет задать
самому. Сейчас скажу только, что мы создадим массив из трех вершин (у треугольника
ведь три вершины, да?) и потом этот массив надо запихать в буфер вершин. Буфер
вершин - это просто участок выделенной памяти, с которым можно обращаться, как
с объектом. Все просто.
А вот теперь мы будем задавать формат наших вершин:
//Структура, определяющая наш формат вершины (aka FVF)
struct CUSTOMVERTEX
{
FLOAT x, y, z, rhw; //Трансформированная позиция вершины
DWORD color; //Цвет вершины
};
Вершина задана в структуре CUSTOMVERTEX. C x, y, z и color вроде все ясно.
rhw расшифровывается как reciprocal homogeneous W. Не справшивайте у меня что
это такое, я и сам этого слова пугаюсь :0)
//Флаговое описание нашего FVF
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
В этой строке мы задали флаговое описание нашего формата. D3DFVF_XYZRHW означает,
что мы используем трансформированные координаты по x, y, z и тому самому, который
"W". D3DFVF_DIFFUSE означает, что каждая вершина имеет свой цвет (diffuse
color). Здесь мы задали только определения, чье применение вы увидите ниже.
Дальше идут знакомые функции создания и уничтожения. Я не буду на них подробно
останавливаться
HRESULT InitD3D( HWND hWnd );
VOID Cleanup();
HRESULT InitVB();
VOID Render();
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam
)
{
switch( msg )
{
case WM_DESTROY:
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc( hWnd, msg, wParam, lParam );
}
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
//Зарегистрировать класс окна
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
"D3D Tutorial", NULL };
RegisterClassEx( &wc );
// Создать окно
HWND hWnd = CreateWindow( "D3D Tutorial", "D3D Tutorial 02:
Vertices",
WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
GetDesktopWindow(), NULL, wc.hInstance, NULL );
// Инициализировать Direct3D
if( SUCCEEDED( InitD3D( hWnd ) ) )
{
//Создать буфер вершин
if( SUCCEEDED( InitVB() ) )
{
// Показать окно
ShowWindow( hWnd, SW_SHOWDEFAULT );
UpdateWindow( hWnd );
// Войти в главный цикл сообщений
MSG msg;
ZeroMemory( &msg, sizeof(msg) );
while( msg.message!=WM_QUIT )
{
if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
Render();
}
}
}
// Убить все и выйти
Cleanup();
UnregisterClass( "D3D Tutorial", wc.hInstance );
return 0;
}
Здесь внимания может удостоиться только то, что теперь мы запускаем рендеринг
каждый шаг цикла сообщений и то, что после инициализации Direct3D мы вызываем
процедуру создания буфера вершин.
HRESULT InitD3D( HWND hWnd )
{
// Создать объект D3D, который потребуется для создания
устройства рендеринга
if( NULL == ( g_pD3D = Direct3DCreate8( D3D_SDK_VERSION ) ) )
return E_FAIL;
// Получить текущий режим экрана
D3DDISPLAYMODE d3ddm;
if( FAILED( g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm
) ) )
return E_FAIL;
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = d3ddm.Format;
if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_pd3dDevice ) ) )
{
return E_FAIL;
}
// Все в порядке, можно вернуть что-нибудь хорошее
return S_OK;
}
VOID Cleanup()
{
if( g_pVB != NULL )
g_pVB->Release();
if( g_pd3dDevice != NULL)
g_pd3dDevice->Release();
if( g_pD3D != NULL)
g_pD3D->Release();
}
Теперь мы будем писать процедуру инифиализации буфера вершин - InitVB, которая
вызывается сразу после создания объектов Direct3D. Сперва мы должны создать
массив из вершин произвольного типа, который мы создали в самом начале.
HRESULT InitVB()
{
// Указать координаты трех вершин для нашего треугольника
CUSTOMVERTEX g_Vertices[] =
{
{ 150.0f, 50.0f, 0.5f, 1.0f, 0xffff0000, }, // x, y,
z, rhw, color
{ 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00, },
{ 50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff, },
};
Координаты точек заданы так, чтобы создать треугольник, расположенный параллельно
плоскости обзора. Таинственный rhw задан в 1.0f и ни на что особо не влияет.
Цвет каждая вершина имеет свой. Он задан в формате ARGB, то есть первая пара
чисел задает альфа-компоненту, вторая красную и так далее.
//Создать буфер под хранение информации о вершинах
if( FAILED( g_pd3dDevice->CreateVertexBuffer( 3*sizeof(CUSTOMVERTEX),
0, D3DFVF_CUSTOMVERTEX,
D3DPOOL_DEFAULT, &g_pVB ) ) )
{
return E_FAIL;
}
Здесь мы создаем буфер вершин, то есть мы занимаем память под хранение координат,
но еще не вносим координаты в буфер. Первый параметр говорит сколько памяти
выделять. Затем мы указываем флаговый формат вершин, говорим, что память надо
выделять из источника по умолчанию (обычно видео и AGP-видеопамять.
//Скопировать информацию о вершинах в буфер
VOID* pVertices;
if( FAILED( g_pVB->Lock( 0, sizeof(g_Vertices), (BYTE**)&pVertices,
0 ) ) )
return E_FAIL;
memcpy( pVertices, g_Vertices, sizeof(g_Vertices) );
g_pVB->Unlock();
return S_OK;
}
После создания, буфер вершин чист и невинен, как младенец :) В этом участке
кода мы поместили информацию из массива в буфер так, чтобы ее можно было использовать.
На этом процедура создания закончилась.
Нам осталось написать только функцию рендеринга треугольника. Все команды рендеринга
должны помещаться между функциями BeginScene и EndScene.
VOID Render()
{
// Очистить задний буфер в синий цвет
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255),
1.0f, 0 );
// Начало рендеринга сцены
g_pd3dDevice->BeginScene();
Теперь, мы должны установить источник потока вершин для организации поточного
рендеринга. Это реализовано в следующей функции:
g_pd3dDevice->SetStreamSource( 0, g_pVB, sizeof(CUSTOMVERTEX) );
g_pd3dDevice->SetVertexShader( D3DFVF_CUSTOMVERTEX );
Первый параметр - номер потока. Второй - это буфер вершин. Наконец, третий
параметр в случае FVF должен соотвествовать размеру структуры вершины. SetVertexShader
дает знать D3D какой тип вершин мы используем.
Теперь мы выполняем сам рендеринг.
g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 );
D3DPT_TRIANGLELIST указывает какой тип примитива рендерить. Мы ведь указали
в буфере только координаты, откуда D3D знать что они означают?! Второй параметр
означает индекс первой координаты в буфере, с которой начинать отсчитывать поток
координат. Последняя единица указывает какое количество примитивов рисовать.
Так как у нас всего один треугольник, то мы указали последним параметром единицу.
На этом рендеринг заканчиватся. Заканчивается и наша программа.
//Конец рендеринга
g_pd3dDevice->EndScene();
//Показать содержимое заднего буфера на экране
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}
На этом вроде как и все. Компилируйте проект и наслаждайтесь плавным переходом
цвета треугольника. Кстати, если вы будете изменять размеры формы, то заметите,
что треугольник тоже меняет размеры. Это происходит из-за того, что координаты
в нашей структуре "трансформированные", то есть уже переведенные в
2D оконные координаты с началом координат в верхнем левом углу.
В следующей части мы будем вращать наш треугольник, используя матрицы.
Приложение: Готовый
проект (17k)
Приятного программирования, Antiloop
[Вверх]
Posted: 23.01.2k1
Autor: Antiloop
<anti_loop@mail.ru>
|