Четверг, 18 Апреля 2024, 04:31

Приветствую Вас Гость

Меню сайта
Категории каталога
Создание игр [355]
Статьи об общих понятиях связанных с созданием игр.
Программирование [82]
Гайды по программированию на разных ЯП.
Движки и Гейммейкеры [145]
Статьи о программах для создания игр, уроки и описания.
Софт [43]
Различные программы, в том числе в помощь игроделам.
2D-графика [14]
Уроки по рисованию, растр, пиксель-арт, создание спрайтов и пр.
3D-графика [16]
Уроки по моделированию, ландшафт, модели, текстурирование и пр.
Моддинг игр [5]
Модификация компьютерных игр, создание дополнений, перевод, хакинг.
Игры [161]
Статьи об играх, в том числе и сделанных на гейммейкерах.
Разное [129]
Статьи, которые не вошли в определённые разделы.
Наш опрос
Разработка игр для вас:
Всего ответов: 11092
Главная » Статьи » Программирование

Программирование 3D графики на Visual Basic 6 и DirectX 8. Часть 4
2.5. Использование прозрачности
Чтобы приступить к изучению четвертой компоненты цвета A (альфа-компоненты), приготовим, как водится, опытный проект. Предыдущий проект преобразован для использования одной пары текстурных координат и цвета в вертексе. Нет смысла еще раз пояснять эти преобразования, просто возьмите проект на компакт-диске в папке Pr15.
Обратите внимание на процедуру Setting, она вызывается из Sub Main непосредственно перед главным циклом. В этой процедуре мы будем тестировать различные варианты использования альфа-компоненты, пока она пустая. Альфа-компонента используется для имитации эффекта полупрозрачности предметов, так же, как и компоненты R, G и B, альфа – это байт в представлении цвета RGBA, либо значение от 0 до 1 в представлении DirectX. Наиболее распространенная формула для имитации эффекта полупрозрачности такая:
R = Arg1 * Alpha + Arg2 * (1 – Alpha)

Здесь R – это результирующий цвет, Arg1 и Arg2 – смешиваемые цвета. Альфа-компонента может применяться как «внутри» стадии текстурирования для работы с ее аргументами, так и к общему результату работы текстурирования для смешивания его с ранее нарисованным изображением. Рассмотрим сначала более простой первый вариант. Текстура к этому проекту сохранена в формате TGA и имеет в составе альфа-компоненту, также альфа-компоненту содержит и цвет вертексов, обратите внимание – все нижние вертексы в цилиндре имеют цвет &HFF0000FF, то есть:
Alpha = &HFF
R = &H0
G = &H0
B = &HFF

А верхние вертексы в цилиндре имеют цвет &HFF, то есть Alpha в них равна 0. Так же, как и другие компоненты цвета вертекса, альфа-компонента линейно интерполируется между вертексами. Впишите в процедуру Setting такие строки:

Code
d3dDevice.SetTextureStageState 0, D3DTSS_COLOROP, D3DTOP_BLENDDIFFUSEALPHA
  d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG1, D3DTA_TEXTURE
  d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE

D3DTOP_BLENDDIFFUSEALPHA для параметра D3DTSS_COLOROP обозначает, что используется альфа-компонента из DIFFUSE. Запускаем программу и видим, что цилиндр снизу покрыт текстурой, которая при подъеме постепенно переходит в DIFFUSE. Чтобы поменять местами TEXTURE и DIFFUSE, достаточно поменять местами COLORARG1 и COLORARG2:

Code
d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG1, D3DTA_DIFFUSE
  d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG2, D3DTA_TEXTURE

Теперь попробуем использовать альфа-компоненту текстуры:

Code
d3dDevice.SetTextureStageState 0, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA

Не правда ли, названия параметров говорят сами за себя? Так же, как и для цвета, для альфа-компоненты тоже предусмотрены оператор и аргументы, они применяются, если нужно каким-либо образом совместить альфа-компоненты нескольких аргументов. Рассмотрим такой вариант:

Code
d3dDevice.SetTextureStageState 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1
  d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG1, D3DTA_TEXTURE
  d3dDevice.SetTextureStageState 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE
  d3dDevice.SetTextureStageState 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE
  d3dDevice.SetTextureStageState 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE

  d3dDevice.SetTextureStageState 1, D3DTSS_COLOROP, D3DTOP_BLENDCURRENTALPHA
  d3dDevice.SetTextureStageState 1, D3DTSS_COLORARG1, D3DTA_CURRENT
  d3dDevice.SetTextureStageState 1, D3DTSS_COLORARG2, D3DTA_DIFFUSE

Мы применили две стадии текстурирования. В нулевой стадии накладывается цвет из текстуры, а альфа-компоненты текстуры и Diffuse перемножаются. В следующей стадии смешиваются цвет из Diffuse с текущим цветом (то есть ранее наложенным, из текстуры), оператор смешения использует текущее (то есть ранее вычисленное в нулевой стадии) значение альфа-компоненты.
Мы рассмотрели несколько примеров использования прозрачности, но цилиндр пока прозрачным не был. Чтобы сделать его прозрачным, нужно чтобы в альфа-смешении участвовали не только аргументы текстурирования, но и ранее отрисованное изображение, то есть то, что находится в BackBuffer. При этом в качестве аргументов альфа-смешения будут выступать значение цвета, взятое из BackBuffer, и значение, полученное в результате текстурирования. Эти аргументы нужно указать:

Code
d3dDevice.SetRenderState D3DRS_SRCBLEND, D3DBLEND_SRCALPHA
  d3dDevice.SetRenderState D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA
  d3dDevice.SetRenderState D3DRS_BLENDOP, D3DBLENDOP_ADD

Это более гибкий подход, позволяющий большего достичь, но и более ресурсоемкий, так как производится чтение из BackBuffer. В этих строках указывается, что в качестве множителя для первого аргумента – D3DRS_SRCBLEND, выступает альфа-компонента первого аргумента – D3DBLEND_SRCALPHA, для второго аргумента в качестве множителя мы выбрали D3DBLEND_INVSRCALPHA, то есть 1 – D3DBLEND_SRCALPHA. Далее мы выбрали оператор, применяемый для смешения полученных произведений – D3DRS_BLENDOP, этот оператор – D3DBLENDOP_ADD, то есть сложение.
Использование такого типа альфа-смешения необходимо разрешить:

Code
d3dDevice.SetRenderState D3DRS_ALPHABLENDENABLE, 1[code]
Закрасим цилиндр текстурой, с использования альфа-компоненты из DIFFUSE:

[code] d3dDevice.SetTextureStageState 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1
  d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG1, D3DTA_TEXTURE
  d3dDevice.SetTextureStageState 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1
  d3dDevice.SetTextureStageState 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE

Цилиндр действительно стал полупрозрачным, но явно видны недостатки – временами изображение правильное, но через ближнюю сторону цилиндра, вторая видна не всегда. Это результат работы ZBuffer, если ближняя сторона цилиндра рисуется раньше, то дальняя в этом месте уже не рисуется, это отлично работало и избавляло нас от упорядочивания треугольников, пока мы не использовали прозрачность. Отключим ZBuffer:

Code
d3dDevice.SetRenderState D3DRS_ZENABLE, D3DZB_FALSE

Эта строка находится в процедуре Main.
Что ж, изображение улучшилось, но небольшие погрешности в изображении остались, результирующий цвет получается разным, в зависимости от того, какая сторона цилиндра рисуется раньше. Кроме того отключение ZBuffer приведет к тому, что прозрачные предметы будут рисоваться даже тогда, когда они закрыты непрозрачными. Для того, чтобы правильно отображать предметы, использующие такой тип прозрачности, во время их рисования отключают не ZBuffer, а запись в него:

Code
d3dDevice.SetRenderState D3DRS_ZWRITEENABLE, 0

И прозрачные треугольники нужно упорядочивать так, чтобы они отображались от дальних к ближним. Упорядочивание – процесс весьма ресурсоемкий, ведь число треугольников может достигать сотен тысяч! Но иногда удается этого избежать. Например, в нашем цилиндре внутренняя сторона (а это обратная сторона треугольников) всегда находится сзади. Таким образом, достаточно отобразить цилиндр дважды – сначала внутреннюю, а затем и внешнюю стороны, и необходимость в упорядочивании отпадает. Для этого изменим процедуру Render:

Code
Private Sub Render()
  d3dDevice.Clear 0, ByVal 0, D3DCLEAR_TARGET Or D3DCLEAR_ZBUFFER, &H346666, 1, 0
  d3dDevice.BeginScene

  d3dDevice.SetStreamSource 0, vBuf, vSize
  d3dDevice.SetVertexShader vFlag
  d3dDevice.SetTexture 0, Tex0

  d3dDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_CW
  d3dDevice.DrawPrimitive D3DPT_TRIANGLESTRIP, 0, 128
  d3dDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_CCW
  d3dDevice.DrawPrimitive D3DPT_TRIANGLESTRIP, 0, 128

  d3dDevice.EndScene
  d3dDevice.Present ByVal 0, ByVal 0, 0, ByVal 0
End Sub

Посмотрите на цилиндр теперь – он отображается корректно!
Но такие проблемы с альфа-смешением возникают не всегда, если множитель при D3DRS_DESTBLEND равен единице – упорядочивание не требуется (запись в ZBuffer отключаем все равно!). Такие настройки могут применяться, например, для изображения огня. Впишите в процедуру Setting такие настройки:

Code
d3dDevice.SetRenderState D3DRS_ZENABLE, D3DZB_TRUE
  d3dDevice.SetRenderState D3DRS_ZWRITEENABLE, 0
  d3dDevice.SetRenderState D3DRS_ALPHABLENDENABLE, 1

  d3dDevice.SetRenderState D3DRS_SRCBLEND, D3DBLEND_SRCALPHA
  d3dDevice.SetRenderState D3DRS_DESTBLEND, D3DBLEND_ONE
  d3dDevice.SetRenderState D3DRS_BLENDOP, D3DBLENDOP_ADD

  d3dDevice.SetTextureStageState 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1
  d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG1, D3DTA_TEXTURE
  d3dDevice.SetTextureStageState 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE
  d3dDevice.SetTextureStageState 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE
  d3dDevice.SetTextureStageState 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE

Такой цилиндр отображается корректно без упорядочивания. При таком альфа-смешении задний фон всегда осветляется. Есть второе значение для D3DRS_BLENDOP, при котором не требуется упорядочивание треугольников, а изображение затемняется:

Code
d3dDevice.SetRenderState D3DRS_BLENDOP, D3DBLENDOP_REVSUBTRACT

При таком значении D3DRS_BLENDOP, произведение цвета, полученного при текстурировании, на альфа-аргумент вычитается из цвета заднего плана.
Напомню, что проект находится на компакт-диске в папке Pr15, причем процедура Setting в нем пуста.

2.6. Использование моделей. Mesh
До сих пор для того, чтобы создать какой-либо геометрический объект, мы выполняли некоторые стандартные действия, занимающие достаточно объемный код.
В первую очередь мы создавали флаговое описание вертекса, новый тип, соответствующий формату вертекса и сам вертексный буфер. Для более сложных моделей так же создавался индексный буфер.
Далее для отображения нашего геометрического объекта мы сообщали устройству рендера (d3dDevice), какие вертексный и индексный буферы используется, какой размер у вертекса и как его обрабатывать (флаговое описание). Дальше мы вызывали процедуру рендера с указанием с какого вертекса и индекса следует начинать вывод и сколько треугольников отображать.
Ну и, естественно, необходимо было не забыть уничтожить все ранее созданные объекты до завершения программы.
А, между прочим, создавать, отображать и уничтожать геометрические объекты можно одной командой. В Direct3D существует класс D3DXMesh, предназначенный для хранения таких объектов. Объект (экземпляр класса) можно загружать из файла, так же некоторые простые формы, такие как куб, сфера и т. п. можно генерировать.
Создадим новый проект, как обычно на основе предыдущего. Модуль modMain перепишем полностью. В разделе Declarations оставим только две общих переменных Running и FPS и добавим один объект класса D3DXMesh:

Code
Public Running As Boolean
Public FPS As Long
Dim Mesh As D3DXMesh

Добавим процедуры для инициализации света и материала:

Code
Private Sub InitLight()
Dim Light As D3DLIGHT8
  Light.Type = D3DLIGHT_DIRECTIONAL
  Light.Direction = vec3(-1, -1, 1)
  Light.diffuse.r = 1
  Light.diffuse.g = 1
  Light.diffuse.b = 1
  d3dDevice.SetLight 0, Light
  d3dDevice.LightEnable 0, 1
End Sub

Private Sub InitMaterial()
Dim Mat As D3DMATERIAL8
  Mat.diffuse.r = 1
  Mat.diffuse.g = 1
  Mat.diffuse.b = 1
  Mat.Ambient = Mat.diffuse
  d3dDevice.SetMaterial Mat
End Sub

Уже привычная инициализация трансформаций:

Code
Private Sub InitMatrix()
Dim Mtrx As D3DMATRIX
  D3DXMatrixIdentity Mtrx
  d3dDevice.SetTransform D3DTS_WORLD, Mtrx

  D3DXMatrixPerspectiveFovLH Mtrx, Pi / 4, frmD3D.ScaleHeight / frmD3D.ScaleWidth, 0.5, 10
  d3dDevice.SetTransform D3DTS_PROJECTION, Mtrx

  D3DXMatrixLookAtLH Mtrx, vec3(0, 1, -3), vec3(0, 0, 0), vec3(0, 1, 0)
  d3dDevice.SetTransform D3DTS_VIEW, Mtrx
End Sub

Процедура ClearAll будет содержать только уничтожение Mesh и D3DTerminate:

Code
Private Sub ClearAll()
  Set Mesh = Nothing
  D3DTerminate
End Sub

Создадим новую процедуру InitMesh для инициализации модели, она выполняет те же функции, которые раньше выполняла процедура InitGeometry:

Code
Private Sub InitMesh()
  Set Mesh = d3dx.CreateBox(d3dDevice, 1, 1, 1, Nothing)
End Sub

В этой процедуре из одной строки генерируется Box (прямоугольный параллелепипед) с размерами «1, 1, 1», то есть куб.
В процедуре Render между BeginScene и EndScene так же будет всего одна строка:

Code
Private Sub Render()
  d3dDevice.Clear 0, ByVal 0, D3DCLEAR_TARGET Or D3DCLEAR_ZBUFFER, &H346666, 1, 0
  d3dDevice.BeginScene

  Mesh.DrawSubset 0

  d3dDevice.EndScene
  d3dDevice.Present ByVal 0, ByVal 0, 0, ByVal 0
End Sub

И, наконец, Sub Main:

Code
Public Sub Main()
Dim Mtrx As D3DMATRIX
  frmD3D.Show
  QFreqIni
  D3DInit frmD3D.hWnd
  InitMatrix
  InitMesh
  InitLight
  InitMaterial
  d3dDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_CCW
  d3dDevice.SetRenderState D3DRS_ZENABLE, D3DZB_TRUE
  d3dDevice.SetRenderState D3DRS_LIGHTING, 1
  Running = True
  Do While Running
  DoEvents
  D3DXMatrixRotationY Mtrx, QTime * 0.5
  d3dDevice.SetTransform D3DTS_WORLD, Mtrx
  Render
  FPS = FPS + 1
  Loop
  Unload frmD3D
  ClearAll
End Sub

Здесь все привычно, добавился только вызов InitMesh – инициализации модели, в нашем случае куба.
Запускаем программу и видим вращающийся куб. Модель, генерируемая D3DX, содержит в формате вертекса координаты и нормаль, поэтому мы можем наблюдать эффект освещения. Обратите внимание, команда для непосредственного отображения модели Mesh.DrawSubset имеет параметр. Дело в том, что сложные модели могут состоять из нескольких частей, называемых Subset, которые можно отображать независимо друг от друга. Это сделано для того, чтобы между отображением частей можно было поменять параметры рендеринга, например, сменить материал или текстуру. Или, представьте, что наша модель – автомобиль. Если ее колеса будут отдельными частями, их можно будет вращать, меняя трансформацию мира непосредственно перед их отображением.
Познакомимся с другими примитивными формами, которые можно генерировать средствами D3DX. Заменим единственную строку в InitMesh на такую:

Code
Set Mesh = d3dx.CreateSphere(d3dDevice, 0.6, 32, 32, Nothing)

Теперь у нас не куб, а сфера. Параметры «0.6, 32, 32» – это радиус, число «меридианов» и число «параллелей». Так же можно сгенерировать цилиндр (а мы мучались!):

Code
Set Mesh = d3dx.CreateCylinder(d3dDevice, 0.7, 0.7, 1, 64, 1, Nothing)

Тор:

Code
Set Mesh = d3dx.CreateTorus(d3dDevice, 0.2, 0.7, 32, 32, Nothing)

И, ставшую уже классической, модель чайника:

Code
Set Mesh = d3dx.CreateTeapot(d3dDevice, Nothing)

Несколько другой синтаксис у процедуры генерации объемного текста:

Code
d3dx.CreateText d3dDevice, frmD3D.hDC, "Test", 0.002, 0.1, Mesh, Nothing, ByVal 0

Для того, чтобы это сработало, необходимо присвоить свойству Font формы frmD3D какой-нибудь шрифт, обязательно TrueType! Этим требованиям удовлетворяет, например, шрифт Courier New, но не Courier.
Код проекта находится на компакт-диске в папке Pr16.

2.7. Имитация отражения. Spherical Environment Mapping
До сих пор мы использовали для наложения текстур координаты, непосредственно заданные в вертексах. Существуют и другие способы, при которых текстурные координаты рассчитываются непосредственно при растеризации. Один из наиболее распространенных подходов – расчет текстурных координат исходя из позиции вертекса и камеры и нормали вертекса, используемый для имитации отражений. Все расчеты производит DirectX, освобождая нас от изучения оптики.
Возьмем наш предыдущий проект, оставив в InitMesh генерацию чайника:

Code
Private Sub InitMesh()
  Set Mesh = d3dx.CreateTeapot(d3dDevice, Nothing)
End Sub

Так же уберем все, связанное с использованием света и материала. Добавим в модуль modMain текстуру Tex и процедуру Setting, где будем испытывать нововведения, не забываем вписать вызов Setting перед главным циклом. Текстуру загрузим из файла sky.jpg, находящегося в папке Pr17. Содержимое Setting сначала будет таким:

Code
Private Sub Setting()
  d3dDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_CCW
  d3dDevice.SetRenderState D3DRS_ZENABLE, D3DZB_TRUE

  Set Tex = d3dx.CreateTextureFromFile(d3dDevice, "sky.jpg")
  TexFilter 0, TexF_TriLinear

  d3dDevice.SetTextureStageState 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1
  d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG1, D3DTA_TEXTURE
  d3dDevice.SetTexture 0, Tex
End Sub

Пока все по старому, загрузили текстуру, настроили нулевую стадию текстурирования. Однако текстуры не видно, ведь мы не указали текстурные координаты, вся модель закрашена цветом, взятым из текстуры с координат 0, 0. Добавим в Setting такую строку:

Code
d3dDevice.SetTextureStageState 0, D3DTSS_TEXCOORDINDEX, _
  D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR

Теперь чайник затекстурирован, но текстура не очень похожа на отражение. Описать работу этой команды, не вдаваясь в подробности, можно так – вычисляется направление нормали вертекса относительно направления на камеру. Если нормаль направлена на камеру – результирующие значения u и v текстурных координат равны нулю. Если нормаль направлена левее – значение u уменьшается, правее – увеличивается. Если нормаль направлена выше – значение v уменьшается, ниже – увеличивается. Изменения u и v лежат в диапазоне от -1 до 1, причем u и v не могут принять крайние значения диапазона одновременно, они лежат в пределах окружности с центром в начале координат и радиусом 1 на координатной плоскости. Но на нашей текстуре изображение отражения лежит на вчетверо меньшей (по площади) окружности с центром в точке 0.5, 0.5 и радиусом 0.5.
Познакомимся с четвертой трансформацией, помогающей исправить эту ситуацию. Это трансформация D3DTS_TEXTURE0, позволяющая с помощью матриц задавать изменения текстурных координат нулевой стадии текстурирования, аналогичные трансформации существуют для всех восьми стадий. Добавим в процедуру Setting такие строки:

Code
D3DXMatrixTranslation Mtrx, 0.5, 0.5, 0
  d3dDevice.SetTransform D3DTS_TEXTURE0, Mtrx
  D3DXMatrixScaling Mtrx, 0.5, 0.5, 1
  d3dDevice.MultiplyTransform D3DTS_TEXTURE0, Mtrx

Необходимо там же объявить переменную Mtrx типа D3DMATRIX.
Запускаем программу, но картинка не поменялась, все потому, что использование этой трансформации по умолчанию запрещено. Такой строкой мы разрешаем ее использовать для изменения двух текстурных координат:

Code
d3dDevice.SetTextureStageState 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2

Теперь изображение гораздо больше похоже на натуральное. Проект можно взять в папке Pr17.

Продолжение

Категория: Программирование | Добавил: -Mikle- (15 Января 2011)
Просмотров: 8470 | Рейтинг: 5.0/4 |
Теги: vb, DirectX 8.1, D3D, программирование, DirectX 8, Direct3D, VB6, Visual Basic, Visual Basic 6, Программирование 3D графики
Дополнительные опции:
Также если вы считаете, что данный материал мог быть интересен и полезен кому-то из ваших друзей, то вы бы могли посоветовать его, отправив сообщение на e-mail друга:

Игровые объявления и предложения:
Если вас заинтересовал материал «Программирование 3D графики на Visual Basic 6 и DirectX 8. Часть 4», и вы бы хотели прочесть что-то на эту же тему, то вы можете воспользоваться списком схожих материалов ниже. Данный список сформирован автоматически по тематическим меткам раздела. Предлагаются такие схожие материалы: Если вы ведёте свой блог, микроблог, либо участвуете в какой-то популярной социальной сети, то вы можете быстро поделиться данной заметкой со своими друзьями и посетителями.

Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск по сайту
10 случ. движков
  • 8bitworkshop
  • Moddio
  • Impact
  • Multimedia Builder
  • Asphyre Sphinx
  • GameBryo
  • AppGameKit Studio
  • AppGameKit
  • Arcade Game Studio
  • CoolBasic
  • Друзья сайта
    Игровой форум GFAQ.ru Перевод консольных игр
    Все права сохранены. GcUp.ru © 2008-2024 Рейтинг