Country not specified
Unknown website Share

Apps4all

Страна: -
Город: -
Был онлайн: -
О себе:
 
08-07-2016, 11:02
Apps4all

Эффективная порядко-независимая прозрачность на Android с использованием фрагментного шейдера

Введение

Этот образец демонстрирует использование расширения GL_INTEL_fragment_shader_ordering, написанного под профиль OpenGL 4.4 и технические требования GLES 3.1. Минимальная требуемая версия – 4.2 или ARB_shader_image_load_store. Расширение представляет новую встроенную функцию GLSL beginFragmentShaderOrderingINTEL(),которая блокирует выполнение вызова фрагментного шейдера до тех пор, пока вызовы от предыдущих базовых элементов, отображаемые на тех же ху-координатах окна, не будут завершены. В образце эта линия поведения используется для предоставления решений, обеспечивающих порядко-независимую прозрачность в типичной 3D-сцене в реальном времени.

Порядко-независимая прозрачность

Прозрачность – это фундаментальный вызов для рендеринга в реальном времени, ввиду сложности наложения случайного числа прозрачных слоёв в правильном порядке. Этот образец построен на работе, изначально описанной в статьях adaptive-transparency и multi-layer-alpha-blending (Марк Сальви, Джефферсон Монтгомери, Картик Вайданатан и Аарон Лефон). Эти статьи показывают, как прозрачность может точно соответствовать реальным результатам, полученным от компоновки с использованием А-буфера, но может быть от 5 до 40 раз быстрее, благодаря использованию различных техник необратимого сжатия применительно к прозрачности данных. Этот образец представляет собой алгоритм на базе этих техник сжатия, который подходит для включения в такие приложения, как, например, игры.

Прозрачность бросает вызов

Пример рендеринга тестовой сцены с использованием стандартного альфа-смешивания показан на Рис. 1:

5527a78b9c45a9.80011631.jpg

Рис. 1: Пример порядко-независимой прозрачности (OIT)

Геометрия визуализируется в фиксированном порядке: за землей следуют объекты внутри свода, затем свод и, наконец, растения снаружи. Блочные объекты рисуются первыми и обновляют буфер глубины, а затем рисуются прозрачные объекты в том же порядке без обновления буфера глубины. Увеличенное изображение демонстрирует один из визуальных артефактов, получающихся в результате: листва находится внутри свода, но перед несколькими плоскостями стекла. К сожалению, порядок рендеринга диктует правила таким образом, что все плоскости стекла, даже те, что находятся позади листвы, рисуются поверх. Обновление буфера глубины прозрачным объектом создает другой ряд проблем. Традиционно их можно решить разбивкой объекта на несколько небольших частей и их сортировкой front-to-back, исходя из точки расположения камеры. Но даже так идеального результата не достичь, поскольку объекты могут перекрещиваться, а затраты рендеринга, тем временем, возрастают с прибавлением числа отсортированных объектов. 

Рис. 2 и Рис. 3 показывают увеличенный визуальный артефакт, где на рис. 2 все плоскости стекла нарисованы перед листвой и на рис. 3 корректно отсортированы. 

5527a7f8b34967.93228531.jpg

Рис. 2: Не отсортированы

5527a812c57939.95244556.jpg

Рис. 3: Отсортированы

Порядко-независимая прозрачность в реальном времени

Было множество попыток применить компоновку произвольно упорядоченных базовых геометрических элементов без необходимости сортировки на CPUили разбивки геометрии на непересекающиеся элементы. Среди таких попыток - depth-peeling, требующий многократного представления (geometry to be submitted multiple times ) геометрии и техник А-буфера, где все фрагменты, связанные с заданным пикселем, хранятся в связном списке, отсортированы и затем перемешаны в корректном порядке. Несмотря на успех А-буфера в офлайн-рендеринге, он мало используется сообществом рендеринга в реальном времени из-за неограниченных требований к памяти и, как правило, низкой производительности.

Новый подход 

Было множество попыток применить компоновку произвольно упорядоченных базовых геометрических элементов без необходимости сортировки на CPUили разбивки геометрии на непересекающиеся элементы. Среди таких попыток - depth-peeling, требующий многократного представления (geometry to be submitted multiple times) геометрии и техник А-буфера, где все фрагменты, связанные с заданным пикселем, хранятся в связном списке, отсортированы и затем перемешаны в корректном порядке. Несмотря на успех А-буфера в офлайн-рендеринге, он мало используется сообществом рендеринга в реальном времени из-за неограниченных требований к памяти и, как правило, низкой производительности.

5527a8b88a16d8.33112641.jpg

Рис. 4: Функция видимости

Число шагов в функции видимости соответствует числу узлов, используемых для хранения информации по видимости на попиксельном уровне в процессе визуализации сцены. По мере добавления пиксели хранятся в структуре узла до его полного заполнения. Затем при попытке включения большего числа пикселей алгоритм подсчитывает, какой из предыдущих узлов может быть присоединен для создания самой маленькой вариации в функции видимости, при этом сохраняя размер набора данных. Финальный этап – вычисление функции видимости vis()и компоновка фрагментов при помощи формулы 

final_color =

Образец визуализирует сцену на следующих этапах:

Очистка Shader Storage Buffer Object до стандартных значений по умолчанию.

Визуализация всей блочной геометрии в основной фреймбуфер с обновлением буфера глубины.

Визуализация всей прозрачной геометрии без обновления буфера глубины; финальные фрагментные данные отброшены из фреймбуфера. Фрагментные данные хранятся в наборе узлов внутри Shader Storage Buffer Object.

Решение данных (Reslove) внутри Shader Storage Buffer Object и вмешивание финального результата в основной фреймбуфер.

Рис. 5: Render Path

Априори, затраты чтения Shader Storage Buffer Object на стадии Resolve могут быть крайне высокими из-за требований пропускной способности. В оптимизации, задействованной в образце, для маскировки участков, где прозрачные пиксели могли бы быть вмешаны во фреймбуфер, используется стенсил буфер Это меняет рендеринг так, как показано на Рис. 6.

Очистка Stencil buffer.

Очистка Shader Storage Buffer Object до стандартных значений по умолчанию.

Постановка следующей Stencil операции:

glDisable(GL_STENCIL_TEST);

Визуализация всей блочной геометрии в основной фреймбуфер с обновлением глубины.

Постановка следующих Stencil операций:

glEnable(GL_STENCIL_TEST);

glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

glStencilFunc(GL_ALWAYS, 1, 0xFF);

Визуализация всей прозрачной геометрии без обновления буфера глубины; финальные фрагментные данные интегрированы в основной фреймбуфер со значением альфа 0. Стенсил буфер отмечен для каждого фрагмента во фреймбуфере. Фрагментные данные хранятся в наборе узлов внутри Shader Storage Buffer Object. Отбрасывание фрагмента невозможно, так как это мешает обновлению стенсил.

Постановка следующих Stencil операций:

glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

glStencilFunc(GL_EQUAL, 1, 0xFF);

Решение данных (Resolve) внутри Shader Storage Buffer Object только для фрагментов, прошедших стенсил тест и вмешивание финального результата в основной фреймбуфер.

Постановка следующих Stencil операций:

glStencilFunc(GL_ALWAYS, 1, 0xFF);

glDisable(GL_STENCIL_TEST);

Рис. 6: Stencil Render Path

Выгода от использования стенсил буфера проявляется в затратах на этапе resolve, которые падают на 80%, хотя это во многом зависит от площади экрана (в %), занятой прозрачной геометрией. Чем больше площадь, занятая прозрачными объектами, тем меньше вы выигрываете в производительности.

01 void PSOIT_InsertFragment_NoSync( float surfaceDepth, vec4 surfaceColor )
02 {
03 ATSPNode nodeArray[AOIT_NODE_COUNT];
04
05 // Load AOIT data
06 PSOIT_LoadDataUAV(nodeArray);
07
08 // Update AOIT data
09 PSOIT_InsertFragment(surfaceDepth,
10 1.0f - surfaceColor.w, // transmittance = 1 - alpha
11 surfaceColor.xyz,
12 nodeArray);
13 // Store AOIT data
14 PSOIT_StoreDataUAV(nodeArray);
15 }

Рис. 7: GLSL Shader Storage Buffer Code

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

В случае с OIT примером, она просто добавляется, как показано на Рис. 9:

1 void PSOIT_InsertFragment( float surfaceDepth, vec4 surfaceColor )
2 {
3 // from now on serialize all UAV accesses (with respect to other fragments shaded in flight which map to the same pixel)
4 #ifdef do_fso
5 beginFragmentShaderOrderingINTEL();
6 #endif
7 PSOIT_InsertFragment_NoSync( surfaceDepth, surfaceColor );
8 }

Рис. 9: Добавление упорядочения фрагмента в доступ к Shader Storage Buffer

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

01 out vec4 fragColor;// -------------------------------------
02 void main( )
03 {
04 vec4 result = vec4(0,0,0,1);
05
06 // Alpha-related computation
07 float alpha = ALPHA().x;
08 result.a = alpha;
09 vec3 normal = normalize(outNormal);
10
11 // Specular-related computation
12 vec3 eyeDirection = normalize(outWorldPosition - EyePosition.xyz);
13 vec3 Reflection = reflect( eyeDirection, normal );
14 float shadowAmount = 1.0;
15
16 // Ambient-related computation
17 vec3 ambient = AmbientColor.rgb * AMBIENT().rgb;
18 result.xyz += ambient;
19 vec3 lightDirection = -LightDirection.xyz;
20
21 // Diffuse-related computation
22 float nDotL = max( 0.0 ,dot( normal.xyz, lightDirection.xyz ) );
23 vec3 diffuse = LightColor.rgb * nDotL * shadowAmount * DIFFUSE().rgb;
24 result.xyz += diffuse;
25 float rDotL = max(0.0,dot( Reflection.xyz, lightDirection.xyz ));
26 vec3 specular = pow(rDotL, 8.0 ) * SPECULAR().rgb * LightColor.rgb;
27 result.xyz += specular;
28 fragColor = result;
29
30 #ifdef dopoit
31 if(fragColor.a > 0.01)
32 {
33 PSOIT_InsertFragment( outPositionView.z, fragColor );
34 fragColor = vec4(1.0,1.0,0.0,0.0);
35 }
36 #endif
37 }

Рис. 10: Типичный фрагментный шейдер

Только те фрагменты, которые имеют альфа-фактор выше граничного значения, добавлены в Shader Storage Buffer Object, отбраковывая любые фрагменты, не представляющие сцене никаких значимых данных.

Построение образца

Требования к билду

Установите последние версии Android SDK и NDK

Добавьте NDK и SDK в свою ветвь:

export PATH=$ANDROID_NDK/:$ANDROID_SDK/tools/:$PATH

Для построения:

  • 1. Проследуйте в папку OIT_2014\OIT_Android
  • 2. Только единожды вам может понадобиться инициализировать проект: 

android update project –path . --target android-19.

  • 3. Постройте NDK-компонент:

NDK-BUILD

  • 4. Постройте APK:

ant debug

  • 5. Установите APK:

adb install -r bin\NativeActivity-debug.apk или ant installd

  • 6. Выполните его

Выводы

Образец демонстрирует, как исследование адаптивной порядко-независимой прозрачности под руководством Марко Сальви, Джефферсона Монтгомери, Картика Вайданатана и Аарона Лефон, первоначально произведенное на высокопроизводительных дискретных видеокартах с использованием DirectX 11, может быть применено в реальном времени на планшете Android при помощи GLES 3.1 и упорядочения фрагментным шейдером. Алгоритм выполняется внутри постоянного требуемого объема памяти, который может варьироваться, исходя из требований визуальной достоверности. Оптимизации вроде стенсил буфера разрешают применение техники на широком ряде устройств на допустимом уровне производительности, обеспечивая практическое решение одной из самых насущных проблем рендеринга в реальном времени. Принципы, продемонстрированные в образце OIT, могут быть применены к целому спектру других алгоритмов, которые могли бы в нормальном режиме создавать попиксельные связные списки, включая техники объемного затенения и пост-процессинговое сглаживание.

Статьи по теме

 
Intel
разработка
разработчикам
0 0 0

Чтобы оставлять комментарии вам необходимо зарегистрироваться