Существуют ли какие-либо методы независимой от заказа прозрачности, подходящие для отложенного затенения?

Я рассмотрел несколько методов прозрачности, не зависящих от порядка, для моего механизма OpenGL, и сначала я подумал, что хотел бы использовать средневзвешенное смешивание, чтобы максимизировать скорость.

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

Есть ряд случаев, когда мне нужно использовать прозрачность:

  • Трава / Волосы (сглаженные вырезы)
  • Стекло (красочное смешение)
  • Объекты, которые исчезают и исчезают
  • Дымовые / Clouds
  • Вода / Жидкость (может включать рефракцию, я знаю, что истинное OIT здесь невозможно)
  • Искры / Магия / Огонь (не должны быть зажжены и могут использовать смешивание добавок, не беспокойтесь об этом)

Я готов пожертвовать правильностью изображения ради скорости (отсюда мой первоначальный выбор средневзвешенного смешивания). Мне не нужно освещать каждый слой полупрозрачных объектов, но я бы хотел, чтобы самые передние пиксели были правильно освещены.

Я использую OpenGL 3.x + Core Context, поэтому я хотел бы избегать всего, что требует OpenGL 4.x (как бы прекрасно), но я могу свободно использовать все, что недоступно в OpenGL 2. Икс.

У меня такой вопрос: каков наилучший метод прозрачности, независимый от порядка, для отложенного затенения? И / или: как лучше всего осветить / затенить полупрозрачный объект при использовании отложенного затенения?

Постскриптум Есть ли лучший способ сделать сглаженные вырезы (трава / волосы / листья), которые не зависят от смешивания? Чистое альфа-тестирование приводит к появлению уродливых алиасов.

1

Решение

Я не уверен, что он подходит для вашего отложенного рендера, но вы могли бы рассмотреть взвешенную, смешанную прозрачность, независимую от порядка. Там есть старая версия без цветной передачи (Web) и более новая версия, которая поддерживает цветную передачу (Web) и много других вещей. Это довольно быстро, потому что он использует только одну непрозрачную, одну прозрачность и один проход композиции и работает с OpenGL 3.2+.
Я реализовал первую версию, и она работает довольно хорошо, в зависимости от вашей сцены и правильно настроенной весовой функции, но имеет проблемы с высокими альфа-значениями. Я не получил хороших результатов с весовыми функциями из статей, но только после использования линейных, нормализованных z-значений в пространстве глаза.
Обратите внимание, что при использовании OpenGL < 4.0 вы не можете указать функцию смешивания для буфера (glBlendFunci), поэтому вам нужно обойти это (см. Первую статью).

  • Настройте буфер кадра с этими приложениями:
    • 0: RGBA8, непрозрачный
    • 1: RGBA16F, накопление
    • 2: R16F, раскрытие
  • Очистите приложение № 0 к экрану очистите цвет (r, g, b, 1), № 1 до (0,0,0,1) и № 2 до 0.
  • Визуализация непрозрачной геометрии в приложении № 0 и буфере глубины.

    glEnable (GL_DEPTH_TEST);
    glDepthMask (GL_TRUE);

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

    glDepthMask (GL_FALSE);
    glEnable (GL_BLEND);
    glBlendEquation (GL_FUNC_ADD);
    glBlendFuncSeparate (GL_ONE, GL_ONE, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);

Часть фрагмента шейдера, записывающая цели накопления и раскрытия, выглядит следующим образом:

uniform mat4 projectionMatrix;

layout (location = 1) out vec4 accum;
layout (location = 2) out float revealage;

/// @param color Regular RGB reflective color of fragment, not pre-multiplied
/// @param alpha Alpha value of fragment
/// param wsZ Window-space-z value == gl_FragCoord.z
void writePixel(vec3 color, float alpha, float wsZ) {
float ndcZ = 2.0 * wsZ - 1.0;
// linearize depth for proper depth weighting
//See: https://stackoverflow.com/questions/7777913/how-to-render-depth-linearly-in-modern-opengl-with-gl-fragcoord-z-in-fragment-sh
//or: https://stackoverflow.com/questions/11277501/how-to-recover-view-space-position-given-view-space-depth-value-and-ndc-xy
float linearZ = (projectionMatrix[2][2] + 1.0) * wsZ / (projectionMatrix[2][2] + ndcZ);
float tmp = (1.0 - linearZ) * alpha;
//float tmp = (1.0 - wsZ * 0.99) * alpha * 10.0; // <-- original weighting function from paper #2
float w = clamp(tmp * tmp * tmp * tmp * tmp * tmp, 0.0001, 1000.0);
accum = vec4(color * alpha* w, alpha);
revealage = alpha * w;
}
  • Свяжите текстуры вложений № 1 и № 2 и скомпонуйте их с вложением № 0, нарисовав четырехугольник с помощью композиционного шейдера.

    glEnable (GL_BLEND);
    glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);

Фрагмент шейдера для композиции выглядит так:

uniform sampler2DMS accumTexture;
uniform sampler2DMS revealageTexture;

in vec2 texcoordVar;
out vec4 fragmentColor;

void main() {
ivec2 bufferCoords = ivec2(gl_FragCoord.xy);
vec4 accum = texelFetch(accumTexture, bufferCoords, 0);
float revealage = accum.a;
// save the blending and color texture fetch cost
/*if (revealage == 1.0) {
discard;
}*/
accum.a = texelFetch(revealageTexture, bufferCoords, 0).r;
// suppress underflow
if (isinf(accum.a)) {
accum.a = max(max(accum.r, accum.g), accum.b);
}
// suppress overflow
if (any(isinf(accum.rgb))) {
accum = vec4(isinf(accum.a) ? 1.0 : accum.a);
}
vec3 averageColor = accum.rgb / max(accum.a, 1e-4);
// dst' = (accum.rgb / accum.a) * (1 - revealage) + dst * revealage
fragmentColor = vec4(averageColor, revealage);
}
0

Другие решения


По вопросам рекламы [email protected]