Visual Basic - мастерская разработчика
Библиотеки

DirectX

Обзоры
DirectDraw
Direct3D
DirectX Audio
DirectPlay
DirectInput
Fido Topics
SourceCode
Tools&Libs

OpenGL

Статьи и учебники
Fido Topics
SourceCode
Tools&Libs

Архив по Glide

Движки

Обзоры
Учебники
SourceCode
Downloads

Создание игр

Ваши игры

Обзорные статьи
Учебники
Fido Topics
SourceCode
Download

Stuff

Программер-Чат

Псевдо-FTP
Disclaimer
Оффтопик

 

DirectX Graphics - начнем снова

Часть 1 - Рендеринг... Ничего

[Следующая часть] [Приложения к этой части]

Судя по проводимым мною опросам, человечество :) более всего было заинтересовано в изучении D3D:Immediate Mode, который я упорно не освещал на своем сайте. Причина была в том, что я ждал когда же выйдет DirectX8, чтобы не обламываться на полпути изучения седьмой версии Direct3D:IM Как выяснилось с выходом этой самой восьмой версии, я ждал не то чтобы не напрасно, а просто очень хорошо, что я ждал, так как добрая Microsoft позаботилась о нас всех и сделала D3D:IM главнейшей компонентой в DX8, обозвав ее DirectX Graphics (Туда же входит библиотека Direct3DX, по сути являющаяся переделанным D3D:RM, но о ней мы поговорим в другой раз и в другом месте)

О чем же пойдет речь? Так как MS серьезно переделала D3D:IM в восьмой версии, мы со спокойной душой, что по крайней мере год ничего нового, более ужасного не произойдет, сможем заняться изучением D3D:IM, то есть Direct Graphics или наконец просто Direct3D. Вот сколько много названий...

Как всегда, мои последующие мысли основаны на DX SDK и в случае чего непонятного первым делом обращайтесь туда. Ну потом можете уже и писать... :)

Начнем мы с создания простейшего приложения, как это написано в MS-туториале. В оправдание себя скажу, что чисто по MS-туториалу без примера сделать ничего невозможно, да к тому же я постараюсь все это дело сбавить огромным количеством пояснений...

Так, себя похвалил... Переходим к делу.

(Немножко по организации. Я планирую сначала издавать статьи, обращенные к Visual C++ и сразу вслед за ними переводить, ежели конечно это возможно, на Visual Basic)

В этой части мы нарисуем окно, в котором мы создадим... Ничего!
Нет, ну что-то у нас все-таки будет и это что-то называется устройством рендеринга. Оно нам очистит вьюпорт и мы увидим прекрасную синюшную сцену.
Я предполагаю, что у вас уже стоит VC версии 6, DX8 SDK и в первом правильно установлены пути к инклудам и либам второго. Не забудьте добавить либ d3d8.lib в настройках конфигурации.

Добавьте к проекту файл с любым названием, например dgtut1.cpp

Для начала, парочка объявлений

#include <d3d8.h>

LPDIRECT3D8 g_pD3D = NULL; // Используется для содания устроййства рендеринга
LPDIRECT3DDEVICE8 g_pd3dDevice = NULL; //Собственно, само устройство рендеринга

HRESULT InitD3D( HWND hWnd );
VOID Render();
VOID Cleanup();

Как вы наверное догадываетесь, первой функцией у нас всегда бывает функция, создающая окно - WinMain. Напишем же ее!

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 01: CreateDevice",
WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
GetDesktopWindow(), NULL, wc.hInstance, NULL );

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

// Инициализировать Direct3D
if( SUCCEEDED( InitD3D( hWnd ) ) )
{
// Показать окно
ShowWindow( hWnd, SW_SHOWDEFAULT );
UpdateWindow( hWnd );

// Войти в главный цикл сообщений программы
MSG msg;
while( GetMessage( &msg, NULL, 0, 0 ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}

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

// Вызвать функцию - чистильщика
Cleanup();
// Ну и убить класс окна
UnregisterClass( "D3D Tutorial", wc.hInstance );
return 0;
}

Так. Для окна нам осталось написать еще одну вполне стандартную функцию - MsgProc:

LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_DESTROY: // При выходе мы... выходим :)
PostQuitMessage( 0 );
return 0;

case WM_PAINT:
// Тут мы вызываем функцию, которая будет нам рисовать на окне - aka рендерить
Render();
ValidateRect( hWnd, NULL );
return 0;
}

return DefWindowProc( hWnd, msg, wParam, lParam );
}

Теперь, собственно, начинается сам D3D. Как я и обещал, мы сейчас напишем функцию, которая все это дело инициализирует.

HRESULT InitD3D( HWND hWnd )
{
// Создать объект D3D, который потребуется для создания устройства рендеринга
if( NULL == ( g_pD3D = Direct3DCreate8( D3D_SDK_VERSION ) ) )
return E_FAIL;

Мы всегда должны указывать в качестве параметра D3D_SDK_VERSION Эта непонятная заморочка нужна, чтобы синхронизировать приложение с нужным заголовочным файлом. Заголовочный файл будет требовать перекомпиляции программы. Если их версии не совпадут, Direct3DCreate8 выдаст ошибку.

// Получить текущий режим экрана
D3DDISPLAYMODE d3ddm;
if( FAILED( g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) )
return E_FAIL;

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

  • UINT Width; UINT Height; UINT RefreshRate; - соответственно ширина, высота и частота обновления экрана.
  • D3DFORMAT Format; - структура D3DFORMAT, которая описывает формат поверхности. Лазить в нее вручную довольно муторное занятие, поэтому если вам она нужна - смотрите документацию

Так. На чем мы остановились?
Теперь нам как и в DirectDraw указать параметры создаваемого устройства рендеринга в особой структуре:

D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = d3ddm.Format;

Сначала мы обнуляем всю структуру. В параметр d3dpp.Windowed мы передаем значение TRUE, так как будем работать в окне. Параметр d3dpp.SwapEffect означает способ отображения заднего буфера на дисплее. Особо не волнуйтесь по поводу этого параметра, смело устанавливая его в D3DSWAPEFFECT_DISCARD. Наконец, мы устанавливаем формат заднего буфера в соответствии с текущим форматом поверхностей экрана.

Наконец, переходим к самому главному - созданию устройства рендеринга:

if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_pd3dDevice ) ) )
{
return E_FAIL;
}

D3DADAPTER_DEFAULT означает главный видеоадаптер. Мы не будем сейчас рассматривать использование дополнительный видеокарт типа Voodoo, оставив это на потом. D3DDEVTYPE_HAL означает использование HAL (Hardware Abstraction Layer) - проще говоря, аппаратного ускорения видеокарты. Для этого случая ваша главная видеокарта должна иметь функции аппаратного ускорения (вы скорее-всего знаете, может она это или нет) хотя бы на уровне всенародной S3 Virge VX/GX с двумя мегами памяти :) Нам сейчас не важна производительность карты - мы хотим заставить приложение работать. Ежели ваша видеокарта "совсем никак", тогда ставьте вместо этого параметра D3DDEVTYPE_REF - это применение функций Direct3D через софтварную эмуляцию. Она хороша тем, что будет позволять выполнять все трехмерные функции вплоть до фильтрации текстур. Кроме этого, софтварная эмуляция D3D будет использовать при возможности специальные инструкции процессора, как говорится, takes advantages of 3DNow! instructions set! ;0) Но в бочку меда капает то, что эта самая эмуляция мягко говоря не быстра.... Поэтому, для продвинутых разработчиков, использующих собственные софтовые устройства рендеринга сделали параметр D3DDEVTYPE_SW - подключаемое софтварное устройство, которое должно быть зарегистрировано с помощью метода IDirect3D8::RegisterSoftwareDevice Но этим мы сейчас заниматься не будем.

// Все в порядке, можно вернуть что-нибудь хорошее
return S_OK;
}

Так, теперь мы будем писать функцию, которая будет рендерить нам искомое Ничего на экран. Как бы то ни было, Ничего тоже надо еще умудриться нарисовать :)

VOID Render()
{
// Если устройства нет, то и рендерить нам нечем
if( NULL == g_pd3dDevice )
return;

// Очистить задний буфер в синий цвет
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );

По хорошему, в первых двух параметрах передается массив прямоугольников. В первом параметре их количество, а во втором указатель на массив структур D3DRECT, которые указывают своими координатами что надо чистить. Подобным методом, вы можете очистить только часть экрана, например если вы рисуете радар, у вас экран уменьшен, да и просто сцена располагается не на всем экране, оставляя по боками кусочки для отображения всяких статусов и т.п. Как бы там ни было, ма передаем сначала 0, а потом NULL - это означает, что очищать надо весь вьюпорт.
D3DCLEAR_TARGET - значит, что очищать надо только поверхность, на которую происходит рендеринг, игнорируя всякие там глубинные буфера и проч.
D3DCOLOR_XRGB(0,0,255) - задает цвет в формате RGB, каким мы будем очищать (а конкретнее - заливать) указанный участок. Тут мы очищаем синим цветом, но в большинстве случаев вы скорее всего захотите очищать черным цветом. В конце-концов, это все равно будет потом заполнено объектами, так что какая разница!
И наконец, последние два параметра в нашем случае неиспользования глубинных буферов просто игнорируются.

// Начало рендеринга
g_pd3dDevice->BeginScene();

Все команды рендеринга идут после этого объявления, а не до

// Здесь происходит рендеринг всех объектов сцены

Следующим объявлением завершается рендеринг. После него вы не можете ставить команды рендеринга.

// Конец рендеринга
g_pd3dDevice->EndScene();

Чтобы пользователь увидел, чего мы там нарендерили, надо показать это ему. Совершаем знакомый нам по DirectDraw флиппинг поверхностей. Не забивайте себе голову множеством NULLей в параметрах. Они нужны только для шибко хитрых случаев. Если что - почитайте документацию :)

// Делаем содержимое заднего буфера видимым на переднем (aka Flip)
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}

Разобравшись с рендерингом, нам осталось написать только процедуру-уборщик:

VOID Cleanup()
{
if( g_pd3dDevice != NULL)
g_pd3dDevice->Release();

if( g_pD3D != NULL)
g_pD3D->Release();
}

Это все. Правда все! :)

Если вы правильно все переписали, то компилируйте программу и все должно работать!

Следующее по плану у нас - рендеринг более существенных вещей, чем просто Ничего...

Приложение: Готовый проект (17k)

Приятного программирования, Antiloop

[Вверх]

Posted: 23.01.2k1
Autor: Antiloop
<anti_loop@mail.ru>

 


Проект
Создание Народного Учебника по OpenGL

Участвовать!
Поиск
Найдите статью или файл:


Рассылка
Новости сайта
La Vision в вашем почтовом ящике








Программирование на С++ Delphi и Паскаль
Центр демо-искусства в России