SSAO резко меняется с углом обзора камеры

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

Вот фрагмент кода шейдера

void main() {

    vec3 origin = positionFromDepth(texture2D(gDepth, samplePosition));
    vec3 normal = texture2D(gNormal, samplePosition).xyz; //multiplying this 
//by 2 and subtracting 1 doesn't seem to help
    vec2 random = getRandom(samplePosition);

    float radius = uRadius/origin.z;
    float occlusion = 0.0;
    int iterations = samples/4;

    for (int i = 0; i<iterations; i++) {
        vec2 coord1 = reflect(kernel[i], random)*radius;
        vec2 coord2 = vec2(coord1.x*0.707 - coord1.y*0.707, coord1.x*0.707 + coord1.y*0.707);
        occlusion += occlude(samplePosition, coord1 * 0.25, origin, normal);
        occlusion += occlude(samplePosition, coord2 * 0.50, origin, normal);
        occlusion += occlude(samplePosition, coord1 * 0.75, origin, normal);
        occlusion += occlude(samplePosition, coord2, origin, normal);
    }

    color = vec4(origin, 1);

}

positionFromDepth() функция:

vec3 positionFromDepth(float depth) {
    float near = frustrumData.x;
    float far = frustrumData.y;
    float right = frustrumData.z;
    float top = frustrumData.w;
    vec2 ndc;           
    vec3 eye;             
    eye.z = near * far / ((depth * (far - near)) - far);
    ndc.x = ((gl_FragCoord.x/buffersize.x) - 0.5) * 2.0; 
    ndc.y = ((gl_FragCoord.y/buffersize.y) - 0.5) * 2.0;
    eye.x = (-ndc.x * eye.z) * right/near;
    eye.y = (-ndc.y * eye.z) * top/near;
    return eye;
}

И occlude() функция:

float occlude(vec2 uv, vec2 offsetUV, vec3 origin, vec3 normal) {
    vec3 diff = positionFromDepth(texture2D(gDepth,(uv+offsetUV)))-origin;
    vec3 vec = normalize(diff);
    float dist = length(diff)/scale;
    return max(0.0,dot(normal,vec)-bias)*(1.0/(1.0+dist))*intensity;
}

У меня есть ощущение, что проблема может быть в positionFromDepth() функция, за исключением того, что я использую тот же код для этапа освещения рендерера, который работает отлично (я думаю). Я перебирал этот код тысячу раз и не нашел ничего, что могло бы быть ошибочным. Я пробовал различные значения для bias, radius, intenisty, а также scaleНо это не проблема. Я беспокоюсь, что мои нормали или позиции неправильны, поэтому вот некоторые скриншоты из них:

Восстановленная позиция:И обычный буфер:

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

Кто-нибудь знает, что здесь не так?

1 ответ

Решение

Странно, что умножение на 2 и вычитание 1 не помогают с вашей обычной картой. Обычно это делается для преодоления проблем, связанных с хранением нормалей в неподписанных / нормализованных форматах текстур. Если ваш обычный G-Buffer не является подписанным / ненормализованным форматом, вам, вероятно, нужно упаковать и распаковать ваши нормали, используя * 0.5 + 0.5 когда вы впервые пишете и * 2.0 - 1.0 когда вы пробуете текстуру.

В любом случае, существует несколько подходов к SSAO, и многие даже не используют поверхностные нормали вообще. Поэтому обсуждение того, в каком векторном пространстве вы храните свои нормали, часто упускается из виду.

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

Оказывается, что нормали пространства обзора на самом деле не так уж полезны, так как число эффектов пост-обработки в наши дни лучше работает с использованием норм пространства мира. Большинство современных механизмов отложенного затенения (например, Unreal Engine 4, CryEngine 3 и т. Д.) Хранят обычный G-буфер в мировом пространстве, а затем преобразуют его в пространство просмотра (при необходимости) в пиксельном шейдере.


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

квартира в mat4 inv_mv_mat;
     в vec2 уф;...

поплавок linearZ (поплавок z)
{
#ifdef INVERT_NEAR_FAR
  const float f = 2,5;
  const float n = 25000,0;
#else
  const float f = 25000,0;
  const float n = 2,5;
#endif

  вернуть n / (f - z * (f - n)) * f;
}

vec4
реконструкция_пос (в глубине поплавка)
{
  глубина = линейный Z (глубина);

  vec4 pos = vec4 (уф * глубина, глубина, 1,0); 
  vec4 ret = (inv_mv_mat * pos);

  возвращение ret / ret.w;
}

Требуется немного дополнительной настройки на этапе вершинного шейдера прохода отсроченного затенения, который выглядит следующим образом:

# версия 150 ядро

в vec4 vtx_pos;
в vec2 vtx_st;

униформа mat4 modelview_mat; // Матрица, использованная при создании G-Buffer
униформа mat4 camera_matrix; // Матрица, используемая для растягивания G-буфера над областью просмотра

равномерное число с плавающей точкой buffer_res_x;
равномерное число с плавающей точкой buffer_res_y;

     out vec2 tex_st;
вычеркнуть mat4 inv_mv_mat;
     вне vec2 уф;


// Жесткий код 45 градусов FOV
//const float fovy = 0.78539818525314331; // NV рвется на линию ниже!
//const float fovy = radians (45.0);
//const float tan_half_fovy = tan (fovy * 0.5);

const float   tan_half_fovy = 0,41421356797218323;

      аспект с плавающей точкой = buffer_res_x / buffer_res_y;
      vec2    inv_focal_len = vec2 (tan_half_fovy * аспект,
                                    tan_half_fovy);

const vec2    uv_scale     = vec2 (2.0, 2.0);
const vec2    uv_translate = vec2 (1.0, 1.0);


пустота главная (недействительная)
{
  inv_mv_mat  = обратный (modelview_mat);
  tex_st      = vtx_st;
  gl_Position = camera_matrix * vtx_pos;
  uv          = (vtx_st * uv_scale - uv_translate) * inv_focal_len;
}
Другие вопросы по тегам