Предлагаю алгоритм для исключения багов, связанных с недостатком
Z-буфера при рисовании полупрозрачных объектов.
Ниже пример алгоритма рисования дерева.
Основное условие - модель дерева должна быть в виде снежинки,
то есть полигоны (ветки) растут от центра.
Алгоритм за ради простоты не учитывает возможные повороты модели
вокруг осей X и Z (снова вылезут баги). Вокруг Y можно крутить сколь угодно.
Смысл алгоритма:
1. Определяется угол между камерой и объектом (деревом) как тангенс
отношения разностей координат X и Z.
2. Прямая вдоль этого угла принимается за ось Z.
3. Определяется реальный BoundBox каждой ветки.
4. Координаты веток транслируются к новой системе координат.
Виртуально! Реально ничего не перемещается!
5. Производится "поворот" веток на -угол.
6. Определяются ветки, расположенные слева и справа от оси Z и отсортировываются в два массива.
7. В каждом из массивов определяются ветки, которые нужно рисовать первыми, то есть те, у которых Z больший. Выполняется сортировка.
8. Формируется основной массив индексов по принципу - если угол <=0 (камера справа от дерева или на одной оси с ним), то сначала в массив записываются индексы веток слева. Если угол > 0 то индексы веток справа.
9. Собственно отрисовка.
- Код: Выделить всё
Dim MIdx() As Long ' основной индексный массив для веток
Dim RIdx() As Long ' массив для веток справа от центра
Dim LIdx() As Long ' массив для веток слева от центра
Dim RIc As Long ' всего индексов ветвей справа
Dim LIc As Long ' всего индексов ветвей слева
Dim MIc As Long ' всего индексов ветвей в дереве
Dim MeshCount ' всего ветвей в дереве
Dim VB() As D3DVERTEX ' вертексы ветвей
Dim VI() As D3DVECTOR ' вектора ветвей (мешей)
' процедура рисования
Private Sub Draw()
' сначала сортируем индексы
SortMeshes
' отрисовываем дерево
For i = 0& To MIc - 1
Mesh(MIdx(i)).DrawMesh
Next i
' обнуляем массивы
ReDim RIdx(0&)
ReDim LIdx(0&)
ReDim MIdx(0&)
RIc = 0&
LIc = 0&
MIc = 0&
End Sub
' процедура сортировки
Private Sub SortMeshes()
Dim tBBMin As D3DVECTOR, tBBMax As D3DVECTOR ' вектора для BoundBox-a
Dim i As Long, T As Single
Dim Ugol as Single ' угол между камерой и центром дерева
Dim MT As D3DMATRIX ' матрица перемещения
Dim MR As D3DMATRIX ' матрица вращения
Dim MN As D3DMATRIX ' результирующая матрица
' получить угол между камерой и центром дерева
'' не забудьте исключить деление на 0!
Ugol = Atn((Camera.X - Tree.X) / (Camera.Z - Tree.Z))
ReDim VI(0& To MeshCount - 1&)
For i = 0& To MeshCount - 1&
' получить BoundBox ветки
D3DX.ComputeBoundingBox VB(0&), VertexCount, D3DFVF_VERTEX, tBBMin, tBBMax
' получить координаты (вектора) концов ветвей
'' по оси Y не обрабатывается для упрощения!
'' будет неоднозначность когда (tBBMin < 0 And tBBMax > 0)
'' то есть, когда ветки "растут" не от центра дерева!
If tBBMin.X = 0 And tBBMax.X = 0 Then
VI(i).X = 0
End If
If tBBMin.X < 0 And Fix(tBBMax.X) = 0 Then
VI(i).X = tBBMin.X
End If
If tBBMax.X > 0 And Fix(tBBMin.X) = 0 Then
VI(i).X = tBBMax.X
End If
If tBBMin.Z = 0 And tBBMax.Z = 0 Then
VI(i).Z = 0
End If
If tBBMin.Z < 0 And Fix(tBBMax.Z) = 0 Then
VI(i).Z = tBBMin.Z
End If
If tBBMax.Z > 0 And Fix(tBBMin.Z) = 0 Then
VI(i).Z = tBBMax.Z
End If
' поменяем знак, если камера по оси Z находится дальше дерева
If Camera.Z > Tree.Z Then
VI(i).Z = -VI(i).Z
VI(i).X = -VI(i).X
End If
' оттранслировать вектора к началу координат
D3DXMatrixTranslation MT, -Tree.X, 0, -Tree.Z
' и повернуть на -угол
D3DXMatrixMultiply MN, MT, MR
D3DXMatrixRotationY MR, -Ugol
D3DXVec3TransformCoord VI(i), VI(i), MN
'----------- собственно сортировка
' ветки, лежащие на прямой "Камера - Центр Дерева"
' не учитываются и не отрисовываются.
' откидываем в левый массив индкексы веток, расположенных
' слева от этой прямой
If VI(i).X < 0 Or (VI(i).X = 0 And Ugol < 0) Then
ReDim Preserve LIdx(0& To LIc)
LIdx(LIc) = i
LIc = LIc + 1&
End If
' то же для веток справа от центра дерева
If VI(i).X > 0 Or (VI(i).X = 0 And Ugol > 0) Then
ReDim Preserve RIdx(0& To RIc)
RIdx(RIc) = i
RIc = RIc + 1&
End If
Next i
' сортируем ветки слева. Перекидываем индексы так, чтобы
' ветки, расположенные дальше от камеры рисовались первыми
i = 0&
Point1:
While i < LIc - 1
DoEvents ' исключим зацикливание от ошибки
If VI(LIdx(i + 1&)).Z > VI(LIdx(i)).Z Then
T = LIdx(i + 1&) ' меняем индексы местами
LIdx(i + 1&) = LIdx(i)
LIdx(i) = T
If i > 0 Then i = i - 1&
GoTo Point1
End If
i = i + 1&
Wend
' сортируем ветки справа
i = 0&
Point2:
While i < RIc - 1&
DoEvents
If VI(RIdx(i + 1)).Z > VI(RIdx(i)).Z Then
T = RIdx(i + 1)
RIdx(i + 1) = RIdx(i)
RIdx(i) = T
If i > 0 Then i = i - 1
GoTo Point2
End If
i = i + 1&
Wend
' формируем основной массив индексов
ReDim MIdx(0& To LIc + RIc - 1)
' если угол отрицательный или = 0, то первыми
' должны рисоваться ветки слева
If Ugol <= 0 Then
For i = 0 To LIc - 1
MIdx(MIc) = LIdx(i)
MIc = MIc + 1
Next i
For i = 0 To RIc - 1
MIdx(MIc) = RIdx(i)
MIc = MIc + 1
Next i
' если угол положительный, то первыми ветки справа
Else
For i = 0 To RIc - 1
MIdx(MIc) = RIdx(i)
MIc = MIc + 1
Next i
For i = 0 To LIc - 1
MIdx(MIc) = LIdx(i)
MIc = MIc + 1
Next i
End If
End Sub
Этот пост, экзешничек, и забавный скриншот найдёте тут, в архиве!
PS. Пример не до конца отлажен, может вываливаться. Так что звыняйте.
У вас нет доступа для просмотра вложений в этом сообщении.