DirectX & Visual Basic

Создание прозрачных объектов и зеркал

Alpha blending

Помимо красной, зелёной и синей составляющих каждый пиксель имеет "прозрачную" составляющую - альфа канал (alpha channel). Альфа канал, как правило, имеет столько же бит, сколько и каждый из трёх цветов. Если альфа канал восьмибитный, то 0 означает, что пиксель полностью прозрачен, а 255 - что пиксель полностью не прозрачен, если же альфа канал принимает любое другое значение, то пиксель прозрачен лишь в некоторой степени. Alpha blending используется для отображения прозрачных и полупрозрачных пикселей. Включается alpha blending следующим образом:

' g_D3DDevice - ссылка на проинициализированный объект  Direct3DDevice8     
g_D3DDevice.SetRenderState D3DRS_ALPHABLENDENABLE, True

Прозрачность может содержаться в вершинах, материалах или текстурах. Рассмотрим подробнее прозрачность в материалах. За основу программы возьмём проект, созданный нами в четвёртой части учебника DirectX Graphics (с вращающимся в пространстве жёлтым цилиндром). В конец функции InitD3d помимо показанной выше строки необходимо добавить этот код:

g_D3DDevice.SetRenderState D3DRS_SRCBLEND, D3DBLEND_INVSRCALPHA
g_D3DDevice.SetRenderState D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA

Параметры альфа блендинга задаются методом SetRenderState, у которого первый параметр - D3DRS_SRCBLEND или D3DRS_DESTBLEND, а второй параметр - константа из набора CONST_D3DBLEND. Дело в том, что при альфа блендинге мы смешиваем цвета двух пикселей: исходного и существуюшего. Исходный пиксель берётся из нашего прозрачного объекта, а существующий - из буфера кадра (frame buffer), он получается в результате рендеринга объектов сцены, расположенных за прозрачным объектом. Вклад исходного пикселя в цвет итогового определяется с помощью D3DRS_SRCBLEND, а вклад существующего - с помощью D3DRS_DESTBLEND. Вызывая метод SetRenderState, у котрого первый параметр - одна из этих двух констант и комбинируя значения второго параметра этого метода можно получить различные интересные эффекты.

Последнее, что остаётся сделать - это создать материал, обладающий прозрачностью. Сделать это можно, на пример, так:

Dim col As D3DCOLORVALUE
Dim mtrl As D3DMATERIAL8
With col: .r = 1: .g = 1: .b = 1: .a = 0.2: End With
mtrl.diffuse = col
mtrl.Ambient = col
g_D3DDevice.SetMaterial mtrl

Выполнив описанные действия и запустив Вашу программу на выполнение Вы увидите нечто подобное этому:

Кроме того, Вы можете скачать архив RAR с готовым VB-проектом, в котором используется alpha blending.

Создание плоского зеркала

Для того, чтобы нарисовать отражение объекта надо развернуть камеру относительно зеркала и повторно нарисовать объект. Здесь, однако, возникают некоторые сложности, связанные с обрезкой получившегося изображения (она необходима, так как зеркало конечно). Есть несколько способов решения данной проблемы, но мы рассмотрим метод, использующий плоскости отсечения (clipping planes). Суть этого метода в том, что надо создать столько плоскостей отсечения, сколько у зеркала граней. Кроме того, надо создать ещё одну плоскость отсечения, необходимую для того, чтобы в зеркале отражалось только то, что расположено перед ним (если этого не сделать в зеркале будут "отражаться" предметы, расположенные с другой стороны).

За основу на этот раз возьмём проект, созданный нами в шестой части учебника DirectX Graphics. Далее будут описаны лишь ключевые изменения, сделанные в исходной программе.

Само зеркало представляет собой квадрат, создаваемый следующим кодом:

'CUSTOMVERTEX - объявленный пользовательский тип 3d вершин
'D3DFVF_CUSTOMVERTEX - константа, описывающая этот тип

Dim Vertices(3) As CUSTOMVERTEX
Vertices(0).position = vec3(-2, -2, 0)
Vertices(1).position = vec3(-2, 2, 0)
Vertices(2).position = vec3(2, 2, 0)
Vertices(3).position = vec3(2, -2, 0)
Vertices(0).normal = vec3(0, 0, 1)
Vertices(1).normal = vec3(0, 0, 1)
Vertices(2).normal = vec3(0, 0, 1)
Vertices(3).normal = vec3(0, 0, 1)

Set g_VB = g_D3DDevice.CreateVertexBuffer(Len(Vertices(0)) * 4, 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT)
If g_VB Is Nothing Then Exit Function

D3DVertexBuffer8SetData g_VB, 0, Len(Vertices(0)) * 4, 0, Vertices(0)

При рендеринге между методами BeginScene и EndScene сначала вызывается процедура RenderScene, рисующая тигра, а затем процедура RenderMirror, рисующая его отражение и само зеркало.

Ниже приведён код процедуры RenderMirror:

Public Sub RenderMirror()
Dim matWorldSaved As D3DMATRIX 'Сохраняемая матрица мира
Dim matReflectInMirror As D3DMATRIX
Dim Plane As D3DPLANE 'плоскость
'a, b, c, d - координаты вершин зеркала
'здесь они задаются статистически, но,
'в большинстве случаев они задаются динамически
Dim a As D3DVECTOR
Dim b As D3DVECTOR
Dim c As D3DVECTOR
Dim d As D3DVECTOR
a = vec3(-2, -2, 0)
b = vec3(-2, 2, 0)
c = vec3(2, 2, 0)
d = vec3(2, -2, 0)

g_D3DDevice.GetTransform D3DTS_WORLD, matWorldSaved

'создаём плоскость по трём точкам
D3DXPlaneFromPoints Plane, a, b, c
'создаём матрицу, "отражённую" от плоскости зеркала
D3DXMatrixReflect matReflectInMirror, Plane
g_D3DDevice.SetTransform D3DTS_WORLD, matReflectInMirror
g_D3DDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_CW

'Это - самое важное место программы
'здесь создаются плоскости отсечения,
'которые отрезают изображение, оказавшееся
'за пределами зеркала
Call D3DXPlaneFromPoints(Plane, c, b, g_EyePoint): g_D3DDevice.SetClipPlane 0, Plane
Call D3DXPlaneFromPoints(Plane, d, c, g_EyePoint): g_D3DDevice.SetClipPlane 1, Plane
Call D3DXPlaneFromPoints(Plane, a, d, g_EyePoint): g_D3DDevice.SetClipPlane 2, Plane
Call D3DXPlaneFromPoints(Plane, b, a, g_EyePoint): g_D3DDevice.SetClipPlane 3, Plane
'Благодаря этой плоскости в зеркале
'отражается только то, что находится
'перед ним
Call D3DXPlaneFromPoints(Plane, c, b, a): g_D3DDevice.SetClipPlane 4, Plane
g_D3DDevice.SetRenderState D3DRS_CLIPPLANEENABLE, D3DCLIPPLANE0 Or D3DCLIPPLANE1 _
                              Or D3DCLIPPLANE2 Or D3DCLIPPLANE3 Or D3DCLIPPLANE4
                                               
'Здесь мы повторно рендерим сцену
'из нового положения
RenderScene

'В конце мы возвращаем предыдущие
'параметры рендеринга
g_D3DDevice.SetTransform D3DTS_WORLD, matWorldSaved
g_D3DDevice.SetRenderState D3DRS_CLIPPLANEENABLE, 0
g_D3DDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_CCW

'Прорисовка самого зеркала
Dim col As D3DCOLORVALUE 'структура для хранения цветов (ARGB)
Dim mtrl As D3DMATERIAL8 'материал
With col:    .r = 0.5: .g = 0.5: .b = 0.5: .a = 0.5: End With
mtrl.diffuse = col
mtrl.Ambient = col
g_D3DDevice.SetMaterial mtrl
g_D3DDevice.SetTexture 0, Nothing
Dim v As CUSTOMVERTEX
g_D3DDevice.SetStreamSource 0, g_VB, Len(v)
g_D3DDevice.SetVertexShader D3DFVF_CUSTOMVERTEX
g_D3DDevice.DrawPrimitive D3DPT_TRIANGLEFAN, 0, 2

End Sub

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

Для более детальной информации Вы можете скачать RAR-архив с работающим VB-проектом и необходимыми файлами.

После запуска на выполнение получившейся программы, Вы должны увидеть что-то, напоминающее это:

Hosted by uCoz