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; }