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

Здесь представлена полная версия программы, только в начале нужно добавить две директивы include для файлов windows.h и d3d9.h.

код на языке c++
LRESULT __stdcall WndProc(HWND, UINT, WPARAM, LPARAM);

int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                      LPSTR lpCmdLine, int nCmdShow)
{
  WNDCLASS wc;
  wc.style = CS_OWNDC;
  wc.lpfnWndProc = WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hbrBackground = (HBRUSH)(6);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wc.hInstance = hInstance;
  wc.lpszClassName = L"class";
  wc.lpszMenuName = NULL;

  RegisterClass(&wc);

  HWND hWnd = CreateWindow(L"class",L"Инициализация Direct3D"
                           WS_OVERLAPPEDWINDOW,200,100,
                           500,500,NULL,NULL,hInstance,NULL);

  ShowWindow(hWnd,nCmdShow);
  UpdateWindow(hWnd);

  IDirect3D9* d3d = NULL;       // объект Direct3D
  IDirect3DDevice9* dev = NULL; // устройство (видеокарта)

  d3d = Direct3DCreate9(D3D_SDK_VERSION);

  D3DPRESENT_PARAMETERS pp;
  ZeroMemory(&pp,sizeof(pp));

  pp.BackBufferWidth = 500;
  pp.BackBufferHeight = 500;
  pp.BackBufferFormat = D3DFMT_X8R8G8B8;
  pp.BackBufferCount = 1;
  pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
  pp.hDeviceWindow = hWnd;
  pp.Windowed = true;

  d3d->CreateDevice(D3DADAPTER_DEFAULT,
                    D3DDEVTYPE_REF,hWnd,
                    D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                    &pp, &dev);
  MSG msg;
  while (1)
  {
    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
      if (msg.message == WM_QUIT)
        break;
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }

    dev->Clear(0,NULL,D3DCLEAR_TARGET,
               D3DCOLOR_XRGB(255,255,255),1.0f,0);

    dev->BeginScene();
    dev->EndScene();

    dev->Present(NULL,NULL,NULL,NULL);
    }

  if (dev != NULL)
    dev->Release();
  if (d3d != NULL)
    d3d->Release();
  return 0;
}

LRESULT __stdcall WndProc (HWND hWnd, UINT msg,
                           WPARAM wParam, LPARAM lParam)
{
  switch (msg)
  {
  case WM_DESTROY:
    PostQuitMessage(0);
    return 0;
  }
  return DefWindowProc(hWnd,msg,wParam,lParam);
}

Теперь немножко поподробнее. Код относящийся к Direct3d "встроен" в каркас приложения под Windows. Здесь можно выделить три основные части: до основного цикла - иницализация, код в основном цикле - построение сцены и вывод на экран, и после основного цикла - освобождение памяти, занимаемой экземплярами интерфейсов.

Инициализация Direct3D

Приложение на основе Direct3d строится вокруг "устройства". В Direct3D устройство - это представление видеокарты (физического устройства) в программе. За устройство отвечает интерфейс IDirect3DDevice9 (device - устройство). Соответствующую переменную обзовём dev.

Прежде чем получить указатель на устройство, во-первых необходимо получить указатель на объект IDirect3D9 - это позволит не прибегать к механизмам получения интерфейсов COM, во-вторых нужно заполнить структуру D3DPRESENT_PARAMETERS, в которой можно задать свойства основной и фоновой поверхности.

Интерфейс IDirect3D9 и функция Direct3DCreate9

Итак, программа использующая Direct3D начинается с получения указателя на интерфейс IDirect3D9. С помощью полученного объекта можно создать нужный нам объект IDirect3DDevice9. Получить указатель на IDirect3D9 можно с помощью одной из немногих функций Direct3D:

d3d = Direct3DCreate9(D3D_SDK_VERSION);
Аргументом функции Direct3DCreate9 всегда должна быть константа D3D_SDK_VERSION. Объявление этой константы расположено в файле d3d9.h, это так к слову.

Объект IDirect3D9 мы получили. Для успешного создания объекта устройства нужно заполнить структурную переменную pp типа D3DPRESENT_PARAMETERS:

код на языке c++
D3DPRESENT_PARAMETERS pp;
ZeroMemory(&pp,sizeof(pp));

pp.BackBufferWidth = 500;
pp.BackBufferHeight = 500;
pp.BackBufferFormat = D3DFMT_X8R8G8B8;
pp.BackBufferCount = 1;
pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
pp.hDeviceWindow = hWnd;
pp.Windowed = true;

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

На данный момент вам должно быть понятно назначение всех вышеперечисленных полей кроме одного - SwapEffect. Данное поле определяет каким способом будет происходить смена поверхностей (основной и фоновой). Здесь присвается значение D3DSWAPEFFECT_DISCARD - это самый эффективный способ вывода содержимого фонового буфера на экран. О других способах мы поговорим позднее.

Метод IDirect3D9::CreateDevice

Теперь, когда у нас есть экземпляр интерфейса IDirect3D9 и заполненная структурная переменная D3DPRESENT_PARAMETERS, можно создать устройство с помощью метода IDirect3D9::CreateDevice. Посмотрим на его объявление:

код на языке c++
HRESULT CreateDevice(
  UINT Adapter,
  D3DDEVTYPE DeviceType,
  HWND hFocusWindow,
  DWORD BehaviorFlags,
  D3DPRESENT_PARAMETERS * pPresentationParameters,
  IDirect3DDevice9 ** ppReturnedDeviceInterface
);

Метод IDirect3D9::CreateDevice принимает шесть аргументов.

UINT Adapter:
Непосредственно видеокарта. Используется, когда в компьютере установлено более одной видеокарты. Мы пока данный случай рассматривать не будем - в программах всегда будем передавать значение по умолчанию (ноль или, что то же самое, D3DADAPTER_DEFAULT).

D3DDEVTYPE DeviceType:
Можно использовать два типа устройств: физические (D3DDEVTYPE_HAL - Hardware Acceleration Level, ускорение на уровне физического устройства) и программные (D3DDEVTYPE_REF). Программные устройства медленнее физических, но зато они поддерживают все возможности Direct3D.

HWND hFocusWindow: Описатель окна, в которое будет осуществляться вывод. Если описатель окна уже был указан при заполнении структуры D3DPRESENT_PARAMETERS (поле hDeviceWindow), то в данный параметр можно передать NULL.

DWORD BehaviorFlags:
Данный аргумент позволяет задать различные свойства. В примере используется значение D3DCREATE_SOFTWARE_VERTEXPROCESSING, это значит, что вершины будут обрабатываться программно (центральным процессором, а не видеокартой).

D3DPRESENT_PARAMETERS * pPresentationParameters:
Данный аргумент - указатель на структурную переменную D3DPRESENT_PARAMTERS.

IDirect3DDevice9 ** ppReturnedDeviceInterface:
И последний аргумент - указатель на указатель на интерфейс IDirect3DDevice9.

Ну и собственно вызов метода:

код на языке c++
d3d->CreateDevice(D3DADAPTER_DEFAULT,
                  D3DDEVTYPE_REF,
                  hWnd,
                  D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                  &pp,
                  &dev);

С этого момента в нашей программе присутствует видеокарта.

Обратите внимание на получения интерфейсов IDirect3D9 и IDirect3DDevice9. В первом случае используется операция присваивания над результатом независимой функции Direct3DCreate9, а во втором - указатель на устройство передаётся в метод IDirect3D9::CreateDevice.

Вывод на экран в Direct3D

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

Перед выводом каждого кадра на экран нужно очищать экран от содержимого предыдущего кадра:

dev->Clear(0,NULL,D3DCLEAR_TARGET,
D3DCOLOR_XRGB(255,255,255),1.0f,0);
Остановимся только на четвёртом аргументе: D3DCOLOR_XRGB(255,255,255) - данный аргумент задаёт цвет, которым будет заполнен экран после очистки (в этом примере - белый). Остальные аргументы позволяют очистить только часть экрана, z-буфер, трафаретный буфер - нам это пока не нужно.

Следующие два метода:

dev->BeginScene();

dev->EndScene();
Именно между этими методами происходит всё самое важное - создание виртуального мира. Как видите, на данный момент между этими двумя методами пусто.

И наконец-то вызов последнего метода:

dev->Present(NULL,NULL,NULL,NULL);
Здесь происходит "представление" фонового буфера (смена поверхностей). Нам пока не важно какие аргументы передаются в данный метод.

Закрытие программы

При завершении программы нужно не забыть освободить память занимаемую экземплярами интерфейсов. Для

код на языке c++
этого используется COM'овский метод Release:

if (dev != NULL)
	dev->Release();
if (d3d != NULL)
	d3d->Release();

Метод Release есть у каждого интерфейса COM (наследуется от базового интерфейса IUnknown).

На сегодня всё.