Среда, 01 Мая 2024, 09:27

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

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

Программирование 3D графики на Visual Basic 6 и DirectX 8. Часть 5
2.8. Преобразование содержимого моделей
Поставим себе новую задачу – покроем чайник, кроме текстуры отражений, обычной текстурой. Но что делать, если наша модель не содержит в вертексах текстурных координат? Очевидно, нужно их туда добавить. Это можно сделать с помощью функции CloneMeshFVF класса D3DXMesh:

Code
Set Mesh = Mesh.CloneMeshFVF(0, D3DFVF_XYZ Or D3DFVF_NORMAL Or D3DFVF_TEX1, d3dDevice)

Эта функция меняет флаговое описание вертекса на новое и соответственно преобразует формат вертекса. Но поменять формат мало, нужно еще записать в новые поля соответствующие данные. Для начала извлечем из модели вертексный буфер, для этого в процедуре InitMesh создадим объектную переменную типа Direct3DVertexBuffer8 и массив для вертексов:

Code
Dim vBuf As Direct3DVertexBuffer8, vCnt As Long, n As Long, Vert() As vFormat

Кроме этого созданы вспомогательные переменные n для использования в цикле и vCnt, которая будет содержать количество вертексов. Для массива Vert() зададим тип:

Code
Private Type vFormat
  Pos As D3DVECTOR
  Normal As D3DVECTOR
  tu0 As Single
  tv0 As Single
End Type

Далее так. Считываем из меша (модели) число вертексов в нем и ассоциируем объектную переменную vBuf с вертексным буфером меша:

Code
Set vBuf = Mesh.GetVertexBuffer
  vCnt = Mesh.GetNumVertices

Внимательно присмотритесь к первой строке. Функция GetVertexBuffer не создает новый вертексный буфер, а лишь возвращает указатель на имеющийся в меше, а объектная переменная vBuf ассоциируется с ним.
Перезадаем размер массива в соответствие с числом вертексов в буфере и считываем данные вертексов в массив:

Code
ReDim Vert(vCnt - 1)
  D3DVertexBuffer8GetData vBuf, 0, vCnt * Len(Vert(0)), 0, Vert(0)

Прописываем в вертексы текстурные координаты, тут вариантов может быть множество, например можно поставить текстурные координаты tu0 и tv0 равными координатам x и z соответственно:

Code
For n = 0 To vCnt - 1
  Vert(n).tu0 = Vert(n).Pos.x
  Vert(n).tv0 = Vert(n).Pos.z
  Next n

Возвращаем данные в вертексный буфер и уничтожаем, больше ненужную, объектную переменную vBuf:

Code
D3DVertexBuffer8SetData vBuf, 0, vCnt * Len(Vert(0)), 0, Vert(0)
  Set vBuf = Nothing

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

Code
Set Tex0 = d3dx.CreateTextureFromFile(d3dDevice, "marb.jpg")
  d3dDevice.SetTexture 0, Tex0
  Set Tex1 = d3dx.CreateTextureFromFile(d3dDevice, "sky.jpg")
  d3dDevice.SetTexture 1, Tex1

Настроим параметры текстурирования для этих стадий:

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

  d3dDevice.SetTextureStageState 1, D3DTSS_COLOROP, D3DTOP_ADD
  d3dDevice.SetTextureStageState 1, D3DTSS_COLORARG1, D3DTA_TEXTURE
  d3dDevice.SetTextureStageState 1, D3DTSS_COLORARG2, D3DTA_CURRENT
  d3dDevice.SetTextureStageState 1, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR

Зададим трансформацию для первой стадии и разрешим ее использование:

Code
d3dDevice.SetTextureStageState 1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2
  D3DXMatrixTranslation Mtrx, 0.5, 0.5, 0
  d3dDevice.SetTransform D3DTS_TEXTURE1, Mtrx
  D3DXMatrixScaling Mtrx, 0.5, 0.5, 1
  d3dDevice.MultiplyTransform D3DTS_TEXTURE1, Mtrx

Осталось инициализировать свет и материал, и программа готова:

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

Проект можно взять в папке Pr18.

2.9. Приближение к реальности
Наш чайник выглядит не очень естественно, блеск слишком сильный, больше похоже не на фарфор, а на полированный алюминий. Для того чтобы отражение немного «приглушить», можно затемнить текстуру. Попробуйте понизить ее яркость с помощью любого графического редактора, и изображение станет более реалистичным, но это не очень удобно, представьте, что у вас множество моделей с различным блеском, для каждой хранить свою текстуру отражений? Можно было бы понижать яркость, задав в материале Diffuse серого цвета и умножив Texture на Diffuse, но Diffuse у нас уже занят, без него мы не сделаем свет.
Для таких и подобных случаев в Direct3D предусмотрена специальная переменная TEXTUREFACTOR типа Long, в нее можно записывать любое значение и использовать в стадиях текстурирования наравне с другими аргументами. Перепишем настройку стадий текстурирования по новому, присвоим переменной TEXTUREFACTOR значение &H404040, соответствующее темно серому цвету:

Code
d3dDevice.SetRenderState D3DRS_TEXTUREFACTOR, &H404040

И сами стадии текстурирования:

Code
d3dDevice.SetTextureStageState 0, D3DTSS_COLOROP, D3DTOP_MODULATE
  d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG1, D3DTA_TEXTURE
  d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE
  d3dDevice.SetTextureStageState 0, D3DTSS_RESULTARG, D3DTA_TEMP

  d3dDevice.SetTextureStageState 1, D3DTSS_COLOROP, D3DTOP_MODULATE
  d3dDevice.SetTextureStageState 1, D3DTSS_COLORARG1, D3DTA_TEXTURE
  d3dDevice.SetTextureStageState 1, D3DTSS_COLORARG2, D3DTA_TFACTOR
  d3dDevice.SetTextureStageState 1, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR

  d3dDevice.SetTextureStageState 2, D3DTSS_COLOROP, D3DTOP_ADD
  d3dDevice.SetTextureStageState 2, D3DTSS_COLORARG1, D3DTA_TEMP
  d3dDevice.SetTextureStageState 2, D3DTSS_COLORARG2, D3DTA_CURRENT

Рассмотрим подробнее эти настройки.
В нулевой стадии происходит умножение Texture на Diffuse, что, в сочетании с использованием света, дает эффект освещения, с этим мы уже знакомы. Но в четвертой строке мы видим новый параметр D3DTSS_RESULTARG, которому присваивается так же новое значение D3DTA_TEMP. Это обозначает, что результат умножения Texture на Diffuse попадет не в регистр Current, как обычно, а в специальный регистр Temp, предназначенный для временного хранения данных во время работы стадий текстурирования.
В первой стадии текстура отражений умножается на TEXTUREFACTOR, что должно обеспечить ослабление блеска. Результат попадает в регистр Current, поскольку не указано другое.
И во второй стадии суммируются результаты первых двух стадий для формирования окончательного результата.
Такой подход более ресурсоемкий, чем тот, что был выбран в предыдущей главе. Используется уже не две, а три стадии текстурирования, что может отразиться несовместимостью с некоторыми старыми видеоадаптерами (смотрите Caps!). Но этот подход гораздо гибче, поскольку позволяет регулировать отражающую способность моделей.
Проект можно взять в папке Pr18, этот вариант помечен ремарками.
А мы продолжим эксперименты с нашим чайником. Поставим себе более сложную задачу – отобразить фарфоровый чайник с цветным рисунком и золотой полоской, при этом постараться обойтись двумя текстурами, включая карту отражений, и одним проходом рендера.
В папке Pr19 на компакт-диске возьмем текстуры. Для отображения такой картинки, как в файле flower.tga, более удобно переделать вычисление текстурных координат таким образом:

Code
Private Sub InitMesh()
Dim vBuf As Direct3DVertexBuffer8, vCnt As Long, n As Long, Vert() As vFormat
  Set Mesh = d3dx.CreateTeapot(d3dDevice, Nothing)
  Set Mesh = Mesh.CloneMeshFVF(0, D3DFVF_XYZ Or D3DFVF_NORMAL Or D3DFVF_TEX1, d3dDevice)
  Set vBuf = Mesh.GetVertexBuffer
  vCnt = Mesh.GetNumVertices
  ReDim Vert(vCnt - 1)
  D3DVertexBuffer8GetData vBuf, 0, vCnt * Len(Vert(0)), 0, Vert(0)
  For n = 0 To vCnt - 1
  Vert(n).tv0 = 0.41 - Vert(n).Pos.y
  Vert(n).tu0 = Vert(n).Pos.x + 0.5
  If Vert(n).tu0 > 1.5 Then
  If Vert(n).tv0 < 0.2 Then Vert(n).tv0 = 0.2
  Else
  If Vert(n).tv0 < 0 Then Vert(n).tv0 = 0
  End If
  If Vert(n).tv0 > 1 Then Vert(n).tv0 = 1
  If Vert(n).tu0 < 0 Then Vert(n).tu0 = 0
  If Vert(n).tu0 > 1 Then Vert(n).tu0 = 1
  Next n
  D3DVertexBuffer8SetData vBuf, 0, vCnt * Len(Vert(0)), 0, Vert(0)
  Set vBuf = Nothing
End Sub

Строго говоря, покрыть сложный трехмерный объект 2D текстурой без искажений невозможно, так или иначе, в некоторых местах текстура окажется сильно растянутой в каком-либо направлении, получить модель без этих недостатков можно, только если примириться с нестыковкой текстуры – со швами. Если в предыдущем проекте мы, как бы, проецировали текстуру сверху, приравнивая текстурные координаты координатам x и z, то для новой задачи более подходит поставить зависимость от x и y, чтобы не боках чайника, где и будет расположен рисунок, искажения были минимальны. Кроме того, в приведенном фрагменте кода значения текстурных координат ограничены, чтобы рисунок не повторялся. Более подробно пояснять этот фрагмент не буду – с точки зрения программирования Direct3D в нем нет ничего нового для нас.
Что, с точки зрения оптических свойств материала, представляет собой золотая полоска? Чем она отличается от просто желтой полосы? Если на блестящей поверхности, окрашенной в желтый (или любой другой) цвет, цвет отражений не зависит от цвета поверхности, а цвет поверхности зависит от освещения, что мы и воспроизвели в прошлом примере, то в материалах, обладающий металлическим блеском, все несколько по-другому. Собственный цвет металла достаточно темный, им даже можно пренебречь, зато коэффициент отражения у него значительно выше, а у цветных металлов он еще и зависит от цвета отражаемого изображения.
Вынесем все настройки, характеризующие данный пример, в отдельную процедуру Setting. Загрузим текстуры и включим фильтрацию:

Code
Set Tex0 = d3dx.CreateTextureFromFile(d3dDevice, "sky.jpg")
  d3dDevice.SetTexture 0, Tex0
  Set Tex1 = d3dx.CreateTextureFromFile(d3dDevice, "flower.tga")
  d3dDevice.SetTexture 1, Tex1
  TexFilter 0, TexF_TriLinear
  TexFilter 1, TexF_TriLinear

В нулевой стадии текстурирования формируем отражение:

Code
d3dDevice.SetTextureStageState 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1
  d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG1, D3DTA_TEXTURE
  d3dDevice.SetTextureStageState 0, D3DTSS_TEXCOORDINDEX, _
  D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR
  d3dDevice.SetTextureStageState 0, D3DTSS_RESULTARG, D3DTA_TEMP
  d3dDevice.SetTextureStageState 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2
  D3DXMatrixTranslation Mtrx, 0.5, 0.5, 0
  d3dDevice.SetTransform D3DTS_TEXTURE0, Mtrx
  D3DXMatrixScaling Mtrx, 0.5, 0.5, 1
  d3dDevice.MultiplyTransform D3DTS_TEXTURE0, Mtrx

Обратите внимание – результат этой операции попадает во временный регистр D3DTA_TEMP.
В следующей, первой стадии текстурирования заполняются альфа и цветовая составляющие регистра D3DTA_CURRENT. Он не указан явно в качестве RESULTARG, так как является таковым по умолчанию. Цветовая составляющая получается умножением D3DTA_TEXTURE на D3DTA_DIFFUSE, то есть мы получаем текстуру с эффектом освещения:

Code
d3dDevice.SetTextureStageState 1, D3DTSS_COLOROP, D3DTOP_MODULATE
  d3dDevice.SetTextureStageState 1, D3DTSS_COLORARG1, D3DTA_TEXTURE
  d3dDevice.SetTextureStageState 1, D3DTSS_COLORARG2, D3DTA_DIFFUSE
  d3dDevice.SetTextureStageState 1, D3DTSS_TEXCOORDINDEX, 0
  d3dDevice.SetTextureStageState 1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1
  d3dDevice.SetTextureStageState 1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE

Обратите внимание на строку:

Code
d3dDevice.SetTextureStageState 1, D3DTSS_TEXCOORDINDEX, 0

Здесь указывается что, не смотря на то, что это первая стадия текстурирования, текстурные координаты берутся из нулевой стадии. Итак, после первой стадии текстурирования мы имеем в регистре D3DTA_TEMP изображение отражений с полной яркостью, в альфа канале D3DTA_CURRENT у нас альфа канал из текстуры Tex1, а в цветовом канале изображение освещенного чайника с текстурой.
Следующая стадия:

Code
d3dDevice.SetRenderState D3DRS_TEXTUREFACTOR, &H30B0A020
  d3dDevice.SetTextureStageState 2, D3DTSS_COLOROP, D3DTOP_MULTIPLYADD
  d3dDevice.SetTextureStageState 2, D3DTSS_COLORARG1, D3DTA_TEMP
  d3dDevice.SetTextureStageState 2, D3DTSS_COLORARG2, D3DTA_TFACTOR Or D3DTA_ALPHAREPLICATE
  d3dDevice.SetTextureStageState 2, D3DTSS_COLORARG0, D3DTA_CURRENT

Здесь мы задаем значение TEXTUREFACTOR и выполняем один из операторов, работающих с тремя аргументами – D3DTOP_MULTIPLYADD. Он умножает COLORARG1 на COLORARG2 и к полученному произведению прибавляет COLORARG0. В вычислении COLORARG2 мы впервые использовали модификатор D3DTA_ALPHAREPLICATE. Смысл его в том, что в используемом в комбинации с ним цветовом аргументе все три цветовых компоненты (R, G, B ) заменяются на альфа компоненту. Первый аргумент D3DTA_TEMP, содержащий, как мы помним, отражение, мы умножаем не на TEXTUREFACTOR, как в предыдущем примере, а на его альфа компоненту. К полученному ослабленному изображению отражений мы прибавляем D3DTA_CURRENT, где в данный момент находится изображение модели с текстурой Tex1 без блеска. В результате в регистре D3DTA_CURRENT получаем реалистичный фарфор.
Теперь воспроизведем золото. В D3DTA_TEMP по-прежнему находится изображение отражений с полной яркостью, для того, чтобы сымитировать цветной (в данном случае желтый) металл, его необходимо умножить на соответствующий цвет. Снова применяем TEXTUREFACTOR, но теперь уже не альфа компоненту, а цвет:

Code
d3dDevice.SetTextureStageState 3, D3DTSS_COLOROP, D3DTOP_MODULATE
  d3dDevice.SetTextureStageState 3, D3DTSS_COLORARG1, D3DTA_TEMP
  d3dDevice.SetTextureStageState 3, D3DTSS_COLORARG2, D3DTA_TFACTOR
  d3dDevice.SetTextureStageState 3, D3DTSS_RESULTARG, D3DTA_TEMP

Результат отправляем в D3DTA_TEMP, чтобы не потерять изображение фарфора, находящееся в D3DTA_CURRENT. Если убрать последнюю строку и запустить программу, можно увидеть золотой чайник.
Итак, что мы имеем после четырех стадий текстурирования? В D3DTA_CURRENT находится изображение фарфора, в D3DTA_TEMP – золота, а в альфа канале D3DTA_CURRENT альфа канал текстуры Tex1, определяющий в каких местах должен быть виден фарфор, а в каких золото. Остается последняя стадия:

Code
d3dDevice.SetTextureStageState 4, D3DTSS_COLOROP, D3DTOP_BLENDCURRENTALPHA
  d3dDevice.SetTextureStageState 4, D3DTSS_COLORARG1, D3DTA_TEMP
  d3dDevice.SetTextureStageState 4, D3DTSS_COLORARG2, D3DTA_CURRENT

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

Архив с примерами к статье

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

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

Всего комментариев: 2
+1-
2 FSO   (22 Июня 2012 14:17) [Материал]
FSOКруто. VB -это сила cool

+1-
1 P1rat   (16 Января 2011 09:57) [Материал]
P1ratrespect Полезный урок, как и остальные части! =)

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск по сайту
10 случ. движков
  • Skyline
  • ZGameEditor
  • Bos Wars
  • jPCT
  • ADRIFT
  • Adventure Game Studio
  • Unreal Engine 5
  • Doom 3
  • Retribution Engine
  • Enigma
  • Друзья сайта
    Игровой форум GFAQ.ru Перевод консольных игр
    Все права сохранены. GcUp.ru © 2008-2024 Рейтинг