Я рассмотрел несколько методов прозрачности, не зависящих от порядка, для моего механизма OpenGL, и сначала я подумал, что хотел бы использовать средневзвешенное смешивание, чтобы максимизировать скорость.
Тем не менее, мой двигатель использует отложенное затенение, и я должен принять это во внимание при выборе метода смешивания. В идеале я хотел бы, чтобы метод, который не требовал от меня реализации прямого затенения, использовался для полупрозрачных объектов.
Есть ряд случаев, когда мне нужно использовать прозрачность:
Я готов пожертвовать правильностью изображения ради скорости (отсюда мой первоначальный выбор средневзвешенного смешивания). Мне не нужно освещать каждый слой полупрозрачных объектов, но я бы хотел, чтобы самые передние пиксели были правильно освещены.
Я использую OpenGL 3.x + Core Context, поэтому я хотел бы избегать всего, что требует OpenGL 4.x (как бы прекрасно), но я могу свободно использовать все, что недоступно в OpenGL 2. Икс.
У меня такой вопрос: каков наилучший метод прозрачности, независимый от порядка, для отложенного затенения? И / или: как лучше всего осветить / затенить полупрозрачный объект при использовании отложенного затенения?
Постскриптум Есть ли лучший способ сделать сглаженные вырезы (трава / волосы / листья), которые не зависят от смешивания? Чистое альфа-тестирование приводит к появлению уродливых алиасов.
Я не уверен, что он подходит для вашего отложенного рендера, но вы могли бы рассмотреть взвешенную, смешанную прозрачность, независимую от порядка. Там есть старая версия без цветной передачи (Web) и более новая версия, которая поддерживает цветную передачу (Web) и много других вещей. Это довольно быстро, потому что он использует только одну непрозрачную, одну прозрачность и один проход композиции и работает с OpenGL 3.2+.
Я реализовал первую версию, и она работает довольно хорошо, в зависимости от вашей сцены и правильно настроенной весовой функции, но имеет проблемы с высокими альфа-значениями. Я не получил хороших результатов с весовыми функциями из статей, но только после использования линейных, нормализованных z-значений в пространстве глаза.
Обратите внимание, что при использовании OpenGL < 4.0 вы не можете указать функцию смешивания для буфера (glBlendFunci), поэтому вам нужно обойти это (см. Первую статью).
Визуализация непрозрачной геометрии в приложении № 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);
}