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 - начнем снова

Часть 5 - Шкурный вопрос

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

Пятая часть учебника целиком и полностью посвящена тому, без чего 3D графика была бы голая и никому не нужная. Я конечно же имею в виду текстуры. В этой части мы переделаем прошлый пример с цилиндром, выкинем из него освещение, но зато добавим код, загружающий и отображающий текстуры.

Так как рассусоливать здесь больше нечего, переходим сразу к практике.
Создаем новый проект Win32 и приаттачиваем ко всем конфигурациям следующие либы:
d3dx8.lib d3d8.lib winmm.lib

В новом файле dgtut5.cpp начнем вколачивать программу.

#include <d3dx8.h>
#include <mmsystem.h>

LPDIRECT3D8 g_pD3D = NULL; // Объект Direct3D
LPDIRECT3DDEVICE8 g_pd3dDevice = NULL; //Устройство рендеринга
LPDIRECT3DVERTEXBUFFER8 g_pVB = NULL; //Буфер для вершин
LPDIRECT3DTEXTURE8 g_pTexture = NULL; //Текстура

Здесь комментировать-то даже нечего. Из нового только объект текстуры.

//Структура, определяющая наш формат вершины (aka FVF)
struct CUSTOMVERTEX
{
D3DXVECTOR3 position; // 3D-позиция вершины
D3DCOLOR color; // Цвет
FLOAT tu, tv; // Координаты текстуры
};

Из формата вершины мы выкинули нормали, так как не будем возиться со светом, а зато добавили новую фичу: координаты текстуры. Что это такое, вам сейчас станет понятно.

Координаты текстуры преследуют одну цель - определить растяжение текстуры по поверхности геометрии. Координаты могут меняться от (0.0, 0.0) - левый верхний угол картинки с текстурой, до (1.0, 1.0) - правый нижний. Таким образом, мы прикрепляем определенный участок шкуры к определенной вершине. Не трудно догадаться, что этот способ позволяет растягивать текстуру как угодно.

Пойдем дальше:

//Флаговое описание нашего FVF
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)

VOID Cleanup();
HRESULT InitD3D( HWND hWnd );
HRESULT InitGeometry();
VOID SetupMatrices();
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", "DirectX Graphics 5 - Текстуры",
WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
GetDesktopWindow(), NULL, wc.hInstance, NULL );

// Инициализировать Direct3D
if( SUCCEEDED( InitD3D( hWnd ) ) )
{
//Создать буфер вершин
if( SUCCEEDED( InitGeometry() ) )
{
// Показать окно
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;
}

В InitD3D добавилась только одна строчка. Сейчас мы до нее дойдем:

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;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

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

// Убрать culling так, чтобы мы видели и переднюю и заднюю сторону треуголника
g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );

Вот тут - мы выключили освещение в D3D

// Выключить освещение D3D
g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

// Включить Z-буфер
g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );

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

В Cleanup добавилась операция уничтожения объекта текстуры

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

if( g_pVB != NULL )
g_pVB->Release();

if( g_pd3dDevice != NULL)
g_pd3dDevice->Release();

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

Большие изменения произошли в InitGeometry

HRESULT InitGeometry()
{
// Используем инструменты D3DX для того, чтобы создать объект текстуры из файла
if( FAILED( D3DXCreateTextureFromFile( g_pd3dDevice, "texture.bmp",
&g_pTexture ) ) )
return E_FAIL;

Текстура загружается в память с помощью всего лишь одной процедуры. Здорово, правда? А помните нудный процесс загрузки битмапа на поверхность в DirectDraw?!

Далее повторяется процесс создания цилиндра, но теперь вместо нормалей, мы просчитываем и устанавливаем координаты текстуры.

// Создать буфер вершин
if( FAILED( g_pd3dDevice->CreateVertexBuffer( 50*2*sizeof(CUSTOMVERTEX),
0, D3DFVF_CUSTOMVERTEX,
D3DPOOL_DEFAULT, &g_pVB ) ) )
{
return E_FAIL;
}

// Заполнить буфер вершин. Мы устанавливаем координаты tu и tv,
// которые находятся в промежутке от 0.0 до 0.1

CUSTOMVERTEX* pVertices;
if( FAILED( g_pVB->Lock( 0, 0, (BYTE**)&pVertices, 0 ) ) )
return E_FAIL;

for( DWORD i=0; i<50; i++ )
{
FLOAT theta = (2*D3DX_PI*i)/(50-1);

pVertices[2*i+0].position = D3DXVECTOR3( sinf(theta),-1.0f, cosf(theta) );
pVertices[2*i+0].color = 0xffffffff;
pVertices[2*i+0].tu = ((FLOAT)i)/(50-1);
pVertices[2*i+0].tv = 1.0f;

pVertices[2*i+1].position = D3DXVECTOR3( sinf(theta), 1.0f, cosf(theta) );
pVertices[2*i+1].color = 0xff808080;
pVertices[2*i+1].tu = ((FLOAT)i)/(50-1);
pVertices[2*i+1].tv = 0.0f;
}
g_pVB->Unlock();

return S_OK;
}

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

Следующая функция осталась без изменений

VOID SetupMatrices()
{
// Установить матрицу мира
D3DXMATRIX matWorld;
D3DXMatrixIdentity( &matWorld );
D3DXMatrixRotationX( &matWorld, timeGetTime()/500.0f );
g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );

// Установить матрицу обзора
D3DXMATRIX matView;
D3DXMatrixLookAtLH( &matView, &D3DXVECTOR3( 0.0f, 3.0f,-5.0f ),
&D3DXVECTOR3( 0.0f, 0.0f, 0.0f ),
&D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );
g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );

// Установить матрицу проекции
D3DXMATRIX matProj;
D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f );
g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
}

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

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

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

// Установить матрицы мира, обзора и проекции
SetupMatrices();

Теперь мы должны выбрать текстуру, с помощью которой устройство рендеринга будет рисовать геометрию, вплоть до следующего переопределения SetTexture

// Выбираем для рендеринга созданную текстуру
g_pd3dDevice->SetTexture( 0, g_pTexture );

Первый параметр - это номер текстуры. На этом самом моменте, мы вплотную сталкиваемся с возможностями мультитекстурирования в DirectX. Текстура под номером 0 - это базовая текстура, которая должна быть всегда. Если вы собираетесь нанести несколько текстур одновременно на одну поверхность, то бишь провести смешивание (blending). DirectX поддерживает смешивание восьми текстур одновременно, то есть значение первого параметра будет меняться от 0 до 7. Однако реальная цифра зависит от возможностей графической карты, и вам следует проверить ее перед использованием смешения с помощью GetDeviceCaps.
Впрочем, у нас с вами тут рассматривается простейший случай и никаким мультитекстурированием голову забивать мы не будем.

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

// Делаем установки уровня текстурирования
g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE );

Во всех подробностях об этих параметрах вы можете почитать в документации к SDK. На самом деле, как я уже сказал, здесь выставлены значения по умолчанию.

Далее идет стандартный рендеринг потока вершин.

// Рендерить содержимое буфера вершин
g_pd3dDevice->SetStreamSource( 0, g_pVB, sizeof(CUSTOMVERTEX) );
g_pd3dDevice->SetVertexShader( D3DFVF_CUSTOMVERTEX );
g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2*50-2 );

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

// Показать содержимое заднего буфера на экране
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}

Ну вот, собственно, и все! Откомпилируйте программу. После этого положите в папку с exe-файлом какую-нить картинку размером 2^xx например 256x256 и с именем texture.bmp Когда сие будет выполнено, можно запускать программу.

Теперь, скажу пару строк тем, кого ну никак не устраивают текстуры в формате bmp. В DirectX вы не можете загружать напрямую ни jpg, ни gif, ни даже tga. Для того, чтобы использовать текстуры собственного формата, вам необходимо писать собственный загрузчик. Вся соль в том, чтобы в конечном итоге получить в объекте LPDIRECT3DTEXTURE8 полноценную текстуру. А как уж она туда попадет: с помощью встроенных функций или методом хитрых преобразований - тема совсем других бесед.

Увидимся в следующей части!

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

Приятного программирования: Anti

[Вверх]

Posted: 30.03.2k1
Author: Anti
<anti_loop@mail.ru>

 


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

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


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








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