24 января 2010 в 13:41
Уроки Direct3D - 1.8. Индексирование вертексов
Представьте, что у нас есть карта некоторой поверхности, представляющая из себя список высот точек на прямоугольном участке. Точки расположены равномерно через равные интервалы, при таком расположении нам достаточно указать только координату Y (высота) точки, а другие координаты легко вычисляются из порядкового номера точки. Такая карта высот называется регулярной сеткой.
Для простоты возьмем квадратную сетку размером 64 * 64. Часто для хранения таких карт применяют монохромные картинки, где яркость соответствующего пикселя интерпретируется как высота. В папке Pr08 будет наш новый проект, там можно взять соответствующую карту – файл HeightMap.tga.
Сетка такого размера будет состоять из 63 * 63 квадратных ячеек, каждую из которых можно изобразить двумя треугольниками. То есть если использовать обычный TRIANGLELIST, то нам понадобится 63 * 63 * 2 * 3 = 23814 вертексов. Можно разрезать карту на 63 полосы TRIANGLESTRIP, тогда число вертексов уменьшится до 64 * 63 * 2 = 8064. Неплохая экономия, но ведь реально на сетке 64 * 64 = 4096 точки, можно ли обойтись таким же количеством вертексов? Да, можно, для этого создают не один, а два буфера, первый – вертексный, содержащий только необходимые вертексы без повторов. Второй буфер будет содержать индексы, то есть порядковые номера вертексов в вертексном буфере. Индексы расположены так, что вместо дублирования вертекса, мы дублируем индекс – указатель на вертекс. Представим, что вертексы пронумерованы рядами слева направо, от ближних к дальним, нумерация идет от нуля. Для вывода с использованием TRIANGLELIST можно расположить индексы так:
Первый треугольник образован вертексами 0, 64, 65, то есть такими и будут первые три индекса – 0, 64, 65. Второй – 0, 65, 1 и так далее. Карта, описанная таким образом, будет содержать 4096 вертексов и 23814 индексов. Что же мы выиграли? Дело в том, что индекс – это, в отличие от вертекса, обычное 16-ти либо 32-х битное число, которое занимает значительно меньше памяти, чем вертекс, особенно при сложных форматах вертекса. Кроме того, при расчетах трансформаций процессор вынужден повторно производить вычисления для продублированных вертексов, так как считает их разными, а при индексировании один раз рассчитанный вертекс попадает в кэш, и при повторном обращении уже может не рассчитываться. Использование индексов дает и другие преимущества, которые мы рассмотрим позже.
Создадим новый проект с уже знакомой нам инициализацией d3dDevice и вертексным буфером с таким форматом:
Private Type vFormat Pos As D3DVECTOR Color As Long End Type
Задайте соответствующую константу флагового описания вертекса. Добавьте три новых переменных:
Dim numIndex As Long, numTri As Long, numVertex As Long
И одну переменную нового типа Direct3DIndexBuffer8:
Dim iBuf As Direct3DIndexBuffer8
Как и всякую переменную объектного типа добавьте iBuf в список для уничтожения. В соответствие с форматом вертекса функция Vertex приобретет такой вид:
Private Function Vertex(x As Single, y As Single, z As Single, c As Long) As vFormat Vertex.Pos = vec3(x, y, z) Vertex.Color = c End Function
Процедура InitGeometry будет состоять из двух частей, в первой инициализируется вертексный буфер. Интересующие нас данные расположены в TGA файле с 19-го байта, эта часть выглядит так:
Dim x As Long, z As Long, nf As Integer, b As Byte Dim Vert(64 * 64 - 1) As vFormat vSize = Len(Vert(0)) numVertex = 64 * 64 Set vBuf = d3dDevice.CreateVertexBuffer(numVertex * vSize, 0, vFlag, D3DPOOL_DEFAULT) nf = FreeFile Open "HeightMap.tga" For Binary As #nf For z = 0 To 63 For x = 0 To 63 Get #nf, x + z * 64 + 19, b Vert(x + z * 64) = Vertex(x - 31.5, b * 0.05, z - 31.5, &H808080) Next x Next z Close #nf
При вызове функции Vertex координаты x и z получены вычитанием 31.5 из соответствующих координат карты, это сделано с целью центровки карты относительно начала координат. Для получения координаты y данные о высоте, взятые из файла, умножаются на 0.05 – это вертикальный масштаб. Для всех вертексов задан серый цвет &H808080.
Новая для нас вторая часть процедуры InitGeometry:
Dim Ind(63 * 63 * 2 * 3 - 1) As Integer numTri = 63 * 63 * 2 numIndex = numTri * 3 Set iBuf = d3dDevice.CreateIndexBuffer(numIndex * 2, 0, D3DFMT_INDEX16, D3DPOOL_DEFAULT) For z = 0 To 62 For x = 0 To 62 Ind((z * 63 + x) * 2 * 3 + 0) = (z + 0) * 64 + x + 0 Ind((z * 63 + x) * 2 * 3 + 1) = (z + 1) * 64 + x + 0 Ind((z * 63 + x) * 2 * 3 + 2) = (z + 1) * 64 + x + 1 Ind((z * 63 + x) * 2 * 3 + 3) = (z + 0) * 64 + x + 0 Ind((z * 63 + x) * 2 * 3 + 4) = (z + 1) * 64 + x + 1 Ind((z * 63 + x) * 2 * 3 + 5) = (z + 0) * 64 + x + 1 Next x Next z D3DIndexBuffer8SetData iBuf, 0, numIndex * 2, 0, Ind(0)
Двойным циклом проходим по всем ячейкам карты и шестью индексами задаем два треугольника в соответствие с рисунком в начале главы.
В процедуре Render появится новая строка:
d3dDevice.SetIndices iBuf, 0&
Здесь мы указываем устройству рендера какой индексный буфер использовать и с какого индекса начинать выборку. И осталось изменить само рисование:
d3dDevice.DrawIndexedPrimitive D3DPT_TRIANGLELIST, 0&, numVertex, 0&, numTri
Запускаем программу – и видим смутно угадываемый фрагмент ландшафта. Дело в том, что вся поверхность равномерно серая, независимо от расстояния и наклона. Попробуем сымитировать эффект освещения раскраской вертексов в зависимости от наклона поверхности, но для этого сначала немного теории.
комментарии отсутствуют
авторизуйтесь