DirectX Graphics - начнем снова
Часть 5 - Шкурный вопрос
[Предыдущая часть]
[Следующая часть] [Приложения
к этой части]
Пятая часть учебника целиком и полностью посвящена тому, без чего 3D графика
была бы голая и никому не нужная. Я конечно же имею в виду текстуры.
В этой части мы переделаем прошлый пример с цилиндром, выкинем из него освещение,
но зато добавим код, загружающий и отображающий текстуры.
Так как рассусоливать здесь больше нечего, переходим сразу к практике.
Создаем новый проект Win32 и приаттачиваем ко всем конфигурациям следующие либы:
d3dx8.lib d3d8.lib winmm.lib
В новом файле dgtut5.cpp начнем вколачивать программу.
#include <d3dx8.h>
#include <mmsystem.h>
LPDIRECT3D8 g_pD3D = NULL;
LPDIRECT3DDEVICE8 g_pd3dDevice = NULL;
LPDIRECT3DVERTEXBUFFER8 g_pVB = NULL;
LPDIRECT3DTEXTURE8 g_pTexture = NULL;
Здесь комментировать-то даже нечего. Из нового только объект текстуры.
struct CUSTOMVERTEX
{
D3DXVECTOR3 position;
D3DCOLOR color;
FLOAT tu, tv;
};
Из формата вершины мы выкинули нормали, так как не будем возиться со светом,
а зато добавили новую фичу: координаты текстуры. Что это такое, вам сейчас станет
понятно.
Координаты текстуры преследуют одну цель - определить растяжение текстуры по
поверхности геометрии. Координаты могут меняться от (0.0, 0.0) - левый верхний
угол картинки с текстурой, до (1.0, 1.0) - правый нижний. Таким образом, мы
прикрепляем определенный участок шкуры к определенной вершине.
Не трудно догадаться, что этот способ позволяет растягивать текстуру как угодно.
Пойдем дальше:
#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 );
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 )
{
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;
}
g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
Вот тут - мы выключили освещение в D3D
g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
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()
{
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;
}
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()
{
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>
|