DirectX - Плюсы и минусы реализации рельефного текстурирования в DirectX 6.x
Каждый, кто когда-либо занимался трехмерным моделированием в каком либо из существующих программных продуктов для 3D графики, наверняка применял технику рельефного текстурирования (bump mapping) в своем проекте. Фактически, современные трехмерные игровые движки очень интенсивно используют данную технику, поэтому художники и дизайнеры не ограничены в возможностях и с легкостью применяют ее для придания объема клёпке на металлической поверхности, например, или для получения эффектной кирпичной стены. Иными словами, данный прием позволяет без особых усилий придать визуальный объем любой детали в сцене, и наоборот, любой объемной детали придать соответствующий внешний вид.
Последние достижения в области аппаратных и программных средств для потребителя, привели нас к такому моменту, что мы можем начать реализацию bump mapping в играх использующих DirectX API. Вкупе с procedural texturing techniques (программным текстурированием), с технологией AGP и возросшими вычислительными мощностями современных процессоров, становится возможным реализация очень внушительных визуальных эффектов.
Вы можете сказать, -"Я никогда не пользовался программами для трехмерного моделирования. Что такое bump mapping?" Хорошо, во-первых я должен пояснить, что способ, каким bumpmapping реализован в DirectX 6.0, это немного не то, что "спецы" называют "настоящим" методом рельефного текстурирования (bump mapping). Если вы спросите Джима Блинна (Jimm Blinn) - что такое bump mapping (а скорее всего он знает, что это такое, так как он сам это и изобрел), то он наверняка скажет, что это отличается от того, о чем я вам хочу рассказать. Блинн наверняка рассказал бы вам, что это метод получения визуальных выпуклостей и вогнутостей на объекте, находящемся в трехмерном пространстве без изменения геометрической формы самого объекта. Это получается путем изменения яркости каждого пикселя в соответствии с текстурой, применяемой в качестве карты выпуклостей и вогнутостей. В процессе прорисовки каждого пикселя конкретного полигона, происходит "подсматривание" на текстуру, используемую в качестве карты рельефа (aka bump map). Значения, полученные в результате подсматривания, используются для соответствующего изменения нормалей к визуализируемому пикселю. Полученная таким образом выпукло - вогнутая нормаль используется в дальнейших просчетах уровня цветности и освещенности - и в результате мы получаем поверхность такой, как будто на ней есть выпуклости и впадины.
Неплохо звучит, правда? Ну, а теперь, вы наверняка хотите узнать, как же это реализовать в DirectX 6.0. Хорошо, но сначала разрешите вас предупредить, что вы вряд ли сможете написать игровой или иной движок просто используя то определение, что было дано выше. Маловероятно, что даже Блинн или любой другой "профи" смог бы реализовать это сейчас. И этому есть ряд причин. Первое, это то, что современное 3D "железо" не способно производить по-пиксельные вычисления освещенности. Вместо этого оно производит затенение по Гуро (Gouraud shading), где цвета интерполируются от значений, полученных в процессе просчета вершин (vertex). Во-вторых, даже если бы железо и могло реализовать bump mapping, вы все равно не смогли бы снабдить API необходимой информацией о нормалях и уровне освещенности. Ситуация очень знакома: "Если курица появилась из яйца, то откуда взялось яйцо?", здесь мы получаем нечто подобное: "Если железо не может производить по-пиксельное вычисление освещенности объекта (даже в случае известного положения источников света), то оно тем более не сможет использовать bump mapping для изменения этой освещенности.
Что ж, раз мы не можем использовать "букварь" для реализации рельефного текстурирования на современном железе в среде DirectX 6.0, то нам надо искать какие то другие пути. На настоящий момент их два. Первый - это использование много текстурного или много проходного рендеринга (процесса визуализации) для реализации эффекта выдавливания (embossed effect) в процессе bump mapping, и второй это рельефное текстурирование для получения эффектов окружающей среды (environment-map bump mapping). Именно этот метод и реализован в DirectX 6.0 API.
Эффект выдавливания (Embossing)
Использование техники получения эффекта выдавливания или эффекта чеканки, это очень удобный способ придать дополнительный объем объекту не усложняя его геометрию. При его применении мы конечно не сможем добиться тех результатов, которые можем получить при использовании bump mapping в DirectX 6, но все же у него есть одно важное преимущество - этот метод реализуем на аппаратном уровне современных 3D акселераторов. Техника выдавливания (embossing) использует карту высот для получения эффекта чеканки в виде выпуклостей на поверхности объекта путем затенения и осветления определенных участков поверхности таким образм, чтобы создавалось впечатление, что под воздействием света, выпуклости отбрасывают тени (Рисунок 1). Существует множество способов реализации данного эффекта, но один из них, который может применятся на большинстве акселераторов (имеются ввиду даже самые простые, с поддержкой наложения только одной текстуры за проход и с минимумом функций alpha blending) реализуется на аппаратном уровне в три прохода.
Вот как он работает. Сначала вы должны подготовить карту высот, это может быть растровый файл изображения подготовленный художником или сгенерированный программным методом в отделных случаях. Допустим, в случае с кирпичной стеной, для получения имитации отверстий для артиллерии, нам надо изобразить самые темные участки, они то и будут отверстиями. Эта карта высот должна иметь цветовую интенсивность в дипазоне 0-0,5 (где 1,0 это наиболее интенсивные (яркие) участки в значениях RGB 255, 255, 255). Далее производим копию данной карты, но только теперь ее интенсивность должна быть эквивалентна 0,5 минус значение интенсивности оригинала, т.е. получаем негатив. Данная операция нужна для совместимости со всеми типами существующих акселераторов. (Конечно, вы можете подготовить этот негатив заранее, а не заставлять процессор заниматься изготовлением негатива карты, но тогда в 2 раза вырастут требования по количеству необходимой памяти для хранения текстур).
При первом проходе, ваш движок должен отрендерить полигон, используя в качестве текстуры вашу карту высот. (Рисунок 1а). Сдвинуть UV координаты негатива карты таким образом, чтобы текстура была направлена в сторону света (Рисунок1б), причем в соответствии с величиной угла между вектором света и нормалью к каждой из вершин (vertex). В случае направленного источника света, задача упрощается и можно использовать только одну нормаль ко всему полигону, а вот в случае точечного или не направленного источника света мы должны сделать различное смещение для каждой из вершин. Угол, на который должна быть сдвинут текстура зависить от типа и рисунка текстуры. Если ваша текстура предусматривает прорисовку крутых уклонов, то тогда излишнее смещение может разрушить весь визуальный эффект, в случае пологих наклонов недостаточное смещение будет портить картину.
При втором проходе, необходимо обеспечить смещение UV координат треугольника в сторону источника света, и произвести его рендеринг используя в качестве текстуры негативную карту высот (Рисунок 1б) и произвести аддитивное альфа смешение (alpha blending) с результатом первого прохода. В этот момент кадровый буфер (framebuffer) акселератора будет содержать значения от 0 до 1, где 0,5 соответствует отсутствию какой-либо визульной деформации поверхности, пиксели со значением менее 0,5 будут соответствовать наклонам с теневой стороны, а со значением более 0,5 - возвышениям в сторону света. Результатом будет текстура с оттенками серого, с тенями и высветленными участками (Рисунок1с).
При третьем, последем проходе, мы рендерим треугольник, накладывая базовую текстуру (Рисунок 1д), а затем модулируем результатом второго прохода в режиме Х2. Этот режим, в отличие отобычного Х1 не позволит нам получить треугольник более темным, чем он есть на самом деле, так как по результатам второго прохода все гладкие участки имеют интенсивность 0,5 и режим Х2 присвоит им текущую интесивность базовой текстуры с коэффициентом 1 (0,5х2=1). Еще одна особенность этого режима, это то, что участки с интенсивностью менее 0,5 будут выводиться с понижением яркости, а на участках интенсивнее 0,5 (т.е. поверхности обращенные к свету) будет увеличена контрасность изображения, что дает имитацию "бликов". (Рисунок 1е).
Величина смещения UV координат во время втрого прохода зависит от вашего конкретного случая и нет жестких правил определяющих ее. Карты высот с резкими переходами могут вызвать визуальные дефекты, если смещение было излишним, с другой стороны эффект выдавливания (чеканки) может быть не столь ощутим, если на мягких переходах высот будет применено недостаточное смещение. Правильная величина смещения выбирается исходя из типа карты и ее рисунка, а так же желания иметь более подчеркнутый или, наоборот, утонченный эффект на объекте.
Смещение UV координат может быть однократным, в случае направленного источника света, или многократным (просчитанным для каждой вершины участка поверхности) в случае применения ненаправленных источников света и их близком расположении к объекту. Для более реалистичной картины вы можете выбрать многократное смещение. (В исходных кодах программы, приложения к этой статье, вы сможете выбрать между этими двумя способами).
Чтобы улучшить производительность приложений, использующих данный метод, можно сократить время его полного исполнения до двух проходов, в случае применения акселераторов, поддерживающих мультитекстурирование (3Dfx Voodoo2, ATI Rage 128, 3DlabsPermedia3, Matrox G-400, и Nvidia Riva TNT). Но, к сожалению, их аппаратная реализация этого метода отличается друг от друга, и результаты будут не всегда одинаковыми. И если вы не поленитесь написать код реализации метода для каждого акселератора в отдельности, то заметное ускорение быстродействия будет достойной компенсацией за это.
Несмотря на то, что метод выдавливания предсказуем и легко реализуется, он все же далеко не универсален. Это просто трюк с осветлением и затенением. Он не позволяет управлять такими вещами, как отражение и манипуляции с картой окружающей обстановки сцены (environment map). Также он работает только в случае применения монохромного (чисто белый цвет) освещения. Конечно, можно реализовать воздействие на поверхность цветного источника света, но для этого нам нужно задействовать четвертый проход. Ну, и в конце, программисту необходимо очень четко отслеживать величину сдвига негатива карты высот во втором проходе, чтобы не получить в результате полигон, похожий на лист изрезанный ножницами.
Пример исходного кода, использует метод выдавливания как один из способов реализации рельефного текструрирования в DirectX 6. Я не стал дополнительно включать в примеры ряд других однопроходных методов реализации метода, так как на целом ряде сайтов производителей 3D акселераторов, вы можете найти готовые варианты для конкретного аппаратного обеспечения.
Пример исходного кода Listing 1.
Данный пример демонстрирует технику выдавливания. С целью получения более короткого кода, некоторые из переменных описывающих статусы рендеринга и текстур (в оригинале renderstates и texturestagestates) опущены, часть из которых, особенно в случаях касающихся фильтрации, вы можете дополнительно посмотреть в исходных кодах программы "Emboss"
// Rendering Pass #1 lpD3DDevice->SetTexture( 0, lpBumpTexture ); //ignore FB color, just use the output of the SRC (the triangle we are about to render) lpD3DDevice->SetRenderState( D3DRENDERSTATE_ALPHABLENDENABLE, FALSE); lpD3DDevice->SetRenderState( D3DRENDERSTATE_SRCBLEND, D3DBLEND_ONE); lpD3DDevice->SetRenderState( D3DRENDERSTATE_DESTBLEND, D3DBLEND_ZERO); //set up the bump texture lpD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); lpD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); lpD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); lpD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE ); lpD3DDevice->SetTextureStageState( 0, D3DTSS_ADDRESS, D3DTADDRESS_WRAP ); lpD3DDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_DISABLE ); lpD3DDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_DISABLE ); hr = lpD3DDevice->DrawPrimitive( D3DPT_TRIANGLELIST, D3DFVF_TLVERTEX, pVertices, 6, D3DDP_DONOTLIGHT); // Rendering Pass #2 lpD3DDevice->SetTexture( 0, lpInvBumpTextureRaw ); //using the D3DBLEND_ONE for both SRC and DEST blend factors results in an // additive blend. FB = (Src * 1) + (Dest * 1) lpD3DDevice->SetRenderState( D3DRENDERSTATE_ALPHABLENDENABLE, TRUE ); lpD3DDevice->SetRenderState( D3DRENDERSTATE_SRCBLEND, D3DBLEND_ONE ); lpD3DDevice->SetRenderState( D3DRENDERSTATE_DESTBLEND, D3DBLEND_ONE ); // inverted bump texture lpD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); lpD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); lpD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); lpD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE ); hr = lpD3DDevice->DrawPrimitive( D3DPT_TRIANGLELIST, D3DFVF_TLVERTEX, pVertices, 6, D3DDP_DONOTLIGHT); // Rendering Pass #3 lpD3DDevice->SetTexture( 0, lpBaseTexture ); //The default blending mode with the frame buffer // (D3DRENDERSTATE_TEXTUREMAPBLEND) is modulate // so by picking the src and dst blend factors below, we get: // Framebuffer color = (SrcColor*DestColor) + (DestColor*SrcColor), or 2*Dst*Src // this is why the heightmap needs to be from 0 to 0.5 only, and not above 0.5 lpD3DDevice->SetRenderState( D3DRENDERSTATE_ALPHABLENDENABLE, TRUE ); lpD3DDevice->SetRenderState( D3DRENDERSTATE_SRCBLEND, D3DBLEND_DESTCOLOR ); lpD3DDevice->SetRenderState( D3DRENDERSTATE_DESTBLEND, D3DBLEND_SRCCOLOR ); // base texture lpD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE ); lpD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); lpD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); lpD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE ); lpD3DDevice->SetTextureStageState( 0, D3DTSS_ADDRESS, D3DTADDRESS_WRAP ); hr = lpD3DDevice->DrawPrimitive( D3DPT_TRIANGLELIST, D3DFVF_TLVERTEX, pVertices, 6, D3DDP_DONOTLIGHT);
DirectX 6 Environment-Map Bump Mapping
Еще один способ реализации рельефного текстурирования в DirectX 6 - это техника текстурирования с использованием текстуры (карты) окружающей обстановки 3D сцены (Environment-Map Bump Mapping). Однако данный метод может быть реализовани только при использовани акселераторов, которые поддерживают его аппаратно. В ближайшие месяцы мы вряд ли сможем увидеть достаточное количество акселераторов, которые будут поддерживать данную технику текстурирования. На данный момент известно, что данную технологию будут поддерживать Matrox G400 и 3Dlabs Permedia3. Остальные производители вероятнее последуют их примеру позже. (Кстати, обзор Matrox G400 с картинками отображающими Environment-Map Bump Mapping, вы можете посмотреть здесь. Прим. переводчика). Используя данную технологию, можно получить множество впечатляющих эффектов, но все же давайте сначала разберемся, что и как работает и способы её примененя.
В данном случае DirectX bump mapping будет использовать две текстуры (не считая базовой текстуры объекта).
Первая текстура - текстура с информацией об уклонах (ее еще можно назвать картой изменений высот, заметьте, что при выдавливании применялась просто карта высот). Эта текстура вырабатывается на основе карты высот путем анализа прилежащих друг к другу текселей. Акселератор, поддерживающий данную технологию, использует эту информацию для изменения координат текселя в соответсвии с информацией, поставляемой второй текстурой. (Вспомните, в противоположность этому, техника выдавливания использовала подобную информацию для изменения положения нормалей к полигону).
Вторая текстура это карта окружающей среды, которая накладывается на исходный полигон, но уже с измененными UV координатами. Рисунок 2б окончательный вариант того, что сказано, где environment map это сцена над водой, отрендеренная в текстуру ссоответсвующим пиксельным форматом.
Если изготовить environment map в виде яркостной карты, расположив источники света градиентно и по кругу (что то похожее на эффект, проявляющийся при использовании инструмента "баллончик с краской" из графических программ), то мы получим результат похожий на выдавливание, но теперь мы не ограничены в количестве применяемых источников света, плюс наше освещение не обязательно должно быть монохромным.
Использование данной технологии производится следующим образом. Сначала, назначте D3Dtexture2surfaces обе текстуры (базовую и environment) и заполните поверхность обычным образом. Назначте D3Dtexture2карту рельефа (картуизменений высот, если она небыла подготовлена заранее, сгенерите ее на основанни существующей карты высот) и установите нужные значения битовых флагов перед производством операции DirectDrawSurface Creation. Создание карты измененй высот - задача не тяжелая и включает в себя операции по сравнению рядом стоящих пикселей. Эти операции подробно описаны в Примере 2. После этого назначьте эту рельефную карту исходной поверхности.
Переменной D3DtextureStageState определяется стадия обработки текстур.
- Stage 0 - Settexture to base texture (как обычно)
- Stage 1 - Set texture to bump map
- Stage 2 - Set texture to environment map
На первой стадии можно дополнительно менять опции рендеринга установкой следующих переменных
D3DTSS_BUMPENVMATxx (где xx - 00, 01, 10 или 11). Определяет размерность рельефной матрицы, значение 11 устаналивает 2х2 матрицу.
D3DTSS_BUMPENVSCALE и D3DTSS_BUMPENVOFFSET. Эти две переменные меняют эффект от использования яркостной компоненты в рельефной карте, если она в ней используется. Аспект использования яркостной компоненты описан ниже.
Последнее, что остается сделать, это произвести рендеринг треугольников в каждом кадре. Пример 2 это отрывок кода, демонстрирующий реализацию описанной технологии на практике.
Пример исходного кода с реализацией Environment Map Bump Mapping
//Pseudo code for creating a height differential map from a height map //you would also have to handle a special case for the last //row and column of the bitmap for (y=0;ydwTextureOpCaps & D3DTEXOPCAPS_BUMPENVMAP ) DoSomething();//this device supports bump mapping Else DontDoAnything();//this device does not support bump mapping //Setup of the bump map surface (assumes a direct draw surface descripton, // ddsd, structure // is set up already, and that pddsurface is a pointer to a DirectDrawSurface) ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_SYSTEMMEMORY; ddsd.ddsCaps.dwCaps2 = 0L; // Pix format for DuDv88 bumpmap, could also do DuDvL556 or 888 ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); ddsd.ddpfPixelFormat.dwFlags = DDPF_BUMPDUDV; ddsd.ddpfPixelFormat.dwBumpBitCount = 16; ddsd.ddpfPixelFormat.dwBumpDuBitMask = 0x000000ff; ddsd.ddpfPixelFormat.dwBumpDvBitMask = 0x0000ff00; ddsd.ddpfPixelFormat.dwBumpLuminanceBitMask = 0x00000000; // Create the bumpmap's surface and texture objects if( FAILED( pDD->CreateSurface( &ddsd, &(pddsurface), NULL ) ) ) return NULL; if( FAILED(pddsurface ->QueryInterface( IID_IDirect3DTexture2, (VOID**)&pd3dtexBumpTexture ) ) ) //Set the Base Texture lpD3DDevice->SetTexure(0, lpBaseTexture); lpD3DDevice->SetTexureStageState(0, D3DTSS_TEXCOORDINDEX, 0); lpD3DDevice->SetTexureStageState (0, D3DTSS_COLOROP, D3DTOP_MODULATE); lpD3DDevice->SetTexureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE); lpD3DDevice->SetTexureStageState (0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); lpD3DDevice->SetTexureStageState (0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); lpD3DDevice->SetTexureStageState (0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); // Set up the Bump Texture lpD3DDevice->SetTexure(1, lpBumpTexture); lpD3DDevice->SetTexureStageState... lpD3DDevice->SetTexureStageState (1, D3DTSS_TEXCOORDINDEX, 0); lpD3DDevice->SetTexureStageState (1, D3DTSS_COLOROP, D3DTOP_BUMPENVMAP); lpD3DDevice->SetTexureStageState (1, D3DTSS_COLORARG1, D3DTA_TEXTURE); lpD3DDevice->SetTexureStageState (1, D3DTSS_COLORARG2, D3DTA_CURRENT); lpD3DDevice->SetTexureStageState (1, D3DTSS_BUMPENVMAT00, DWORD(1.0f)); lpD3DDevice->SetTexureStageState (1, D3DTSS_BUMPENVMAT01, DWORD(0.0f)); lpD3DDevice->SetTexureStageState (1, D3DTSS_BUMPENVMAT10, DWORD(0.0f)); lpD3DDevice->SetTexureStageState (1, D3DTSS_BUMPENVMAT11, DWORD(1.0f)); lpD3DDevice->SetTexureStageState (1, D3DTSS_BUMPENVLSCALE, DWORD(1.0f)); lpD3DDevice->SetTexureStageState (1, D3DTSS_BUMPENVLOFFSET, DWORD(0.0f)); // Set up the Environment map texture lpD3DDevice->SetTexure(2, lpEnvMapTexture); lpD3DDevice->SetTexureStageState (2, D3DTSS_ADDRESS, D3DTADDRESS_CLAMP); lpD3DDevice->SetTexureStageState (2, D3DTSS_TEXCOORDINDEX, 1); lpD3DDevice->SetTexureStageState (2, D3DTSS_COLOROP, D3DTOP_ADD); lpD3DDevice->SetTexureStageState (2, D3DTSS_COLORARG1, D3DTA_TEXTURE); lpD3DDevice->SetTexureStageState (2, D3DTSS_COLORARG2, D3DTA_CURRENT);
Рельефная текстура (bump map texture) может поддерживать несколько новых пискельных форматов и все эти форматы являются нововведением в DirectX 6.0. Пиксельные форматы устанавливаются посредством флагов DDPF_BUMPDUDV и DDPF_BUMPLUMINANCE в период подсчета и создания поверхностей. Эти флаги позволяют описать три пиксельных формата: два размерностью 16 bits-per-pixel (DuDv 88 and 556DuDvL) и один 32 bits-per-pixel формат (888DuDvL).
Рельефная матрица 2х2 предназначена для вращения и масштабирования UV координат по следующей формуле:
[U',V'] = [U,V] + [bump(u),bump(v)] * [ M00 M01 ] [ M10 M11 ]
откуда вытекает
U' = U + (bump(u)*M00 + bump(v)*M10) V' = V + (bump(u)*M01 + bump(v)*M11) где
U и V - координаты текущего пикселя подлежащего визуализации, bump(U) и bump(V) - значения полученные с рельефной карты (bump map) в точке с координатами U и V; U' и V' - результирующие координаты используемые для извлечения текселей из environment map.
Когда в качестве рельефной карты используется текстура в пиксельном формате с яркостной компонентой, то переменные D3DTSS_BUMPENVSCALE и D3DTSS_BUMPENVOFFSET также беруться в расчет. После перемножения матрицы в примере выше, цвет пикселя меняется следующим образом:
L' = bumpmap(L) * BumpEnvScale + BumpEnvOffset Stage output = EnvironmentMap(U',V') * L' * Polygon diffuse color
Применение эффектов рельефного текстурирования
Самое интересное начинается тогда, когда мы видим результаты того, где было использовано рельефное текстурирование. Некоторые стадии, в процессе применения bump mapping, могут быть анимированы, что позоволят реализовать некоторые наиболее удивительнейшие эффекты. Среди них можно перечислить следующие:
Генерация карт для окружающей среды (environment maps) на "лету", в процессе выполнения программы. Этой картой может быть предварительно отвизуализированный кадр или карта с композицией источников света (если их несколько) или карта (текстура) полученная программным методом путем генерации по одному из шумовых алгоритмов (например кудрявые, вихрящиеся облака). Рисунок 2б демонстрирует применение этой технологии (для получения этого скриншота использовалась программа - приложение к данной статье) -- это при реальный пример Environment Map Bump Mapping.
Программная генерация и модификация рельефной карты (bump map). Для этого может использоваться любая синусоидальная функкция или эффект имитирующий появление отверстий на поверхности от попадющих пуль -- и это все комбинируется с рельефной картой в процессе выполнения программы ("на лету"). Приложение демонстрирует применение подобных эффектов. Волны на поверхности воды это программно генерированная рельефная карта, пользователь может щелкнуть мышкой на поверхности воды и протащить мышку по поверхности, что бы получить круги на воде следующие за перемещающимся указателем мышки. Динамически меняющаяся рельефная карта, позволяет получить соответсвующее искажение поверхности в окончательном изображении.
Динамическое изменение значений BUMPENVMATхх позоволяет производить вращение и масштабирование текселей составляющих карту (текстуру) окружающей среды (environment map). Это проявляется в виде появления дополнительной "бугристости" поверхности. Например, персонаж, вдруг резко может покрытся "гусиной кожей" в каком либо из игровых моментов. И этот эффект достигается изменением всего двух значений в матрице (и всегo за два вызова процедур определяющих RenderState).
Динамическое изменение значений BUMPENVOFFSET и BUMPENVSCALE позволяет изменить или анимировать цветовую яркость "выдавленного" текселя. Данным премом можно воспользоватся для вращения подсвеченной рельефной поверхности вокруг объекта.
Рельефные текстуры и текстуры окружающей среды можно подвергнуть дополнительной обработке посредством filtering или MIP-map tecnique. Это можно использовать для повышения детализации объекта c bump mapping, в тот момент когда персонаж подходит к нему достаточно близко, что позволит сделать рельефность более очевидной.
Рисунок 2а. Сцена воспроизведенная на текстуру, которая будет использована в качестве environment map для поверхности для воды.
Рисунок 2б.Сцена воспроизведенная на текстуру. Environment map искажена методом DirectX Bump Mapping с программной генерацией рельефной карты.
Рисунок 2с. Аналогия 2б, только визуалиция в режиме wireframe
Теперь, когда вы знаете как реализовать Bump Mapping в DirectX 6.0 давайте поговорим о том, что еще возможно реализовать ( я сказал возможно потому, что я сам, еще это не пробовал) используя подобную технологию. В качестве приложения к этой статье, я включил две законченных программы, которые вы можете скачать и попробовать. Одна из них демонстрирует технику выдавливания (emboss.zip, 350 Kb), другая демонстрирует технику bump mapping для создания отражения на слабо мерцающей поверхности воды (waterdemo.zip, 870 Kb)
ТЕХНИКА ВЫДАВЛИВАНИЯ (EMBOSSING). Используя пример реализации техники выдавливания, описанный ранее в этой статье вы можете создать эффект воздействия точечного источника света на объект используя карту с внедренным источником. Однако, в противоположность технике embossing с использованием нескольких текстур, вы можете вместо них применить карты с освещением в различных цветовых оттенках. Если вы будете использовать данную технику совместно с просчетом освещения вершин (vertex), то скорее всего вам придется поэкспериментировать с масштабом и затемненными участками световой карты, для того чтобы полученный эффект вписывался в общую картину освещения вашей трехмерной сцены.
ОТРАЖЕНИЕ НА ВОДЕ (WATER REFLECTION). Динамически, программно изменяемая по синусоиде рельефная карта водной поверхности позволяет достичь изумительных натуральных эффектов без дополнительного усложнения общей геометрии. (Рисунок 2).
ИСКАЖЕНИЯ В ВОДНОЙ СРЕДЕ (WATER DISTORTION). Эффект подобный тому, что реализован в software режиме Quake (где целый кадровый буфер подвергался синусоидальной анимации). Сначала вы должны провести рендеринг трехмерной сцены на поверхность текстуры. Далее, используя ее в качестве карты окружающей среды, просчетом треугольников заполненить кадровый буфер и сразу подвергнуть всю сцену рельфеному текстурированию по синусоидальному образцу для получения необходимого искажения. Таким образом мы подчиним весь видеовыход волновому искажению производимому водной средой.
КРИВЫЕ ЗЕРКАЛА И ЛИНЗЫ (CIRCUS MIRRORS AND LENS EFFECTS). Разместив поверхность текстуры в поле зрения, отвизуализируйте сцену (или только часть ее) используя в качестве точки наблюдения место где предполагается находится ЗЕРКАЛУ. Затем используйте полученное изображение в качестве текстуры для зеркала и примените на него технику рельефного текстурирования что бы изменить отражение даваемое зеркалом в соответствии с рельефной картой которую вы примените. Таким образом вы можете создать зеркало производящее отражение окружающей действительности по типу тех, что вы вы видели в "Комнатах смеха" и цирковых площадках.
ТЕЛЕПОРТЫ ПОДОБНЫЕ ТЕМ, ЧТО ВЫ МОГЛИ ВИДЕТЬ В ИГРЕ "SUPER MARIO 64". Анимированная текстура для рельефной карты, может в повторить эффект реализованный в игре Super Mario 64, когда портал или телепорт дрожал после того как Марио прыгал в него. В Nintendo реализация этого эффекта была проведена геометрическим образом.
РЕАЛИЗАЦИЯ ПЕРЕХОДОВ МЕЖДУ УРОВНЯМИ ДЕТАЛИЗАЦИИ. Модели с низким уровнем детализации (LOD) могут использовать рельефное текстурирование для того, что бы скрыть свою грубоватость ввиду малого количества использованных полигонов. Например, если дом имеет дверь с дверной ручкой и окно с подоконником которые должны выделятся наружу, то в случае нахождения игрока достаточно далеко от дома, данные детали с низким LOD будут иметь кубическую форму. Если применить на них рельефное текстурирование, то эти мелкие детали будут иметь освещенные и затененные участки, имитируя натуральную объемность. Когда же игрок подойдет к ним достаточно близко, то можно будет увеличить LOD и отключить bump mapping для этих деталей.
ЭФФЕКТ РАСТВОРЕНИЯ (ИСЧЕЗНОВЕНИЯ) ПУЛЕВЫХ ОТВЕРСТИЙ. В фильме Terminator 2 мы могли видеть, как отверстия от попавших пуль на серебристом теле Терминатора постепенно затягиваются и исчезают, как бы растворяясь. Этот эффект может быть достигнут применением предварительно расчитанной рельефной текстуры пулевого отверстия на поверхность и дальнейшего постепенного динамического уменьшения значения BUMPENVMATхх до 0.
ИСКАЖЕНИЯ ВЫЗЫВАЕМЫЕ ТЕПЛЫМ ВОЗДУХОМ ПОДНИМАЮЩИМСЯ НАД ЗЕМЛЕЙ. Этот эффект достигается путем аналогичным для искажений в водной среде, но только применительно к определенной части сцены. Это может быть как раз тем примером, где вы захотите использовать для текстуры пиксельный формат DuDvL, где яркостная составляющая на рельефной карте будет постепенно уменьшатся к верху, создавая затухание эффекта в верхней части сцены.
ЭФФЕКТЫ РАЗВИВАЮЩИХСЯ НА ВЕТРУ ФЛАГОВ, КОЛЕБАНИЙ ШТОР/ОДЕЖДЫ. Программно вырабатывая анимированную рельефную карту или просто динамически меняя UV координаты рельефной карты можно заставить одежду персонажа развиваться на ветру.
ДЕТАЛЬНЫЕ ТЕКСТУРЫ ЗЕМЛИ ДЛЯ АВИАСИМУЛЯТОРОВ. Идея проста -- изменение уровня детализации видимой поверхности в зависимости от расстояния до нее. Допустим если вы летите на самолете на высоте 1000 футов, то земля под вами будет похожа на стеганое одеяло, когда же вы снизитесь до высоты в 10 футов (как раз за мгновение до того как врежетесь в землю) - детализация увеличится до такой степени, что вы в состоянии разглядеть колосья пшеницы. Или еще -- вы можете применить bump mapping для имитации 20 футовых волн на воде, летя на высоте в 1000 футов, а когда вы снизитесь до 10 футовой высоты, можно дополнительно добавить эффекты дрожжания и капель воды на стекле.
Реализация описанных технологий и методов
В зависимости от конечных целей и ограничений которые присутствуют в вашей игре некоторые из перечисленных рекомендаций будут работать, а некоторые возможно нет. Все зависит от того какой эффект вы хотите получить, в чем вы хотите выйграть и насколько ваш движок окажется масштабируемым с точки зрения широкого диапазаона используемого потребителями аппаратного обеспечения. Однако я надеюсь, что реализация рельефного текстурирования посредством DirectX 6.0 API, доступная на некотрых акселераторах, появившихся в этом году, даст солидный толчок разработчикам программного обеспечения для применения в своих продуктах целого ряда потрясающих эффектов. Особенно это касается игр.
Также, постарайтесь не забыть, для чего вообще разрабатывалась технология bump mapping изначально: высокая детализация без добавления дополнительных полигонов. Технология является особо желательной там, где разработчик планирует использование программного продукта на целом парке разнотипных по мощности машин или, где просто не используется возможность выбора уровня детализации для моделей и объектов. (Например, строения на уровнях целого ряда 3D шутеров). Вы можете использовать оба метода (Embossingи Environment Map - Bump Mapping) для придания более реалистичного вида кирпичным стенам, изрезанным мраморным колоннам и заклепкам на старых железных лестницах.
Конечно, решаясь на применение технологии рельефного текстурирования, вы должны обязательно взвесить все плюсы (получение дополнительных визуальных эффектов без усложнения геометрии) и минусы (необходимость прописывать участки кода для различного аппаратного обеспечения и более высокие требования к акселератору по способности заполнения (fillrate) во время рендеринга), и только после этого принять решение. Последние графические акселераторы выпущенные на массовый рынок, имеют достаточные мощности по части заполнения пикселями треугольников, поэтому один из минусов в ближайшем будующем уже не будет иметь такого значительного веса. Также, стоит учитывать тот факт, что способность акселераторов накладывать несколько текстур за один проход, дает значительный выигрыш в производительности, и программистам скоро не надо будет изыскивать многопроходные решения для наложения нескольких текстур, которые в основном расчитаны на старый парк акселераторов, обладающих к тому же низкой скоростью заполнения.
Разработчики, которые пишут программы для PC, сталкиваются с необходимостью масштабировать свою программу под компьютеры с различной мощностью. К счастью, оба описанных в данной статье подхода к реализации эффектов рельефности на объектах, помогают в масштабировании ваших приложений к различному аппаратному обеспечению без особых трудозатрат. Вам просто достаточно сделать так, чтобы bumpmapping отключалось при обнаружении более медленного процессора на компьютере, и наоборот, включалась на полную мощность, в случае машины высокого класса. Так что, Bump Mapping сделает вам графику в вашем следующе мпроекте, ой, пардон, обещает сделать...
и публицист по процессорным
технологиям Intel's Developer Relations Group.
Ориганальная статья опубликована на сайте Gamasutra.