12 февраля 2010 в 20:54
Уроки Direct3D - 2.9. Приближение к реальности
Наш чайник выглядит не очень естественно, блеск слишком сильный, больше похоже не на фарфор, а на полированный алюминий. Для того, чтобы отражение немного приглушить, можно затемнить текстуру. Попробуйте понизить ее яркость с помощью любого графического редактора, и изображение станет более реалистичным, но это не очень удобно, представьте, что у вас множество моделей с различным блеском, для каждой хранить свою текстуру отражений? Можно было бы понижать яркость, задав в материале Diffuse серого цвета и умножив Texture на Diffuse, но Diffuse у нас уже занят, без него мы не сделаем свет.
Для таких и подобных случаев в Direct3D предусмотрена специальная переменная TEXTUREFACTOR типа Long, в нее можно записывать любое значение и использовать в стадиях текстурирования наравне с другими аргументами. Перепишем настройку стадий текстурирования по новому, присвоим переменной TEXTUREFACTOR значение &H404040, соответствующее темно серому цвету:
d3dDevice.SetRenderState D3DRS_TEXTUREFACTOR, &H404040
И сами стадии текстурирования:
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, более удобно переделать вычисление текстурных координат таким образом:
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. Загрузим текстуры и включим фильтрацию:
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
В нулевой стадии текстурирования формируем отражение:
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, то есть мы получаем текстуру с эффектом освещения:
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
Обратите внимание на строку:
d3dDevice.SetTextureStageState 1, D3DTSS_TEXCOORDINDEX, 0
Здесь указывается что, не смотря на то, что это первая стадия текстурирования, текстурные координаты берутся из нулевой стадии. Итак, после первой стадии текстурирования мы имеем в регистре D3DTA_TEMP изображение отражений с полной яркостью, в альфа канале D3DTA_CURRENT у нас альфа канал из текстуры Tex1, а в цветовом канале изображение освещенного чайника с текстурой.
Следующая стадия:
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, но теперь уже не альфа компоненту, а цвет:
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, определяющий в каких местах должен быть виден фарфор, а в каких золото. Остается последняя стадия:
d3dDevice.SetTextureStageState 4, D3DTSS_COLOROP, D3DTOP_BLENDCURRENTALPHA d3dDevice.SetTextureStageState 4, D3DTSS_COLORARG1, D3DTA_TEMP d3dDevice.SetTextureStageState 4, D3DTSS_COLORARG2, D3DTA_CURRENT
Задача выполнена.
комментарии отсутствуют
авторизуйтесь