Пора, наконец, нарисовать что-то существенное. В Direct3D, за редким исключением, вывод графики основан на выводе примитивов – серий треугольников. Треугольники выбраны потому, что на них можно разбить любой многоугольник или, как его еще называют, полигон.

Для однозначного задания положения треугольника в пространстве достаточно задать координаты трех его вершин. В Direct3D существует понятие «вертекс» – особая структура, содержащая координаты и некоторые другие характеристики точки (например, вершины треугольника) в пространстве.

Определение
Вертекс – особая структура, содержащая координаты и некоторые другие характеристики точки в пространстве.

Возьмем за основу наш предыдущий проект. Добавим такую структуру:

код на языке vb
 
Private Type vFormat
  PosX As Single
  PosY As Single
  PosZ As Single
  RHW As Single
  Color As Long
End Type
 

Эта структура задает формат вертекса, то есть перечень характеристик точки, описанных в вертексе. Поля PosX, PosY и PosZ соответствуют трем координатам точки в 3D пространстве. Поле RHW задает особую характеристику, значение которой мы рассмотрим чуть позже, и поле Color задает цвет точки. Кроме структуры вертекса мы должны также задать способ его обработки, то есть мы указываем Direct3D, каким образом нужно обрабатывать вертекс. Для этого служат специальные константы, заданные в Enum CONST_D3DFVFFLAGS, их имена начинаются с «D3DFVF_»:

код на языке vb
 
Private Const vFlag = D3DFVF_XYZRHW Or D3DFVF_DIFFUSE
 

Такая комбинация констант «поясняет» Direct3D структуру нашего вертекса.
Добавим новые общие переменные:

код на языке vb
 
Dim vBuffer As Direct3DVertexBuffer8
Dim Vert(0 To 2) As vFormat
Dim vSize As Long
 

Массив Vert() для трех вертексов, переменная vSize содержащая размер вертекса в байтах и vBuffer, переменная нового для нас типа Direct3DVertexBuffer8. Напишем специальную процедуру для задания значений вертексов:

код на языке vb
 
Private Sub InitGeometry()
  vSize = Len(Vert(0))
  Set vBuffer = d3dDevice.CreateVertexBuffer(3 * vSize, 0, vFlag, D3DPOOL_DEFAULT)
 
  Vert(0).PosX = 10
  Vert(0).PosY = 10
  Vert(0).PosZ = 0
  Vert(0).RHW = 1
  Vert(0).Color = &HFF
 
  Vert(1).PosX = 210
  Vert(1).PosY = 10
  Vert(1).PosZ = 0
  Vert(1).RHW = 1
  Vert(1).Color = &HFF00&
 
  Vert(2).PosX = 10
  Vert(2).PosY = 210
  Vert(2).PosZ = 0
  Vert(2).RHW = 1
  Vert(2).Color = &HFF0000
 
  D3DVertexBuffer8SetData vBuffer, 0, vSize * 3, 0, Vert(0)
End Sub
 

Используя d3dDevice.CreateVertexBuffer инициализируем объектную переменную vBuffer, не забываем сразу добавить в процедуру ClearAll строку для ее уничтожения:

код на языке vb
 
  Set vBuffer = Nothing
 

Функция D3DVertexBuffer8SetData переносит данные в вертексный буфер, откуда и будет происходить вывод вертексов при растеризации.
И, наконец, внесем изменения в процедуру Render:

код на языке vb
 
Private Sub Render()
  d3dDevice.Clear 0, ByVal 0, D3DCLEAR_TARGET, &H346666, 1, 0
  d3dDevice.BeginScene
 
  d3dDevice.SetStreamSource 0, vBuffer, vSize
  d3dDevice.SetVertexShader vFlag
  d3dDevice.DrawPrimitive D3DPT_TRIANGLELIST, 0, 1
 
  d3dDevice.EndScene
  d3dDevice.Present ByVal 0, ByVal 0, 0, ByVal 0
End Sub
 

Весь вывод графики в Direct3D должен начинаться с d3dDevice.BeginScene и заканчиваться d3dDevice.EndScene. С помощью d3dDevice.SetStreamSource 0, vBuffer, vSize указываем нашему устройству рендера (d3dDevice) на вертексный буфер, а d3dDevice.SetVertexShader vFlag конкретизирует формат его содержимого.

И само рисование - d3dDevice.DrawPrimitive D3DPT_TRIANGLELIST, 0, 1. Это указание вывести список треугольников (TRIANGLELIST), начинающийся с адреса 0 и содержащий 1 треугольник.

Жмем <F5> – и видим градиентно раскрашенный треугольник.

Для пояснения смысла поля RHW в структуре vFormat проведем эксперимент. В процедуре InitGeometry заменим строку Vert(2).RHW = 1 на Vert(2).RHW = 3. В результате цвет от красной вершины (№ 2) как бы расползается более сильно, чем от остальных вершин. Если немного напрячь воображение, можно представить, что красная вершина находится к нам ближе. Использование поля RHW в формате вертекса означает использование «приведенного» формата, в такой формат Direct3D неявно преобразует геометрию из других форматов перед выводом.

Проведем еще один эксперимент. Добавим процедуру обработки события MouseMove для формы:

код на языке vb
 
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
  If Button = 1 Then
    Vert(0).PosX = X
    Vert(0).PosY = Y
    D3DVertexBuffer8SetData vBuffer, 0, vSize, 0, Vert(0)
  End If
End Sub
 

Теперь мы можем перетаскивать синюю вершину мышкой. Заметьте, что при перемещении вершины за противоположную сторону треугольника сам треугольник пропадает. Дело в том, что Direct3D отображает по умолчанию только лицевую сторону треугольника. При использовании TRIANGLELIST лицевой считается та сторона, на которой вершины расположены по часовой стрелке. Это сделано с целью повышения быстродействия при выводе объемных объектов. Они, как правило, представлены своей поверхностью, и их внутренняя сторона не бывает видна ни при каких обстоятельствах. Если нам необходимо отобразить двухстороннюю фигуру, эту оптимизацию необходимо запретить. Добавим в D3DInit такую строку:

код на языке vb
 
d3dDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_NONE
 

Теперь наш треугольник виден с двух сторон.