четверг, 23 июня 2011 г.

Создание сложной процедурной поверхности средствами GPU (Перевод) Часть 4

 

Когда мы используем bump mapping (карты выпуклостей) с трехпроекционным текстурированием, мы должны быть уверены, что базис касательных создан из нормалей независимо для каждой проекции. Например, для x-проекции, в начальной стадии мировой базис касательных может быть векторами <0, 1, 0> и <0, 0, 1>. Однако, мы можем сделать и лучше. К счастью, очень просто получить базис касательных из нормалей. Здесь достаточен 90-градусный поворот векторов величины двух компонент и смена знака одной из них. Например, для x-проекции, вы должны использовать векторы <normal.z, normal.y, -normal.x> и <normal.y, -normal.x, normal.z>. Однако, надо иметь ввиду, что позиция отрицательного знака влияет на данные в картах выпуклости.
Трехпроекционное текстурирование может быть выполнено за один проход, как показано в коде пиксельного шейдера в Примере 1-3. Пример выбирает значения цвета и векторов выпуклости для каждой из трех плоских проекций и затем смешивает их, основываясь на векторе нормали. В итоге, смешанные векторы выпуклости накладываются на вершинно-интерполированную нормаль для получения выпуклости нормали.
Еще один способ текстурирования земли - сопоставление конкретных значений высот конкретным цветам. С использование видеокарты, идет трансляция в1D текстуру, которая использует мировую координату y —или, лучше использовать 2D текстуру. В этой текстуре, пусть цветовая схема меняется вдоль оси u, а ось v представляет высоту. При просмотре, нужно использовать низкочастотный шум для управления координатой u, то есть цветовая схема будет менять в зависимости от перемещения смотрящего.
Example 1-3. Текстурная плоская проекция
  1. // Определить веса смешивания трех проекций.
  2. // N_orig вершинно-интерполированный вектор нормали.
  3. float3 blend_weights = abs( N_orig.xyz ); // Уплотняем зону смешивания:
  4. blend_weights = (blend_weights - 0.2) * 7;
  5. blend_weights = max(blend_weights, 0); // Сумма весом д.б. 1.0 (очень важно!)
  6. blend_weights /= (blend_weights.x + blend_weights.y + blend_weights.z ).xxx; 
  7. // Определим цвет и вектор выпуклости для каждой из 3
  8. // проекций, смешаем, и сохраним результаты в два
  9. // вектора:
  10. float4 blended_color; // .w хранит особ. значение
  11. float3 blended_bump_vec;
  12. {
  13. // Считаем координаты UV cдля каждой из трех плоских проекций.
  14. // tex_scale (default ~ 1.0) определяет величину текстуры.
  15. float2 coord1 = v2f.wsCoord.yz * tex_scale;
  16. float2 coord2 = v2f.wsCoord.zx * tex_scale;
  17. float2 coord3 = v2f.wsCoord.xy * tex_scale;
  18. // Здесь можно применить условное смещение текстур.
  19. //if (blend_weights.x > 0) coord1 = . . .
  20. //if (blend_weights.y > 0) coord2 = . . .
  21. //if (blend_weights.z > 0) coord3 = . . .
  22. // выбираем цвета для каждой проекции, в этих UV координатах.
  23. float4 col1 = colorTex1.Sample(coord1);
  24. float4 col2 = colorTex2.Sample(coord2);
  25. float4 col3 = colorTex3.Sample(coord3);
  26. // Выбираем карты выпуклости, и создаем вектора выпуклости.
  27. // (Замечание: используем упрощенный базис касательных.)
  28. float2 bumpFetch1 = bumpTex1.Sample(coord1).xy - 0.5;
  29. float2 bumpFetch2 = bumpTex2.Sample(coord2).xy - 0.5;
  30. float2 bumpFetch3 = bumpTex3.Sample(coord3).xy - 0.5;
  31. float3 bump1 = float3(0, bumpFetch1.x, bumpFetch1.y);
  32. float3 bump2 = float3(bumpFetch2.y, 0, bumpFetch2.x);
  33. float3 bump3 = float3(bumpFetch3.x, bumpFetch3.y, 0);
  34. // В итоге, смешиваем результаты для плоских проекций.
  35. blended_color = col1.xyzw * blend_weights.xxxx +
  36.                 col2.xyzw * blend_weights.yyyy +
  37.                 col3.xyzw * blend_weights.zzzz;
  38. blended_bump_vec = bump1.xyz * blend_weights.xxx +
  39.                    bump2.xyz * blend_weights.yyy +
  40.                    bump3.xyz * blend_weights.zzz;
  41. }
  42. // Применение вектора выпуклости к вершинно-интерполируемой нормали
  43. float3 N_for_lighting = normalize(N_orig + blended_bump); 

Для того, чтобы сделать картину более интересной, сделаем низкочастотный шум слабоменяющимся вдоль осей x и z но быстро вдоль y—это сделает цветовую схему зависимой от высоты. Также забавно добавить малую величину координаты y вектора нормали к компоненте u; это помогает избежать горизонтализации мира. Рисунки 1-24 и 1-25 иллюстрируют эти приемы.
clip_image001
Рисунок 1-24
clip_image002
Figure 1-25 Текстура различного климата
Плоские 2D проекции и просмотр по высоте очень полезны для проецирование детальных, 2D текстур на поверхность. Однако, текстурирование может быть процедурным. Например, несколько октав шума—основанных на мировых координатах—могут быть использованы для возмущения векторов нормали, добавляя несколько новых октав, как показано в Примере 1-4 и на Рисунке 1-26.
clip_image003
Рисунок1-26 Текстурирование земля
Пример 1-4. Возмущение вектора нормали
  1. // возмущение вектора нормали несколькими октавами процедурно шума.
  2. float3 v = 0;
  3. v += noiseVol1.Sample(ws* 3.97)*1.00;
  4. v += noiseVol2.Sample(ws* 8.06)*0.50;
  5. v += noiseVol3.Sample(ws*15.96)*0.25;
  6. N = normalize(N + v);  

Базовый цвет поверхности также может быть получен процедурно. Например, текстура мрамора может быть получена деформацией мировых координат несколькими октавами низкочастотного шума, использующими(ws.y), как показано в Примере 1-5 и на Рисунке 1-27.
clip_image004
Рисунок 1-27 Земля с текстурой мрамора
Текстурная проекция и процедурное текстурирование – очень мощные инструменты, которые можно легко комбинировать для создания множества эффектов. Поодиночке, процедурное текстурирование дает меньшую детализацию, чем текстура (как фотография или нарисованная карта). С другой стороны, одиночное использование наложения текстуры выглядит очень повторяющееся. Однако, комбинирование этих способов решает эти проблемы и создает красивые и разнообразные ландшафты.
Пример 1-5. Создание текстуры мрамора
  1. // Используем искаженные мировые координаты для создания текстуры мрамора.
  2. float3 v = 0;
  3. v += noiseVol2.Sample(ws*0.47)*1.00;
  4. v += noiseVol3.Sample(ws*1.06)*0.50;
  5. v += noiseVol1.Sample(ws*1.96)*0.25;
  6. float3 ws_warped = ws + v;
  7. float is_marble = pow( saturate( sin(ws_warped.y)*1.1 ), 3.0) ;
  8. float3 marble_color = 1;
  9. blended_color = lerp(blended_color, marble_color, is_marble); 

1.6 Рассмотрение реалистичных приложений

1.6.1 Уровень детализации
Идеальная 3D сцена - сцена, в которой все полигоны имеют одинаковый размер на экране. На практике, это редко случается. Техника, описанная раннее создает мозаику, однородную в мировых координатах, но неоднородную в экранных. В результате, удаленные полигоны очень малы (менее пикселя), что плохо, и создает артефакты. Для решение проблемы, мы должны разделить блоки на три группы: близкие, средние и удаленные. Близкие блоки будут иметь полигоны нормального размера, средние – более большие полигоны (в мировых координатах). Наконец, удаленные блоки будут иметь наибольшие полигоны. Для выполнение этого, мы должны выбрать из основных схем: первая – наименее детализированные блоки (LOD) будут иметь меньше полигонов, вторая - LOD блоки будут занимать большее пространство.
Для первой схемы, все блоки занимают 1x1x1 в мировых координатах, при чем удаленных блоки будут иметь меньший размер внутренней сетки (163 или 83 вместо 323). К несчастью, эта схема число блоков разрастается очень быстро, что снижает производительность—для создания и отображения блоков.
Вторая схема работает лучше. Здесь все блоки имеют одинаковую внутреннюю сетку 323, но изменяется размер блоков в мировых координатах, основываясь на их удалении от наблюдателя. Ближайшие блоки будут иметь размер 1x1x1, более удаленные - 2x2x2 и самые удаленные - 4x4x4. Во время отрисовки, мы сначала создаем большие (удаленные) блоки, потом средние блоки, а затем меньшие (1x1x1). Из-за того, что количество блоков остается управляемым, эта схема более предпочтительна.
Переход части земли из одного LOD к другому происходит резко, что порождает артефакты. Простейший способ решение проблемы – рисовать оба LOD в ситуации перехода. Рисование менее качественного LOD вначале и плавный переход в более качественный LOD за короткий период времени. Однако, это хорошо работает только если значения z-буфера (глубина) каждого пикселя лучше согласуется с более качественным LOD блоком; иначе более качественный LOD блок не будет перекрывать менее качественный.
Поэтому, малое значение убывания значения буфера z (глубины) должно быть использовано вершинным шейдером, когда мы рисуем более качественный LOD блок. Лучше, если мы создадим менее качественный LOD блок используя убывание функции плотности. Это приближение "уменьшает" блоки и оно аналогично сжатию поверхности вдоль нормалей (это лучше, потому что не дает сжатия точек). В результате, более качественный LOD заключает в себе менее качественный LOD, что избавляет от проблем с наложением.
1.6.2 Коллизия и освещенность внешних объектов
Коллизии
В интерактивном игровом окружении существует множество движущихся объектов—такие как насекомые, птицы, ноги персонажа, поэтому надо определять и реагировать на коллизии с землей. "Умно" летающие создания должны время от времени испускать лучи (как драконы из демо "Каскады") чтобы следовать особенностям рельефа. Стелящиеся объекты—такие как виноград (скрытая особенность демо "Каскады"), пауки, или водопады—должны следовать поверхности. Брошенные объекты должны знать, когда они соприкоснутся с землей, то есть когда они должны прекратить движение например, копье, вонзающееся в землю), отскочить (как футбольный мяч), или определить какое-то событие.
Такие взаимодействие легко рассчитать, если движение объекта напрямую управляется с помощью GPU шейлера. Проще всего сделать вычисления геометрическим шейдером, где каждый маленький буфер, содержащий единичный элемент (вершину) для каждого двигающегося объекта пропускается через геометрический шейдер на каждом фрейме. Геометрический шейдер знает все о поверхности, функция плотности , которая может быть включена (при помощи #include) другими шейдерами. Геометрический шейдер может включать файл и использовать функцию, запрашивая когда надо, находится точка внутри или вне поверхности.
Для примера, если футбольный мяч проходит сквозь землю, геометрический шейдер должен проверить функцию плотности на предыдущем и на новом фрейме. Если мяч был в воздухе, а потом оказался внутри поверхности земли, значит точная точка соприкосновения может быть найдена при помощи интерполяции функции плотности и нахождения точки, где она обращается в ноль. Мы можем использовать итеративный подход, используя деление отрезка пополам. Когда мы нашли точку коллизии, мы можем посчитать градиент в этой точке (за шесть выборок). В итоге, зная скорость мяча и нормаль, мы можем посчитать направление отскока, и мы можем получить правильное новое положение мяча.
Освещение
Если мяч из предыдущего примера попадет в пещеру, наблюдатель ожидает затемнения, из-за уровня occlusion of ambient внутри пещеры (при условии, что он немагический). К счастью, это легко достигается ambient occlusion из центра объекта к каждому фрейму (если объект движется), также как и при генерации вершин поверхности.
Единственная разница – использование функции плотности вместо массива плотности. Использование функции плотности более медленное, но если лучи occlusion raysлучи испускаются для нескольких дюжин движений, видимых объектов на фрейм, это не так значительно.

1.7 Заключения

Мы предложили способ использования GPU для создания безграничного 3D ландшафта с приемлемой скоростью. Мы также показали, как текстурировать и освещать поверхность для отображения, построение разных схем детализации, описали взаимодействие внешних объектов с землей, плюс осветили эти объекты.
Мы поощряем использование и развитие этих подходов. Подход, описанный в этом разделе, возможно, станет новой парадигмой генерации земли средствами GPU. Очень много не исследовано из того, что возможно, но новые поколения чипов вселяют большие надежды.

1.8 Ссылки

Ebert, David S., F. Kenton Musgrave, Darwyn Peachey, Ken Perlin, and Steven Worley. 2003. Texturing & Modeling: A Procedural Approach, 3rd ed. Academic Press. Chapters 9–13 by F. Kenton Musgrave are recommended.
Geiss, Ryan, and Michael Thompson. 2007. "NVIDIA Demo Team Secrets—Cascades." Presentation at Game Developers Conference 2007. Available online at http://developer.download.nvidia.com/presentations/2007/gdc/CascadesDemoSecrets.zip.
NVIDIA Corporation. 2007. "Cascades." Demo. More information available online at http://www.nzone.com/object/nzone_cascades_home.html.

Комментариев нет:

Отправить комментарий