Индексные буферы (Index buffers) в Direct3D
Дата создания: 2009-11-20 18:11:44
Последний раз редактировалось: 2012-02-08 11:07:28
Сегодня у нас небольшая и простая тема: индексные буферы.
В прошлом уроке было упражнение, в котором вы должны были построить куб. Особо наблюдательные могли заметить, что для этого нужно было запихать в вершинный буфер координаты 36 вершин. Но, как вы возможно знаете, в кубе всего 8 вершин. Используя вершинный буфер пришлось определять 36. Представьте, сколько вершин нужно определять для моделей немногим сложнее куба!!?? Это ж рехнуться можно!
Для решения данной проблемы используется два буфера: вершинный и индексный. В вершинном буфере хранится непосредственно информация о вершинах: координаты, цвет и много чего другого, о чём нам ещё рано рассуждать. В индексном же буффере хранится, как бы это сказать, порядок (номера/индексы) вершин в треугольниках. Для создания индексного буфера предназначен метод IDirect3DDevice9::CreateIndexBuffer:
HRESULT CreateIndexBuffer( UINT Length, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DIndexBuffer9** ppIndexBuffer, HANDLE* pSharedHandle );
Length
:
Длина буфера в байтах.
Usage
:
Используется также как и в вершинном буфере.
Format
:
Формат индексов, или по другому - размер каждого индекса. Для хранения индексов мы будем использовать тип unsigned short (16 бит), поэтому аргумент будет принимать значение - D3DFMT_INDEX16.
Pool
Используется также, как и в вершинном буфере.
ppIndexBuffer
Адрес указателя на индексный буфер.
pSharedHandle
Параметр используется только в Windows Vista. Всегда будем передавать NULL.
IDirect3DIndexBuffer9
Для индексных буферов по аналогии с IDirect3DVertexBuffer9 используется свой интерфейс - IDirect3DIndexBuffer9. Пока нас интересует только два метода (из трёх) этого интейрфейса:
HRESULT Lock( UINT OffsetToLock, UINT SizeToLock, VOID ** ppbData, DWORD Flags );
Как видите, данный метод идентичен соотвествующему из интерфейса IDirect3DVertexBuffer9. Поэтому остановимся только на третьем аргументе ppbData. В данный аргумент передаётся массив в котором хранится информация, а конкретно - индексы вершин из вершинного буфера.
Второй метод предназначен для размыканния буфера - Unlock, аргументов у него нет.
Ну, а теперь собственно сам код. Весь код связанный с индексным буфером я поместил рядом с вершинным, надеюсь, это поможет вам лучше разобраться.
Экземпляр интерфейса обзовём ib:
IDirect3DVertexBuffer9* vb = NULL; IDirect3DIndexBuffer9* ib = NULL;
Создаём буферы:
dev->CreateVertexBuffer( 4* sizeof(vertex), D3DUSAGE_WRITEONLY, D3DFVF_XYZ|D3DFVF_DIFFUSE, D3DPOOL_DEFAULT, &vb,NULL); dev->CreateIndexBuffer( 6*sizeof(unsigned short), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &ib, NULL );
Далее, создаём данные для буферов: в первом - информация о вершинах, во втором - индексы этих вершин в треугольниках модели. Определение структуры vertex с прошлого выпуска не изменилось:
vertex vertices[] = { { 0, 0, 0, 0xff000000}, { 0, 1, 0, 0xff000000}, { 1, 0, 0, 0xff000000}, { 1, 1, 0, 0xff000000}, }; unsigned short indexes[] = { 0, 1, 2, 1, 3, 2 };
Здесь определены четыре вершины в массиве vertices.
В массиве indexes хранятся индексы вершин из массива vertices, которые образуют треугольники. Первые три цифры - первый треугольик, следующие три цифры - второй и т.д.
В индексном буфере указываются номера элементов из вершинного буфера: 0, 1, 2, 1, 3, 2. Т.е. первый треугольник будет построен из элементов vertices[0], vertices[1], vertices[2]. Второй треугольник будет построен из вершин vertices[1], vertices[3], vertices[2].
Объявляем вспомогательные переменные:
void* vb_vertices; void* ib_indexes;
Заполняем буферы:
vb->Lock(0,sizeof(vertices),(void**)&vb_vertices,0); memcpy(vb_vertices,vertices,sizeof(vertices)); vb->Unlock(); ib->Lock(0,sizeof(indexes),(void**)&ib_indexes,0); memcpy(ib_indexes,indexes,sizeof(indexes)); ib->Unlock();
Насколько мы помним, дальше нужно указать гибкий формат вершин (FVF) и привязать вершинный буфер к потоку данных. Это выполняется двумя методами устройства: SetFVF и SetStreamSource. В этот раз нам понадобится ещё один метод - SetIndices, который говорит устройству (видеокарте) адрес индексного буфера. Единственный параметр данного метода - указатель на индексный буфер. Теперь, все три метода вместе:
dev->SetStreamSource(0,vb,0,sizeof(vertex)); dev->SetFVF(D3DFVF_XYZ|D3DFVF_DIFFUSE); dev->SetIndices( ib );
Вот в общем-то и всё, инициализация закончилась, можно переходить к выводу графики.
В прошлый раз мы использовали метод IDirect3DDevice9::DrawPrimitive. В случае использования индексный буферов, применяется другой метод:
HRESULT DrawIndexedPrimitive( D3DPRIMITIVETYPE Type, INT BaseVertexIndex, UINT MinIndex, UINT NumVertices, UINT StartIndex, UINT PrimitiveCount );
Type
:
Передаём D3DPT_TRIANGLELIST. В одном из следующих уроков мы познакомимся и с другими типами примитивов.
BaseVertexIndex
:
Смещение в вершинном буфере. Значение аргумента говорит, с какой вершины начнётся вывод.
MinIndex
:
Минимальный индекс вершины.
NumVertices
:
Количество вершин используемых для вывода.
StartIndex
:
Первый индекс.
PrimitiveCount
:
Количество примитивов.
Пока что на параметры BaseVertexIndex, MinIndex и StartIndex не обращайте внимания. Мы ещё разберёмся с ними.
Следующей строчкой нужно заменить вызов DrawPrimitive в основном цикле:
DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,4,0,2);
Четвёртый параметр - количество вершин применяемых для вывода графики, последний параметр - количество примитивов.
На сегодня всё.
Упражнения
1. Создайте куб используя индексные буферы. Программу из прошлого выпуска можно скачать из раздела Листинги.