Все главные и существенные вводные моменты мы уже рассмотрели, осталось только пройтись по ключевым примерам DirectX SDK, что займет несколько выпусков. Дальнейшее развитие серии зависит от вас, то есть, можете выбрать пункт, о котором вы хотите узнать прежде всего: - Создание объектов и персонажей в Direct3D, рисование, текстурирование, анимация.
- Создание карт, визуализация ландшафта.
- Освещение, шейдеры, HLSL.
- Работа с системами частиц.
- Разработка интерфейса.
- Искусственный интеллект.
- Сетевое программирование.
- Программирование звука для игр.
- Перевод игр на консоли.
Все темы достаточно объемны, поэтому вам предлагается на выбор: с чего лучше стартовать в следующем году. Причем, будут изменения в формате представления материалов, они уже не будут выходить как общая серия «Разработка компьютерных игр», а разбиваться тематически. В общем, пишите все ваши пожелания на почтовый ящик или в гостевую книгу.
Инициализация Direct3D
Теперь с багажом полученных знаний мы начнем внедряться в программирование. Осознанно. Открываем самый первый пример из документации к SDK, который расположен по адресу …\Program Files\Microsoft DirectX SDK\Samples\C++\Direct3D\Tutorials\Tut01_CreateDevice, в рамках нашей Visual Studio. Ваш покорный слуга опирается на Visual Studio 2008, хотя данный момент не сильно принципиален. Смотрим файл CreateDevice.cpp (полный листинг без объяснений в конце материала). Заголовочный файл DirectX SDK подключается следующим образом:
#include <d3d9.h>
Итак, первым этапом инициализации идет запрос указателя на интерфейс IDirect3D, который применяется для получения информации об аппаратных устройствах компьютера, а также для создания интерфейса IDirect3DDevice9, который будет являться объектом С++, представляющим аппаратное устройство для вывода трехмерной графики (визуализации). Этому соответствуют строки:
LPDIRECT3D9 g_pD3D = NULL; LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
В принципе, лично ваш покорный слуга в качестве примера для избежания путаницы назначил бы другие имена переменных, например, так бы было все намного понятнее:
LPDIRECT3D9 direct3d = NULL; LPDIRECT3DDEVICE9 device = NULL;
…но все же будем опираться на листинг программы c учетом ее названий переменных.
HRESULT InitD3D( HWND hWnd ) {
Init3D — функция инициализации главного окна приложения, содержащая код инициализации Direct3D. Здесь содержатся ключевые строки. Создавая главный объект, мы вызываем функцию Direct3DCreate9 с единственным параметром D3D_SDK_VERSION, который является предопределенной константой, описанной в заголовочном модуле (d3d9.h), и указывает на версию DirectX. То есть, в простейшем виде можно записать:
g_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
В нашем случае мы создаем главный объект следующим образом, причем нам нужно просто узнать, установлен ли DirectX вообще:
if(NULL == (g_pD3D = Direct3DCreate9(D3D_SDK_VERSION) ) ) return E_FAIL;
Следующим этапом подключается структура, используемая для создания интерфейса IDirect3DDevice9:
D3DPRESENT_PARAMETERS d3dpp; ZeroMemory( &d3dpp, sizeof(d3dpp) ); d3dpp.Windowed = TRUE; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
Теперь расшифруем: ZeroMemory () — заполняем указанную строку нулями, Windowed=True указывает на то, что запуск будет происходить в окне, в рамках SwapEffect задается режим действия механизма двойной буферизации, в данном случае мы выбрали форсированный вариант D3DSWAPEFFECT_DISCARD, BackBufferFormat указывает на то, какой формат буфера будет использоваться. Теперь мы создаем устройство вывода (визуализации):
if (FAILED(g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pd3dDevice) ) ) { return E_FAIL; } return S_OK;
Отдельно стоит сказать, что в книгах по программированию DirectX вы чаще всего встретите несколько отличный подход к написанию кода, то есть, там все выполняется без условий if. В SDK же это показано в рамках более осторожных методов, то есть, «если это возможно…». Теперь расшифровываем строку. Вызывая метод CreateDevice мы получаем ссылку на IDirect3DDevice9, с помощью которого производится визуализация. Далее все вам должно быть знакомо, хотя пробежаться по параметрам еще раз не помешает: D3DADAPTER_DEFAULT указывает на номер графического адаптера, установленного в системе, D3DDEVTYPE_HAL — тип устройства, и в данном случае мы подразумеваем полностью аппаратную обработку (если бы указали D3DDEVTYPE_REF, то была бы программная), третий параметр позволяет задать окно (Handle), в которое будет производиться выход сцены, D3DCREATE_SOFTWARE_VERTEXPROCESSING указывает на программную обработку вершин, потому как, например, видеоадаптер может ее не поддерживать, в &d3dpp хранятся параметры создаваемого устройства вывода, &g_pd3dDevice — имя переменной, куда будет помещен результат. На самом деле, не смотря на кажущуюся сложность, при близком рассмотрении все оказывается достаточно простым. Причем в рамках этого листинга не делается одна вещь, которую мы описывали ранее, а именно, проверка возможностей графического адаптера. Дальше в коде идет функция очистки памяти. Напомню, что мы имеем дело с COM-объектами, и для их удаления не подходит ключевое слово delete, а вместо него используется метод Release.
VOID Cleanup() { if( g_pd3dDevice != NULL) g_pd3dDevice->Release();
if( g_pD3D != NULL) g_pD3D->Release(); }
Теперь наступила реализация вывода пустого окна, закрашенного в определенный цвет, в указанном примере, синий. Для этого используем метод Clear() интерфейса IDirect3DDevice9.
VOID Render() { if( NULL == g_pd3dDevice ) return;
Закрашиваем вторичный буфер в синий цвет:
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
Начало сцены:
if( SUCCEEDED( g_pd3dDevice->BeginScene() ) ) { //здесь помещаются объекты сцены g_pd3dDevice->EndScene(); }
Вывод содержимого вторичного буфера на дисплей с помощью метода Present() интерфейса IDirect3DDevice9. g_pd3dDevice->Present( NULL, NULL, NULL, NULL ); }
Теперь имеет смысл посмотреть на функцию wWinMain, которая, собственно и отвечает за все происходящее, а именно, инициализирует главное окно и Direct3D, вызывает процедуру инициализации приложения, запускает цикл обработки сообщений, освобождает ресурсы:
INT WINAPI wWinMain( HINSTANCE hInst, HINSTANCE, LPWSTR, INT ) {
Определяемся с оконным классом: WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, L"D3D Tutorial", NULL }; RegisterClassEx( &wc );
Создание окна приложения с размерами 300х300 пикселей:
HWND hWnd = CreateWindow( L"D3D Tutorial", L"D3D Tutorial 01: CreateDevice", WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, NULL, NULL, wc.hInstance, NULL );
Инициализация Direct3D:
if( SUCCEEDED( InitD3D( hWnd ) ) ) {
Показать окно: ShowWindow( hWnd, SW_SHOWDEFAULT ); UpdateWindow( hWnd );
Запуск цикла обработки сообщений:
MSG msg; while( GetMessage( &msg, NULL, 0, 0 ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } } UnregisterClass( L"D3D Tutorial", wc.hInstance ); return 0; }
Каждый раз при обновлении цикла DispatchMessage вызывает функцию обработки системных сообщений MsgProc, которая прописана отдельно:
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { switch( msg ) { case WM_DESTROY: Cleanup(); PostQuitMessage( 0 ); return 0;
case WM_PAINT: Render(); ValidateRect( hWnd, NULL ); return 0; }
return DefWindowProc( hWnd, msg, wParam, lParam ); }
Другими словами, программа ждет, пока выполнится закраска WM_PAINT, после чего начинает перерисовывать. А как работают Cleanup() и Render() мы можем увидеть выше. В результате, при запуске программы, пред нами появляется окно, закрашенноев однотонный синий цвет. В большинстве случаев листинги следует рассматривать от функции WinMain, но в нашем случае просмотр сверху-вниз оправдан. Второй пример Tutorial02 из DirectX SDK, в котором выводится треугольник с заданными вершинами и градиентной заливкой, мы уже рассматривали, то есть… к самостоятельному изучению.
Тест
На самом деле, в качестве тестового задания попробуйте несколько перевернуть описание нашего примера и самостоятельно сделать блок-схему, логически построенную от WinMain(), а не от создания Direct3D-объекта. То есть, мы просмотрели листинг сверху-вниз, и в данном случае это не было неудобным, но в ряде случаев правильнее "плясать" от Main().
Полный код файла CreateDevice.cpp, который мы только что рассмотрели
// File: CreateDevice.cpp // Copyright (c) Microsoft Corporation. All rights reserved. //--------------------------------- #include <d3d9.h> #pragma warning( disable : 4996 ) #include <strsafe.h> #pragma warning( default : 4996 ) //------------------------ // Global variables //------------------------ LPDIRECT3D9 g_pD3D = NULL; LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; //--------------------------- // Name: InitD3D() // Desc: Initializes Direct3D //--------------------------- HRESULT InitD3D( HWND hWnd ) { if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) ) return E_FAIL; //-------------------------- D3DPRESENT_PARAMETERS d3dpp; ZeroMemory( &d3dpp, sizeof(d3dpp) ); d3dpp.Windowed = TRUE; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; //------------------------- if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pd3dDevice ) ) ) { return E_FAIL; } //------------------------ return S_OK; } //----------------------- // Name: Cleanup() // Desc: Releases all previously initialized objects //----------------------- VOID Cleanup() { if( g_pd3dDevice != NULL) g_pd3dDevice->Release();
if( g_pD3D != NULL) g_pD3D->Release(); } //---------------------- // Name: Render() // Desc: Draws the scene //--------------------- VOID Render() { if( NULL == g_pd3dDevice ) return; // Clear the backbuffer to a blue color g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 ); // Begin the scene if( SUCCEEDED( g_pd3dDevice->BeginScene() ) ) { // Rendering of scene objects can happen here // End the scene g_pd3dDevice->EndScene(); } // Present the backbuffer contents to the display g_pd3dDevice->Present( NULL, NULL, NULL, NULL ); } //------------------------- // Name: MsgProc() // Desc: The window's message handler //------------------------- LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { switch( msg ) { case WM_DESTROY: Cleanup(); PostQuitMessage( 0 ); return 0;
case WM_PAINT: Render(); ValidateRect( hWnd, NULL ); return 0; } return DefWindowProc( hWnd, msg, wParam, lParam ); } //---------------------- // Name: wWinMain() // Desc: The application's entry point //---------------------- INT WINAPI wWinMain( HINSTANCE hInst, HINSTANCE, LPWSTR, INT ) { WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, L"D3D Tutorial", NULL }; RegisterClassEx( &wc ); //--------------------- HWND hWnd = CreateWindow( L"D3D Tutorial", L"D3D Tutorial 01: CreateDevice", WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, NULL, NULL, wc.hInstance, NULL ); // Initialize Direct3D if( SUCCEEDED( InitD3D( hWnd ) ) ) { // Show the window ShowWindow( hWnd, SW_SHOWDEFAULT ); UpdateWindow( hWnd ); // Enter the message loop MSG msg; while( GetMessage( &msg, NULL, 0, 0 ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } } UnregisterClass( L"D3D Tutorial", wc.hInstance ); return 0; }
Перепечатка материалов или их фрагментов возможна только с согласия автора.
|