DirectX 8 - Урок 1. Вывод простых фигур
В нашем первом уроке мы с помощью Direct3D нарисуем две простые фигуры: треугольник и квадрат. Для этого нам понадобятся последняя версия DirectX SDK, которую вы можете взять здесь, и
любой компилятор языка С++. Сам я пользуюсь Visual C++ 6.0, потому что с ним не возникает никаких проблем при работе с DirectX SDK. Итак, начнем:
Во-первых , мы создадим главное окно программы, в котором будут нарисованы наши фигуры.
void CreateMainWindow(void)
{
WNDCLASSEX wc = {sizeof(WNDCLASSEX),CS_CLASSDC,
WindowMsgProc,0,0,
GetModuleHandle(NULL),NULL,NULL,NULL,NULL,
"D3D Samples",NULL};
RegisterClassEx(&wc);
hWnd = CreateWindow("D3D Samples","D3D Lesson 1: Simple Draw",
WS_OVERLAPPEDWINDOW,100,100,600,300,
GetDesktopWindow(),NULL,GetModuleHandle(NULL),NULL);
ShowWindow(hWnd,SW_SHOWNORMAL);
}
|
Для тех, кто не знаком с программированием для Windows, поясню, что сперва необходимо зарегистрировать класс окна, затем мы создаем само окно и отображаем его на экране. Каждое окно имеет свой обработчик сообщений, поступающих от
Windows. Я ограничился лишь обработкой сообщения о закрытиии окна WM_DESTROY , чтобы закрыть все приложение:
LRESULT WINAPI WindowMsgProc(HWND hWnd,UINT msg,
WPARAM wParam,LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY: PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd,msg,wParam,lParam);
}
|
Теперь проинициализируем Direct3D:
int InitDirect3D(void)
{
D3DDISPLAYMODE d3d_dm;
D3DPRESENT_PARAMETERS d3d_pp;
d3d = Direct3DCreate8(D3D_SDK_VERSION);
if(!d3d) return 0;
if(FAILED(d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,
&d3d_dm)))
return 0;
ZeroMemory(&d3d_pp,sizeof(d3d_pp));
d3d_pp.Windowed = TRUE;
d3d_pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3d_pp.BackBufferFormat = d3d_dm.Format;
if(FAILED(d3d->CreateDevice(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3d_pp,&d3d_device)))
return 0;
return 1;
}
|
Функция инициализации InitDirect3D() разбита на два этапа:
1. Создание объекта Direct3D. Это первый объект, который должна получить любая ваша программа. Только через него мы сможем получить информацию о всех устройствах Direct3D и создать любой из них.
2. Создание устройства Direct3D. Устройство Direct3D является основным компонентом, который выполняет трансформацию, освещение и растеризацию наших графических данных, проще говоря, именно оно и рисует нашу картинку. Устройство Direct3D
может быть нескольких типов, но остановимся на основных двух:
- D3DDEVTYPE_HAL - Аппаратное устройство, в котором все операции выполняет с помощью графического процессора видеокарты, что заметно ускоряет работу.
- D3DDEVTYPE_REF - Программное устройство, все операции в котором выполняются центральным процессором, что крайне медленно, поэтому лучше его не использовать.
Перед тем, как мы продолжим, я бы хотел немного остановиться на основных принципах построения 3-х мерной графики, т.к. без понимания этих основ последующий материал будет просто не понятен.
Direct3D оперирует тремя основными пространственными координатими X, Y и Z . Иначе их можно назвать "ширина","высота" и "глубина". С помощью этих трех координат можно однозначно определить положение любого объекта
в пространстве, будь-то это человек, дом или дерево. Любой объект состоит из конечного количества точек (для дальнейшего удобства будем называть точки - вершинами), которые образуют его каркас.
Рисунок 1. Куб
Каркас объекта состоит из треугольников, каждый из которых определен в пространстве тремя вершинами. Треугольник - это основной графический примитив, который используются в Direct3D. Например, модель куба, показанного на Рис.1,
состоит из 12 треугольников и 8 вершин. Каждая вершина в объекте имеет свои уникальные параметры: положение в пространстве, цвет вершины и т.д.
Теперь вернемся обратно к нашим фигурам. Очевидно, что нам нужно всего три треугольника, два из которых будут образовывать квадрат. Т.к. все треугольники состоят из вершин, нам необходимо определит новый тип данных.
struct CUSTOMVERTEX {
FLOAT x,y,z,rhw;
DWORD color;
};
|
Мы создали структуру CUSTOMVERTEX , в которой содержится необходимая нам информация о каждой вершине:
x,y,z - координаты вершины
rhw - обратная величина координаты w из гомогенной точки (x,y,z,w)
color - цвет вершины
Мы используем величину rhw , для того, чтобы самим определить позицию каждой вершины в окне без стандартных преобразований Direct3D. Сформируем массив вершин Vertices с нашими данными:
CUSTOMVERTEX Vertices[] = {
{150.0f, 50.0f,0.5f,1.0f,0x00ff0000},
{250.0f,250.0f,0.5f,1.0f,0x0000ff00},
{ 50.0f,250.0f,0.5f,1.0f,0x000000ff},
{500.0f, 50.0f,0.5f,1.0f,0x00ff0000},
{500.0f,250.0f,0.5f,1.0f,0x0000ff00},
{300.0f,250.0f,0.5f,1.0f,0x000000ff},
{300.0f,250.0f,0.5f,1.0f,0x000000ff},
{300.0f, 50.0f,0.5f,1.0f,0x0000ff00},
{500.0f, 50.0f,0.5f,1.0f,0x00ff0000},
};
|
В нашем массиве 9 вершин, которые и составляют три треугольника. Чтобы устройство Direct3D нарисовало наши фигуры, необходимо данные записать в специальный буфер вершин.
int InitDirect3DData(void)
{
void *pVertices;
if(FAILED(d3d_device->CreateVertexBuffer(9*sizeof(CUSTOMVERTEX),
0,D3DFVF_CUSTOMVERTEX,
D3DPOOL_DEFAULT,&d3d_vb))) return 0;
if(FAILED(d3d_vb->Lock(0,sizeof(Vertices),(BYTE**)&pVertices,0)))
return 0;
memcpy(pVertices,Vertices,sizeof(Vertices));
d3d_vb->Unlock();
return 1;
}
|
При создании этого буфера мы должны указать формат наших вершин. Для этого существуют так называемые флаги формата вершины.
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
|
Первый флаг D3DFVF_XYZRHW указывает на то, что передаваемая вершина содержит позицию вершины, а второй флаг D3DFVF_DIFFUSE - ее цвет. После инициализации Direct3D и подготовки данных, теперь мы можем нарисовать наши фигуры.
void RenderDirect3D(void)
{
d3d_device->Clear(0,NULL,D3DCLEAR_TARGET,
D3DCOLOR_XRGB(0,0,0),1.0f,0);
d3d_device->BeginScene();
d3d_device->SetStreamSource(0,d3d_vb,
sizeof(CUSTOMVERTEX));
d3d_device->SetVertexShader(D3DFVF_CUSTOMVERTEX);
d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST,0,3);
d3d_device->EndScene();
d3d_device->Present(NULL,NULL,NULL,NULL);
}
|
Прорисовка сцены выполняется функцией DrawPrimitive , аргументами которой являются: последовательность и тип графических примитивов, в нашем случае это список треугольников (D3DPT_TRIANGLELIST), индекс первого
элемента в буфере вершин и количество выводимых треугольников. Чтобы выполнять все вышеперечисленные действия в правильном порядке, их нужно определить в главной функции программы.
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow)
{
MSG msg;
CreateMainWindow();
if(!InitDirect3D()||!InitDirect3DData())
return 0;
PeekMessage(&msg,NULL,0,0,PM_NOREMOVE);
while(msg.message!=WM_QUIT)
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
RenderDirect3D();
}
ReleaseDirect3D();
return 0;
}
|
Перед выходом из главной функции программы закроем все созданные нами объекты Direct3D.
void ReleaseDirect3D(void)
{
if(d3d_vb) d3d_vb->Release();
if(d3d_device)
d3d_device->Release();
if(d3d) d3d->Release();
}
|
Полный исходный текст программы и exe-файл находится тут.
Взято с DirectX Design
|