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

Для таких и подобных случаев в Direct3D предусмотрена специальная переменная TEXTUREFACTOR типа Long, в нее можно записывать любое значение и использовать в стадиях текстурирования наравне с другими аргументами. Перепишем настройку стадий текстурирования по новому, присвоим переменной TEXTUREFACTOR значение &H404040, соответствующее темно серому цвету:

код на языке vb
 
  d3dDevice.SetRenderState D3DRS_TEXTUREFACTOR, &H404040
 

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

код на языке vb
 
  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!). Но этот подход гораздо гибче, поскольку позволяет регулировать отражающую способность моделей.

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

Для отображения такой картинки, как в файле flower.tga, более удобно переделать вычисление текстурных координат таким образом:

код на языке vb
 
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. Загрузим текстуры и включим фильтрацию:

код на языке vb
 
  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
 

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

код на языке vb
 
  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, то есть мы получаем текстуру с эффектом освещения:

код на языке vb
 
  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
 

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

код на языке vb
 
  d3dDevice.SetTextureStageState 1, D3DTSS_TEXCOORDINDEX, 0
 

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

Следующая стадия:

код на языке vb
 
  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, но теперь уже не альфа компоненту, а цвет:

код на языке vb
 
  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, определяющий в каких местах должен быть виден фарфор, а в каких золото. Остается последняя стадия:

код на языке vb
 
  d3dDevice.SetTextureStageState 4, D3DTSS_COLOROP, D3DTOP_BLENDCURRENTALPHA
  d3dDevice.SetTextureStageState 4, D3DTSS_COLORARG1, D3DTA_TEMP
  d3dDevice.SetTextureStageState 4, D3DTSS_COLORARG2, D3DTA_CURRENT
 

Задача выполнена.