воскресенье, 19 июня 2011 г.

Отложенное освещение и затенение в игре Tabula Rasa (Перевод) Часть 2

 

19.4 Усовершенствованные возможности работы со светом

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


19.4.1 Двунаправленное освещение


В мире общераспространно традиционное гемисферическое освещение. В данной модели освещения используются два цвета, традиционно именуемые как «верх» и «низ». и затем линейно интерполирует между этими двумя цветами, расположенными на нормали к поверхности.
Обычно гемисферичное освещение интерполирует цвета, когда нормаль к поверхности двигается от положения, указывающего прямо наверх к положению, указания прямо вниз (отсюда термины верх и низ). В Табула Раса мы используем эту традиционную гемисферическую модель, но мы также добавляем цвет фона к направленному освещению (источникам прямого света).
С использованием ООЗ гр.дизайнеры могут легко добавлять составные(сложные, со сложной структурой) источники прямого света. Мы наблюдали, как они добавляли второй источник прямого освещения, который был направлен в противоположную от первого сторону с целью симулирования мерцания или сияющего источника света из окружающей среды. Им очень понравилось взгляд и контроль, который он им дал, поэтому обычная оптимизация должна была соединить эти два противоположно направленных света в один, новый тип прямого света – с передним светом и задним светом – фоном. Это принесло им тот же контроль за полцены.
Как дальнейшая оптимизация, задний свет является просто Н-Л моделью, или простой световой моделью Ламбертиана. Мы не приводим расчеты для более сложных моделей отражения, тенизации, преграждения. Этот задний свет – простое приближение окружающего света на сцене. Мы сохранили модель Н-Л, рассчитанную для переднего света и просто применили ее для заднего света – фона.


19.4.2 Cферическое отображение


Cферическое отображение – текстура, используемая для такого типа света, который образуется, если в стеклянную сферу поместить естественный источник света. Когда луч света посылается от источника, он должен пройти сквозь сферу, где он или окрашивается, или блокируется. Для точечных источников света мы используем кубическое отображение (преобразование данных) для получения рассматирваемого эффекта. Для прожекторов мы используем 2Д текстуру. Это может быть приложено к пренебрежительно искусственным эффектам цветного стекла или для блокировать свет в отдельных частях изображения.
Дизайнеры используют этот эффект для имитации изображения теней, когда возможно, для имитации эффектов цветного стекла, эффекты отражения диско-шаров и др. Все типы света в нашем механизме поддерживают эти эффекты. См. рис. 19.1 – 19.3 в качестве примеров применения к свету сферического отображения.
Рис 19-1 Простой прожектор

Рис 19-2 Простая текстура для сферического отображения
Рис 19-3Прожектор с текстурой для сферического отображения
19.4.3 Освещение Коробки
В Табула Раса прямые источники света освещают всю сцену и используются для симуляции солнечного или лунного света. Мы наблюдали, как дизайнеры хотели осветить прямым светом небольшую площадь, при этом не освещая всю сцену действия целиком. То, что им было нужно – это локальные источники прямого света.
Наше решение для локального источника прямого света – световой короб. Эти источники света используют нашу базовую модель прямого осещения, но они ограничены объемной фигурой в форме параллелепипеда. Они поддерживают падение прожекторного света, поэтому их интенсивность может постепенно исчезатьпо мере приближения к краям светового объема. Световые коробки также поддерживают отображение теней, сферические отображения, фоновый свет и все остальные признаки нашего механизма освещения.
В Табула Раса не присутствует заранее вычисленное освещение. Мы исключительно используем карты теней, не шаблонные тени или отображения света. Дизайнеры могут обеспечивать построение теней от любого света (кроме гемисферического). Мы используем кубическое отображение для тени световой точки и 2Д текстуру для всего остального.
Сейчас все shadow maps в Табула Раса имеют текстуру с плавающей точкой и используют дрожание для сглаживания теней. Дизайнеры могут контролировать ширину дрожания, позволяя контролировать, насколько мягко появляется тень. Этот подход позволил найти одно решение, которое работало бы и выглядело одинаково при любом техническом обеспечении. Однако, специфичные форматы текстуры могут быть также использованы для отображения теней. Они могут обеспечивать такие преимущества, как более высокая четкость и фильтрация.
Global Shadow Maps (глобальное отображение теней)
Существует много трудов, написанные об отображении теней, или отображении теней от одного источника прямого света на всей сцене действий. Мы потратили пару недель исследуя перспективное отображение теней (Stamminger and Drettakis 2002) и трапецевидное отображение теней (Martin and Tan 2004). Провал этих техник заключается в том, что конечный результат зависит от угла между направлением света и взгляда. В обоих методах, при движении камеры, качество теней отличается, в худшем случае сокращаясь до стандартной ортогональной проекции.
В Табула Раса существует дневной и ночной циклы, с солнцем или луной, постоянно двигающейся через небо. Рассвет и сумерки хитро отображаются за счет того, что направление света идет практически параллельно земле, что сильно увеличивает вероятность того, что направление взгляда совпадает с направлением света. Для перспективного и трапецевидного отображения теней этот вариант является наихудшим.
Из-за несовместимого качества теней в процессе движения камеры или источника света, мы остановились на использование единого большого 2048 ан 2048 отображения теней с нормальной ортогональной проекцией. Это принесло совместимые результаты, которые не зависели от движения камеры и источника света. Однако новые техники, которые мы не опробовали, такие как каскадное отображение теней, могут работать еще лучше.
Мы использовали дрожание для смягчения углов теней, квантовали (оцифровали) позицию трансформации света таким образом, что он всегда направлен на согласующуюся позицию в пространстве в рамках (с точностью до ) субъэлемента изображения теневой карты., и мы квантовали направление света таким образом, что значения, направленные для построения карты теней не изменяли ежеминутно каждый кадр. Это позволило получить постоянную тень с двигающейся камерой. См. 19-2.
  
  1.    // Assumes a square shadow map and square shadow view volume.  
  2.    // Compute how "wide" a pixel in the shadow map is in world space.  
  3.    const float pixelSize = viewSize / shadowMapWidth;  
  4. // How much has our light position changed since last frame?  
  5.    vector3 delta(lightPos - lastLightPos);  
  6. // Project the delta onto the basis vectors of the light matrix.  
  7.    float xProj = dot(delta, lightRight);  
  8. float yProj = dot(delta, lightUp);  
  9. float zProj = dot(delta, lightDir);  
  10. // Quantize the projection to the nearest integral value.  
  11.    // (How many "pixels" across and up has the light moved?)  
  12.    const int numStepsX = static_cast<int>(xProj / pixelSize);  
  13. const int numStepsY = static_cast<int>(yProj / pixelSize);  
  14. // Go ahead and quantize "z" or the light direction.  
  15.    // This value affects the depth components going into the shadowmap.  
  16.    // This will stabilize the shadow depth values and minimize  
  17.    // shadow bias aliasing.  
  18.    const float zQuantization = 0.5f;  
  19. const int numStepsZ = static_cast<int>(zProj / zQuantization);  
  20. // Compute the new light position that retains the same subpixel  
  21.    // location as the last light position.  
  22. lightPos = lastLightPos + (pixelSize * numStepsX) * lgtRight +  
  23.                           (pixelSize * numStepsY) * lgtUp +  
  24.                           (zQuantization * numStepsZ) * lgtDir;  
Local Shadow Maps (локальное отображение теней)
Так как в нашей системе любой свет может отбрасывать тень, с изображением, имеющим сотни источников света, механизм должен управлять созданием и использованием многих карт теней. Все изображения теней воспроизводятся на ходу в случае необходимости. Однако, большинство теневых карт статичны и не должны быть регенерированы в каждом кадре. Мы позволили дизайнерам контролировать это, позволив им расставить флажки на любом отбрасывающем тень источнике света, если они хотели использовать статичную или динамическую карту теней. Статичные карты теней построены только однажды и используются снова и снова в каждом кадре; динамические перестраиваются с каждым кадром.

19.4.5 Будущее Расширение

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

Высоко динамический диапазон, расцвет, и другие эффекты в равной степени как легко добавить к ООЗ . Как правило, добавить особенности в данном случае легче или не намного тяжелее, чем это было бы в случае механизма основанного на форвард шейдинге. Проблемой, которая, наиболее вероятно, ограничит набор признаков ООЗ, является ограниченное число материальных свойств, которые могут быть сохранены на пикселе, доступном видео памяти, и пропускной способности видеопамяти.


19.5 Преимущества для чтения глубины и нормального буфера

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


19.5.1 Расширение воды и преломление

В Tabula Rasa, при использовании канала ООЗ , наш шейдер воды принимает во внимание водную глубину . Поскольку каждый пиксель в воде предоставлен, шейдер проверяет глубину, сохраненную от канала ООЗ, и сравнивает его с пикселем глубины воды. Вода может изменить цвет и прозрачность, и пиксели под водой преломляются, тогда как пиксели над водой не преломляются. Это также означает, что мы можем сделать все эти вещи за единственный шаг, в отличие от традиционных передовых рендеров.

Наш форвард рендер поддерживает только основную особенность преломления, а это требует дополнительное сделать проход для инициализации альфа-канала структуры преломления, чтобы не преломлять пиксели, которые не находятся под водой. Эта базовая процедура обрисована в общих чертах в Sousa 2005.

В нашем ООЗ рендере мы можем попробовать глубину текущего пикселя и глубину соседнего преломляемого пикселя. Сравнивая эти глубины, мы можем определить, находится ли преломляемый пиксель позади целевого пикселя. Если да, то мы возобновляем эффект преломления; в противном случае мы этого не делаем. См. иллюстрации 19-4 и 19-5.


Иллюстрация 19-4 Использование воды только перед затенением


Иллюстрация 19-5 Использование воды перед затенением, но с доступом к буферу глубины от отсроченной затененной непрозрачной геометрии

Чтобы позволить художнику управлять цветом и прозрачностью с глубиной, мы фактически используем структуру объема такую же, как 1D структура. 1D структура – всего лишь стол поиска прозрачности с нормализованной водной глубиной, используемой для координаты структуры. Эта техника позволила художникам легко моделировать нелинейные отношения между водной глубиной и ее прозрачностью. Структура объема фактически использовалась для водного поверхностного цвета. Это может быть плоский структурный объем (становящийся регулярной 2-ой структурой), или у него могло быть два или четыре W слоя. Снова, нормализованная глубина использовалась для координаты структуры W с ультрафиолетовыми координатами, определяемыми художниками. Поверхностью воды управляли две независимо УФ-анимированные карты.


19.5.2 Независимое решение Обнаружение Края

Shishkovtsov 2005 представил методику обнаружения края, которая использовалась для того, чтобы подделать сглаживание в структуре. Выполнение полагалось на некоторые магические числа, которые менялись, основываясь на решении. Нам также надо было обнаружить края для сглаживания; однако, мы изменили алгоритм, чтобы принять независимое решение выполнения.

Мы смотрели на изменения в глубоких градиентах и на изменения в нормальных углах, пробуя все восемь соседей, окружающих пиксель. Это - в основном то же самое как метод Шишковстова. В этом пункте мы откланяемся и сравниваем максимальное изменение в глубине с минимальным чтобы определить количество в крае. Этот градиент глубины между пикселями - зависимое решение. Сравнивая родственные изменяется в этом градиенте вместо того, чтобы сравнить градиент с постоянными значениями, мы в состоянии принять логически независимое решение.

Наша обработка схожа с методом Шишковстова. Мы сравниваем изменения в косинусе угла между пикселем центра и его соседними пикселями вдоль тех же самых краев, на которых мы проверяем градиенты глубины. Здесь мы используем наше собственное постоянное число; однако, изменение в нормах пикселей не является зависимым решением. Это сохраняет логическое решение независимым.

Мы не вкладываем никакой логики в алгоритм, чтобы ограничить выбор "верхними правыми" или "передними" краями; следовательно, множество краев становятся несколькими расширениями пикселей. Однако, это удается хорошо с нашим методом фильтрования, чтобы помочь пригладить те края.

Производительность обнаружения края - вес между нулем и один за пиксель. Вес отражает то, как много пикселей выходит из края. Мы используем этот вес, чтобы сделать четыре билинеарных выборки, вычисляя конечный цвет пикселя. Эти четыре образца, которые мы берем, в центре пикселя для веса ноля и в четырех углах пикселя для веса одного. Это приводит к взвешенному среднему числу целевого пикселя со всеми восьмью из его соседей. Чем больше края пикселя, тем больше его связь с его соседями. См. Распечатку 19-3.


  1.  ////////////////////////////  
  2.    // Neighbor offset table  
  3.    ////////////////////////////  
  4.    const static float2 offsets[9] = {  
  5.   float2( 0.0,  0.0), //Center       0  
  6.    float2(-1.0, -1.0), //Top Left     1  
  7.    float2( 0.0, -1.0), //Top          2  
  8.    float2( 1.0, -1.0), //Top Right    3  
  9.    float2( 1.0,  0.0), //Right        4  
  10.    float2( 1.0,  1.0), //Bottom Right 5  
  11.    float2( 0.0,  1.0), //Bottom       6  
  12.    float2(-1.0,  1.0), //Bottom Left  7  
  13.    float2(-1.0,  0.0)  //Left         8  
  14. };  
  15. float DL_GetEdgeWeight(in float2 screenPos)  
  16. {  
  17.   float Depth[9];  
  18.   float3 Normal[9];  
  19.   //Retrieve normal and depth data for all neighbors.  
  20.    for (int i=0; i<9; ++i)  
  21.   {  
  22.     float2 uv = screenPos + offsets[i] * PixelSize;  
  23.     Depth[i] = DL_GetDepth(uv);  //Retrieves depth from MRTs  
  24.     Normal[i]= DL_GetNormal(uv); //Retrieves normal from MRTs  
  25.   }  
  26.   //Compute Deltas in Depth.  
  27.    float4 Deltas1;  
  28.   float4 Deltas2;  
  29.   Deltas1.x = Depth[1];  
  30.   Deltas1.y = Depth[2];  
  31.   Deltas1.z = Depth[3];  
  32.   Deltas1.w = Depth[4];  
  33.   Deltas2.x = Depth[5];  
  34.   Deltas2.y = Depth[6];  
  35.   Deltas2.z = Depth[7];  
  36.   Deltas2.w = Depth[8];  
  37.   //Compute absolute gradients from center.  
  38.   Deltas1 = abs(Deltas1 - Depth[0]);  
  39.   Deltas2 = abs(Depth[0] - Deltas2);  
  40.   //Find min and max gradient, ensuring min != 0  
  41.    float4 maxDeltas = max(Deltas1, Deltas2);  
  42.   float4 minDeltas = max(min(Deltas1, Deltas2), 0.00001);  
  43.   // Compare change in gradients, flagging ones that change  
  44.    // significantly.  
  45.    // How severe the change must be to get flagged is a function of the  
  46.    // minimum gradient. It is not resolution dependent. The constant  
  47.    // number here would change based on how the depth values are stored  
  48.    // and how sensitive the edge detection should be.  
  49.    float4 depthResults = step(minDeltas * 25.0, maxDeltas);  
  50.   //Compute change in the cosine of the angle between normals.  
  51.   Deltas1.x = dot(Normal[1], Normal[0]);  
  52.   Deltas1.y = dot(Normal[2], Normal[0]);  
  53.   Deltas1.z = dot(Normal[3], Normal[0]);  
  54.   Deltas1.w = dot(Normal[4], Normal[0]);  
  55.   Deltas2.x = dot(Normal[5], Normal[0]);  
  56.   Deltas2.y = dot(Normal[6], Normal[0]);  
  57.   Deltas2.z = dot(Normal[7], Normal[0]);  
  58.   Deltas2.w = dot(Normal[8], Normal[0]);  
  59.   Deltas1 = abs(Deltas1 - Deltas2);  
  60.   // Compare change in the cosine of the angles, flagging changes  
  61.    // above some constant threshold. The cosine of the angle is not a  
  62.    // linear function of the angle, so to have the flagging be  
  63.    // independent of the angles involved, an arccos function would be  
  64.    // required.  
  65.    float4 normalResults = step(0.4, Deltas1);  
  66.   normalResults = max(normalResults, depthResults);  
  67.   return (normalResults.x + normalResults.y +  
  68.           normalResults.z + normalResults.w) * 0.25;  
  69. }  

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

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