| « Поставить закладку » « Сделать стартовой » | |||
|
|||
|
Статьи:: Графика и игроделание DirectX, OpenGL etc.) :: Direct3D :: Техника индексирования вершин примитивов
Техника индексирования вершин примитивов
Приветствую всех, кому предназначены данные уроки. В данном уроке мы рассмотрим один, весьма важный и полезный аспект - индексация примитивов (полигонов). Что это такое? Данный прием позволяет нам избежать повторения данных при задании вершин. Допустим, у вас есть куб - для его задания нам достаточно восемь вершин (в реальном мире), а при наших знаниях на данный момент - для его задания в нашей программе нам понадобится задавать двадцать четыре точки (на одну грань по два полигона, каждый полигон из трех вершин - 2*6*2=24). Если нам понадобится изменить какие-нибудь атрибуты конкретной вершины то, в первом случае мы изменяем параметры всего одной точки, во втором же нам придется изменять параметры от трех до шести заданных нами псевдовершин:
Тут нам поможет индексирование полигонов. Мы просто создаем нужные нам вершины и потом задаем полигоны путем указания необходимых индексов (каждый из которых сопоставляется вершине). Это позволяет нам избавиться от многократного повторения одних и тех же данных (что в свою очередь позволяет нам экономить память), а также от проблемы обновления этих же данных (также как и Скорости их обновления), но в некоторых случаях мы теряем один аспект - координаты текстур - так как для каждой текстуры мы задаем их только одиножды, а в случае с кубом, если нам надо на каждую грань "натянуть" по текстуре, то нам просто необходимо будет задавать несколько вершин - для каждой грани свои. Итак, с небольшой теорией покончено, теперь возьмемся за практическое применение. С индексными буферами мы уже сталкивались в предыдущем уроке, так что я кое в чем немного повторюсь. Как обычно, сперва зададим необходимые переменные и интерфейсы: LPDIRECT3DVERTEXBUFFER8 p_VertexBuffer = NULL; // our VertexBuffer LPDIRECT3DINDEXBUFFER8 p_IndexBuffer = NULL; // our IndexBuffer сперва конечно интерфейсы на вершинный и индексный буферы. Далее... Я решил для данного и последующего примеров создать небольшую поверхность, состоящую из определенного числа узлов, составляющих регулярную сетку (в общем, поле, разбитое на квадратики :), для этого я задаю следующие данные: WORD *FlagIndices; CUSTOMVERTEX *FlagVertices; #define FLAG_SIZE 50 #define NUM_FLAG_VERTICES ((FLAG_SIZE+1)*(FLAG_SIZE+1)) #define NUM_FLAG_INDICES (FLAG_SIZE*FLAG_SIZE*6) --- два массива - один для заполнения его данными
о вершинах, другой для представления индексов. Собственно теперь мы можем создавать необходимые нам данные. Для начала создадим массив вершин и создадим по ним вертексный буфер:
FlagVertices = new CUSTOMVERTEX [NUM_FLAG_VERTICES];
for(ix=0; ix<FLAG_SIZE+1; ix++)
for(WORD iy=0; iy>FLAG_SIZE+1; iy++)
{
FLOAT tu = ix/(float)FLAG_SIZE;
FLOAT tv = iy/(float)FLAG_SIZE;
FlagVertices[ix+iy*(FLAG_SIZE+1)].x = (float)(ix - FLAG_SIZE/2);
FlagVertices[ix+iy*(FLAG_SIZE+1)].y =
3.0f*(cos((float)iy/3.0f)+cos((float)ix/3.0f));
FlagVertices[ix+iy*(FLAG_SIZE+1)].z = (float)(iy - FLAG_SIZE/2);
FlagVertices[ix+iy*(FLAG_SIZE+1)].nx = 0.0f;
FlagVertices[ix+iy*(FLAG_SIZE+1)].ny = 1.0f;
FlagVertices[ix+iy*(FLAG_SIZE+1)].nz = 0.0f;
FlagVertices[ix+iy*(FLAG_SIZE+1)].tu = (float)(tu*4);
FlagVertices[ix+iy*(FLAG_SIZE+1)].tv = (float)(tv*4);
};
p_d3d_Device->CreateVertexBuffer (NUM_FLAG_VERTICES*sizeof(CUSTOMVERTEX),
0, D3DFVF_CUSTOMVERTEX, D3DPOOL_MANAGED, &p_VertexBuffer);
VOID* pVertices;
p_VertexBuffer->Lock (0, NUM_FLAG_VERTICES*sizeof(CUSTOMVERTEX),
(BYTE**)&pVertices, 0);
memcpy (pVertices, FlagVertices, NUM_FLAG_VERTICES*sizeof(CUSTOMVERTEX));
p_VertexBuffer->Unlock();
Собственно, тут ничего нового нет, мы просто создаем массив необходимых данных, затем создаем вертексный буфер ( CreateVertexBuffer() ), и следующим шагом копируем наши данные в созданный буфер. Далее нам необходимо инициализировать данные для индексного буфера и загрузить их в него. Данный процесс почти ни чем не отличается от использованного ними подхода в отношении вертексного буфера, мы также создаем необходимые нам данные, только на это раз по индексам, а не по вершинам, создаем буфер (только на этот раз используем функцию CreateIndexBuffer() - предназначенную для создания именно индексного буфера), и напоследок копируем наши данные по индексам в индексный буфер. Выглядит это следующим образом:
FlagIndices = new WORD [NUM_FLAG_INDICES];
for(WORD i=0, ix=0; ix<LAG_SIZE; iy++ )
{
FlagIndices[i++] = (ix+0) + (iy+1)*(FLAG_SIZE+1);
FlagIndices[i++] = (ix+0) + (iy+0)*(FLAG_SIZE+1);
FlagIndices[i++] = (ix+1) + (iy+0)*(FLAG_SIZE+1);
FlagIndices[i++] = (ix+0) + (iy+1)*(FLAG_SIZE+1);
FlagIndices[i++] = (ix+1) + (iy+0)*(FLAG_SIZE+1);
FlagIndices[i++] = (ix+1) + (iy+1)*(FLAG_SIZE+1);
};
p_d3d_Device->CreateIndexBuffer (NUM_FLAG_INDICES*sizeof(WORD),
0, D3DFMT_INDEX16, D3DPOOL_MANAGED, &p_IndexBuffer);
VOID* pVerticesI;
p_IndexBuffer->Lock (0, NUM_FLAG_INDICES*sizeof(WORD), (BYTE**)&pVerticesI, 0);
memcpy (pVerticesI, FlagIndices, NUM_FLAG_INDICES*sizeof(WORD));
p_IndexBuffer->Unlock();
вот теперь, после того как мы все это
проинициализировали, мы можем заняться рендерингом. Вот выдержки из функции
рендеринга окна: p_d3d_Device->BeginScene (); p_d3d_Device->SetVertexShader (D3DFVF_CUSTOMVERTEX); p_d3d_Device->SetStreamSource (0, p_VertexBuffer, sizeof(CUSTOMVERTEX)); p_d3d_Device->SetIndices (p_IndexBuffer, 0); p_d3d_Device->SetMaterial (&mtrl0); p_d3d_Device->SetTexture (0, textura001); p_d3d_Device->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE); p_d3d_Device->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); p_d3d_Device->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_MODULATE); p_d3d_Device->DrawIndexedPrimitive (D3DPT_TRIANGLELIST, 0, NUM_FLAG_VERTICES, 0, NUM_FLAG_INDICES/3); p_d3d_Device->EndScene (); похожий кусок кода вы уже встречали в предыдущем уроке, и тут надеюсь все понятно. Теперь при запуске получившегося кода вы должны увидеть нечто похожее на это:
И не забудьте после использования все за собой убрать :) _DELETE_ (FlagIndices); _DELETE_ (FlagVertices); _RELEASE_ (p_VertexBuffer); _RELEASE_ (p_IndexBuffer); P.S. Для данного и некоторых последующих уроков, я вел в программу небольшую обработку ввода с клавиатуры (в оконной функции):
case WM_KEYDOWN:
{
if(wParam==VK_NEXT)
p_d3d_Device->SetRenderState (D3DRS_FILLMODE, D3DFILL_WIREFRAME);
if(wParam==VK_PRIOR)
p_d3d_Device->SetRenderState (D3DRS_FILLMODE, D3DFILL_SOLID);
if(wParam==VK_ADD) distance--;
if(wParam==VK_SUBTRACT) distance++;
....
--- нажав на PAGE_UP/PAGE_DOWN , мы меняем режим
отображения - полигонами/каркасной сеткой. за более полной информацией смотрите исходный код примера для данного урока. Рубрика: Direct3D
{BLOCK|article|lastarticle||non.tpl} |
Цитата дня (все,добавить):
|
Realcoding.NET
© 2003-2008 |
Контакты |
Реклама на сайте
|