До сих пор мы накладывали на наши модели не более одной текстуры, в то время как Direct3D позволяет использовать одновременно до восьми текстур. Мы уже не раз использовали такую команду:

код на языке vb<код на языке vb
 
  d3dDevice.SetRenderState D3DRS_LIGHTING, 0
 

Можно было написать:

код на языке vb
 
  d3dDevice.SetTextureStageState 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1
 

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

код на языке vb
 
  d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG1, D3DTA_TEXTURE
  d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE
  d3dDevice.SetTextureStageState 0, D3DTSS_COLOROP, D3DTOP_MODULATE
 

То есть первый аргумент – текстура, второй – цвет, действие – умножение. Мы заменили умножение D3DTOP_MODULATE на выбор первого аргумента D3DTOP_SELECTARG1.

Попробуем добиться какого-нибудь полезного эффекта, используя две текстуры. За основу берем проект из главы «Мип-мэппинг, фильтрация текстур». Для начала то, что мы уже отладили – фильтрация текстур, перенесем в модуль ModDX. Для этого создадим такую процедуру:

код на языке vb
 
Public Sub TexFilter(Stage As Long, TF As TexF, Optional MaxAnisotropy As Long = 2)
  Select Case TF
    Case TexF_None
      d3dDevice.SetTextureStageState Stage, D3DTSS_MIPFILTER, D3DTEXF_NONE
      d3dDevice.SetTextureStageState Stage, D3DTSS_MAGFILTER, D3DTEXF_NONE
      d3dDevice.SetTextureStageState Stage, D3DTSS_MINFILTER, D3DTEXF_NONE
    Case TexF_BiLinear
      d3dDevice.SetTextureStageState Stage, D3DTSS_MIPFILTER, D3DTEXF_POINT
      d3dDevice.SetTextureStageState Stage, D3DTSS_MAGFILTER, D3DTEXF_LINEAR
      d3dDevice.SetTextureStageState Stage, D3DTSS_MINFILTER, D3DTEXF_LINEAR
    Case TexF_TriLinear
      d3dDevice.SetTextureStageState Stage, D3DTSS_MIPFILTER, D3DTEXF_LINEAR
      d3dDevice.SetTextureStageState Stage, D3DTSS_MAGFILTER, D3DTEXF_LINEAR
      d3dDevice.SetTextureStageState Stage, D3DTSS_MINFILTER, D3DTEXF_LINEAR
    Case TexF_Anisotropic
      d3dDevice.SetTextureStageState Stage, D3DTSS_MIPFILTER, D3DTEXF_LINEAR
      d3dDevice.SetTextureStageState Stage, D3DTSS_MAGFILTER, D3DTEXF_LINEAR
      If Caps.MaxAnisotropy >= MaxAnisotropy Then
        d3dDevice.SetTextureStageState Stage, D3DTSS_MINFILTER, D3DTEXF_ANISOTROPIC
        d3dDevice.SetTextureStageState Stage, D3DTSS_MAXANISOTROPY, MaxAnisotropy
      ElseIf Caps.MaxAnisotropy >= 2 Then
        d3dDevice.SetTextureStageState Stage, D3DTSS_MINFILTER, D3DTEXF_ANISOTROPIC
        d3dDevice.SetTextureStageState Stage, D3DTSS_MAXANISOTROPY, Caps.MaxAnisotropy
      Else
        d3dDevice.SetTextureStageState Stage, D3DTSS_MINFILTER, D3DTEXF_LINEAR
      End If
  End Select
End Sub
 

Аргументами процедуры TexFilter являются номер стадии текстурирования, один из четырех стандартных вариантов фильтрации и, необязательный аргумент – уровень анизотропии. Для вариантов фильтрации можно сделать набор констант, чтобы обращаться к ним по именам:

код на языке vb
 
Public Enum TexF
  TexF_None
  TexF_BiLinear
  TexF_TriLinear
  TexF_Anisotropic
End Enum
 

Имена констант говорят сами за себя, так что расшифровывать не буду.

Теперь нам понадобится две пары текстурных координат, поэтому переделаем формат вертекса:

код на языке vb
 
Private Type vFormat
  Pos As D3DVECTOR
  tu0 As Single
  tv0 As Single
  tu1 As Single
  tv1 As Single
End Type
 

Так же переделаем его флаговое описание:

код на языке vb
 
Private Const vFlag = D3DFVF_XYZ Or D3DFVF_TEX2
 

И функцию Vertex:

код на языке vb
 
Private Function Vertex(x As Single, y As Single, z As Single, tu0 As Single _
, tv0 As Single, tu1 As Single, tv1 As Single) As vFormat
  Vertex.Pos = vec3(x, y, z)
  Vertex.tu0 = tu0
  Vertex.tv0 = tv0
  Vertex.tu1 = tu1
  Vertex.tv1 = tv1
End Function
 

Вспомним, как мы генерировали цилиндр, и немного переделаем с учетом применения двух текстур:

код на языке vb
 
Private Sub InitGeometry()
Dim Vert(1) As vFormat
Dim n As Long
  vSize = Len(Vert(0))
  Set vBuf = d3dDevice.CreateVertexBuffer(2 * 65 * vSize, 0, vFlag, D3DPOOL_DEFAULT)
  For n = 0 To 64
    Vert(0) = Vertex(Sin(2 * Pi * n / 64), 1, Cos(2 * Pi * n / 64), _
    3 * n / 64, 0, 12 * n / 64, 0)
    Vert(1) = Vertex(Sin(2 * Pi * n / 64), -1, Cos(2 * Pi * n / 64), _
    3 * n / 64, 1, 12 * n / 64, 4)
    D3DVertexBuffer8SetData vBuf, vSize * 2 * n, vSize * 2, 0, Vert(0)
  Next n
End Sub
 

Создадим две текстуры:

код на языке vb
 
  Set Tex0 = d3dx.CreateTextureFromFile(d3dDevice, App.Path & "\brick.jpg")
  Set Tex1 = d3dx.CreateTextureFromFile(d3dDevice, App.Path & "\detail.jpg")
 

Файлы brick.jpg" alt="Фото" /> и detail.jpg есть в папке Pr14.

Также подберите подходящие матрицы для основных трансформаций (это вы уже можете), впишите все необходимое для создания и уничтожения цилиндра с двумя текстурами и инициализации QueryPerformanceCounter. В основной цикл можно вписать такие строки:

код на языке vb
 
  D3DXMatrixRotationY Mtrx, QTime * 0.5
  d3dDevice.SetTransform D3DTS_WORLD, Mtrx
  D3DXMatrixLookAtLH Mtrx, vec3(0, 0, -7 + 5 * Sin(QTime * 0.5)), vec3(0, 0, 0), vec3(0, 1, 0)
  d3dDevice.SetTransform D3DTS_VIEW, Mtrx
 

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

код на языке vb
TexFilter:
 
  TexFilter 0, TexF_TriLinear
  TexFilter 1, TexF_TriLinear
 

Запускаем программу – цилиндр черный. Это мы уже проходили! Настраиваем нулевую стадию текстурирования:

код на языке vb
 
  d3dDevice.SetTextureStageState 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1
  d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG1, D3DTA_TEXTURE
 

Теперь все в порядке, цвет появился, но текстура только одна, первая стадия текстурирования выключена. Во-первых, в процедуре Render нужно указывать не одну, а две текстуры:

код на языке vb
 
  d3dDevice.SetTexture 0, Tex0
  d3dDevice.SetTexture 1, Tex1
 

Во-вторых, укажем аргументы и оператор для первой стадии:

код на языке vb
 
  d3dDevice.SetTextureStageState 1, D3DTSS_COLOROP, D3DTOP_MODULATE2X
  d3dDevice.SetTextureStageState 1, D3DTSS_COLORARG1, D3DTA_TEXTURE
  d3dDevice.SetTextureStageState 1, D3DTSS_COLORARG2, D3DTA_CURRENT
 

Здесь мы первый аргумент, текстуру, умножаем на второй – D3DTA_CURRENT, этот аргумент – результат работы предыдущей стадии текстурирования. Обратите внимание, что умножение используется не D3DTOP_MODULATE, а D3DTOP_MODULATE2X. Этот оператор домножает результат перемножения аргументов на 2, что позволяет использовать одну текстуру в качестве карты теней для другой. Вспомните, что в Direct3D компоненты цвета лежат в диапазоне от 0 до 1. Если использовать в качестве второго аргумента серую текстуру (R = 128, G = 128, B = 128), мы умножаем первый аргумент сначала на 0.5, потом на 2 – то есть не меняем. Если на серой текстуре сделать более темные и более светлые места – в этих местах первый аргумент будет соответственно затемняться и высветляться.

Обратите внимание, что текстурные координаты у первой текстуры расположены более «густо», чем у нулевой. Это позволяет добиться детализации нулевой текстуры при сильном приближении.