Урок 5. Процедурная генерация моделей для DirectX11
В этом уроке вы узнаете: для чего нужна процедурная генерация; что нужно для того, чтобы правильно определить исходные данные для вершинного и индексного буфера; каким образом правильно задать вершинный и индексный буфер с произвольным количеством вершин; разберем процедурно генерируемый объект: сетку вершин.
Процедурная генерация
В дальнейшем, в восьмом уроке, мы разберем, как загружать внешнюю модель. Вообщем, необходимые навыки для этого вы уже имеете, так как знаете как создавать вершинный и индексный буфер, а 3d модель, сохраненная в виде файла как раз и представляет из себя дамп вершинных и индексных буферов редактируемой 3d модели.
Однако есть немало примеров того, когда использование внешних моделей для каких-то конкретных целей может только замедлить процесс разработки игры. Когда использование внешнего 3d редактора оказывается неэффективным, используется процедурная генерация. В число таких задач входит генерация ландшафта. На самом деле, разрабатывать ландшафт в 3d редакторе – очень утомительно. Начать хотя бы с того, что ландшафт очень большой, так что подготовить правильный каркас 3d ландшафта сложно.
Проще всего ландшафт сгенерировать на ходу. Для этого нужно иметь определенную карту высоты – то есть, картинку. Светлые места будут обозначать горы и холмы а более темные – более низкие участки ландшафта. Надо отметить, что кроме генерации ландшафта существует немало и других примеров процедурной генерации определенных объектов. Такая методика позволяет оживить саму игру и процесс разработки, внося в игру определенную динамику – вместоодин раз и навсегда определенных внешних мешей мы можем получить динамически меняющиеся 3d модели и поверхности. Нам же процедурная генерация понадобится в качестве закрепления навыков работы с вершинными и индексными буферами, так как это пожалуй, один из ключевых аспектов работы с DirectX.
Определение формата исходных данных
Плоскость для нашего ландшафта мы определим как сетку из точек. Количество вершин в сетке будет определять разрешение нашего ландшафта. Чем больше вершин в ландшафте, тем детальнее он будет. Однако не стоит увлекаться излишней детализацией, так как при большом количестве обрабатываемых вершин вершинный шейдер окажется перегружен. Так, мы имеем для сетки ландшафта размером 512×512 вершин 524000 отображаемых полигонов в сцене, что чрезмерно много, нас интересует количество около 40000 полигонов, с учетом моделей сцены.
Для нашего текущего примера мы создадим совсем небольшой тестовый ландшафт. Исходными данными для ландшафта будет количество вершин по горизонтали u и по вертикали v а также размер ландшафта size. Зададим размер ландшафта:
const u=8; const v=8; |
Определим формат вершинного буфера для ландшафта. Обратите внимание, что в вершинный буфер добавлена новая переменная normal. Эта переменная потребуется для правильного освещения ландшафта. О том, как работать с освещением и устанавливать его мы поговорим уже в следующем уроке. Итак, определим формат вершинного буфера для сетки ландшафта:
struct VERTEX{ XMFLOAT3 pos; XMFLOAT4 color; XMFLOAT3 normal; } vertices[u*v]; |
Теперь определим формат индексного буфера:
DWORD indices[IndicesCount]; |
Теперь посмотрим какой код потребуется для генерации сетки и заполнения вершинного буфера:
for (int i=0; i<u; i++) for (int j=0; j<v; j++) { float x=(float)i/(float)u-0.5f; float y=(float)j/(float)v-0.5f; vertices[j*u+i].pos = XMFLOAT3(x,hmap[i][j],y)*7.5f; vertices[j*u+i].color= XMFLOAT4(1,1,1,1); vertices[j*u+i].normal=XMFLOAT3(0,1,0); } |
Сетка сгенерирована правильно. Однако обратите внимание, что хотя координаты x иz просчитаны правильно, высота элемента ландшафта взята из пока еще неопределенного массива hmap[][]. В этом массиве будет храниться карта высот для нашего ландшафта. Для индексного буфера мы должны сгенерировать номера вершин в следующем виде:
indexes[] = { 0,1,9, 0,9,8 1,2,10, 1,10,9 2,3,11, 2,11,10 .... } |
Таким образом, существует определенная несложная формула длянахождение каждой пары тройки чисел, определяющих индексы для использования в индексном буфере ландшафта.
for (int i=0; i<(u-1); i++) for (int j=0; j<(v-1); j++) { unsigned int indexa=j*(u-1)+i; unsigned int indexb=j*u+i; indices[indexa*6+0]=indexb; indices[indexa*6+1]=indexb+1+u; indices[indexa*6+2]=indexb+1; indices[indexa*6+3]=indexb; indices[indexa*6+4]=indexb+u; indices[indexa*6+5]=indexb+u+1; } |
Обзор приложения
В данном приложении мы рассмотрели генерацию меша из произвольного набора вершин и индексов. Исходный код текст данного приложения а также само приложение вы можете скачать тут. Сетка из вершин, генерируемая данным приложением иллюстрирует возможность генерации ландшафта используя имеющуюся карту высот.
Заключение
В этом уроке вы узнали, для чего нужна процедурная генерация; что нужно для того, чтобы правильно определить исходные данные для вершинного и индексного буфера; каким образом правильно задать вершинный и индексный буфер из своего набора вершин; разобрали пример приложения, генерирующего сетку вершин. В следующем уроке вы познакомитесь с очень увлекательной темой создания освещения в Direct3D11.
Заполнен: DirectX11
