DirectX & Visual Basic

Учебник DirectX Graphics: часть 4

<<Предыдущая часть

Использование источников света

В этой части мы создадим вращающийся трёхмерный цилиндр, а также будем использовать материалы и источники света, чтобы добавить сцене реалистичности.

Код программы, которую мы будем создавать в этой части основан на коде программы, которую мы создали в предыдущей. Здесь описаны произошедшие изменения, которые главным образом касаются замены треугольника циллиндром, а также добавления в сцену источника света.

Для начала изменим тип 3d вершин. Для этого заменим объявление типа CUSTOMVERTEX и константы D3DFVF_CUSTOMVERTEX, описывающей этот тип, следующими:

Private Type CUSTOMVERTEX
postion As D3DVECTOR    '3d координаты вершины
normal As D3DVECTOR     'нормаль вершины
End Type

Const D3DFVF_CUSTOMVERTEX = (D3DFVF_XYZ Or D3DFVF_NORMAL)

В переводе на русский нормаль - это перпендикуляр. Если Вы используете трёхмерное освещение, то каждая поверхность должна иметь свою нормаль, которя нужна Direct3d для выполнения вычислений.

Следующим нашим шагом будет создание циллиндра. В процедуре Form_Load вместо кода, вызывающего функцию InitVB, вставим код, вызывающий функцию InitGeometry.

If Not InitGeometry() Then
   MsgBox "Невозможно инициализировать буфер вершин."
   End
End If

Код функции InitVB удалим, а вместо него вставим приведённый ниже код функции InitGeometry.

Function InitGeometry() As Boolean
On Local Error Resume Next

Dim i As Long

Dim Vertices(99) As CUSTOMVERTEX 'массив 3d вершин, составляющих циллиндр
Dim VertexSizeInBytes As Long
Dim theta As Single
    
VertexSizeInBytes = Len(Vertices(0))

For i = 0 To 49
    theta = (2 * g_pi * i) / (50 - 1)
    Vertices(2 * i + 0).postion = vec3(Sin(theta), -1, Cos(theta))
    Vertices(2 * i + 0).normal = vec3(Sin(theta), 0, Cos(theta))
    Vertices(2 * i + 1).postion = vec3(Sin(theta), 1, Cos(theta))
    Vertices(2 * i + 1).normal = vec3(Sin(theta), 0, Cos(theta))
Next

' Создание буффера вершин
Set g_VB = g_D3DDevice.CreateVertexBuffer(VertexSizeInBytes * 50 * 2, _
                     0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT)
If g_VB Is Nothing Then Exit Function

' Заполнение буффера вершинами из массива
D3DVertexBuffer8SetData g_VB, 0, VertexSizeInBytes * 100, 0, Vertices(0)

InitGeometry = True
End Function

Как известно, Direct3d может рендерить только треугольники. Все остальные объекты составляются из них. Функция InitGeometry  создаёт не циллиндр, а скорее "гранёный стакан" без дна, каждая грань которого состоит из двух треугольников. Из-за того, что у "стакана" 50 граней, он выглядит похожим на циллиндр. Функцией InitGeometry создаётся буффер трёхмерных вершин, составляющих циллиндр, а трегольники строятся на этих вершинах уже при рендеринге.

Следующим нашим шагом будет настройка материалов и источников света. Настройка эта будет производится процедурой SetupLights, код которой приведён ниже.

Sub SetupLights()
Dim col As D3DCOLORVALUE 'структура для хранения цветов (ARGB)

Dim mtrl As D3DMATERIAL8 'материал
With col:    .r = 1: .g = 1: .b = 0: .a = 1:   End With
mtrl.diffuse = col
mtrl.Ambient = col
g_D3DDevice.SetMaterial mtrl

Dim light As D3DLIGHT8 'источник света
light.Type = D3DLIGHT_DIRECTIONAL
light.diffuse.r = 1
light.diffuse.g = 1
light.diffuse.b = 1
light.Direction.x = Cos(Timer * 2)
light.Direction.y = 1
light.Direction.z = Sin(Timer * 2)
light.Range = 1000

g_D3DDevice.SetLight 0, light   'Даёт знать Direct3d об источнике света
g_D3DDevice.LightEnable 0, 1
g_D3DDevice.SetRenderState D3DRS_AMBIENT, &H202020 'задаёт цвет окружающего света
g_D3DDevice.SetRenderState D3DRS_LIGHTING, 1    'Включает трёхмерное освещение

End Sub

Сначала в процедуре SetupLights  создаётся материал mtrl жёлтого цвета. Материал определяет, как объект будет отражать свет различного типа. Метод SetMaterial объекта g_D3DDevice применяет материал, заданный единственным параметром метода, ко всем рисуемым примитивам. Для рисования примитивов, использующих другой материал, необходимо повторно вызвать метод SetMaterial.

Затем происходит создание и настройка параметров источника света. В этом примере мы будем использовать окружающий свет, освещающий в равной мере все объекты, а также один направленный источник света. В Direct3d есть 3 типа источников света: точечные (имеют координаты, излучают свет одинаково во всех направлениях), направленные (имеют направление, не имеют координат), а также источники света, имеющие и координаты, и направление (освещают только те предметы, которые находятся внутри воображаемого конуса). Напраленные источники света одинаково освещают все предметы сцены только с одной стороны и используются для моделирования света от тел, находящихся на большом расстоянии, например, солнечного света. Член Type типа D3DLIGHT8 задаёт тип источника света (D3DLIGHT_DIRECTIONAL значит направленный); diffuse задаёт его цвет; Direction - направление (в данном примере источник света вращается в плоскости XOZ); а Range - радиус действия (при использовании направленных источников света он не имеет никакого значения). После того, как источник света создан, с помощью метода SetLight мы применяем его к устройству, назначая индекс 0. Затем, с помощью метода LightEnable мы "включаем" источник света с индексом 0.

Не забудьте добавить код, вызывающий процедуру SetupLights в процедуру Render после метода BeginScene.

В функции InitD3d сделайте следующие изменения:

1. Удалите строку:

g_D3DDevice.SetRenderState D3DRS_LIGHTING, 0

Эта строка выключала трёхмерное освещение, теперь же, когда мы используем его, она не нужна.

2. В конец функции, после строки, выключающей culling, вставьте следующий код:

g_D3DDevice.SetRenderState D3DRS_ZENABLE, 1

Этот код включает испольхование Z-буффера. Z-буффер - это поверхность, в которой вместо информации о цветах пикселей хранится информация о расстоянии до каждого пиксела в сцене. Пиксел, расстояние до которого меньше, перекрывает пиксел, расстояние до которого больше. Этим обеспечивается правильное отображение перекрывающихся геометрических тел.

3. Вставьте код, задающий ещё несколько параметров структуры d3dpp, необходимой при создании устройства рендеринга.

d3dpp.BackBufferCount = 1
d3dpp.EnableAutoDepthStencil = 1
d3dpp.AutoDepthStencilFormat = D3DFMT_D16

Код этот нужно вставить после строк, устанавливающих другие параметры структуры d3dpp.

В процедуре Render сделаем следующие изменения:

1. Заменим код, который раньше выполнял очистку поверхности, на которую происходит рендеринг, следующим:

g_D3DDevice.Clear 0, ByVal 0, D3DCLEAR_TARGET Or D3DCLEAR_ZBUFFER, &HFF&, 1, 0

Теперь метод Clear будет очищать ещё и Z-буффер.

2. Заменим код, рисующий примтивы на приведённый ниже:

g_D3DDevice.DrawPrimitive D3DPT_TRIANGLESTRIP, 0, (4 * 25) - 2

В процедуре SetupMatrices заменим код, вращавший все объекты сцены вокруг оси OX, следующим:

D3DXMatrixRotationAxis matWorld, vec3(1, 1, 1), Timer / 4

Процедура D3DXMatrixRotationAxis будет вращать цилиндр вокруг вектора (1;1;1).

Всё! Запускайте проект на выполнение и любуйтесь вращающимся трёхмерным циллиндром.

Вы можете скачать архив RAR с готовым проектом (3 Кб).

В следующей части мы будем создавать текстуры и "наклеивать" их на примитивы.

Следующая часть>>

Hosted by uCoz