SSAO некорректен при z = 0 и на расстоянии

Итак, я попытался внедрить SSAO, но он не работает должным образом. Кажется, что он разделился в позиции z=0 (мировое пространство), есть белая линия в позиции z=0. Кроме того, окклюзия выглядит неправильно.

введите здесь описание изображения

Плюс на большем расстоянии, поэтому при перемещении камеры окклюзия становится еще более странной введите здесь описание изображения

Мой шейдер для рендеринга геометрии (экземпляр):

Вершина:

#version 330 core

 layout(location = 0) in vec3 vertexPosition;
 layout(location = 1) in vec2 vertexUV;
 layout(location = 2) in vec3 vertexColor;
 layout(location = 3) in vec3 vertexNormal;
 layout(location = 4) in mat4 offset;
 layout(location = 8) in vec4 instanceColor;

uniform mat4 Projection;
uniform mat4 View;

out vec2 UV;
out vec4 Color;
out vec3 Normal;

void main()
{

        mat4 Model = offset;
    mat4 MVP = Projection * View * Model;



vec4 Pos = MVP * vec4(endpos,1);

gl_Position = Pos;

UV = vertexUV;

Color = instanceColor;
    Normal = normalize((Model * vec4(vertexNormal,0)).xyz);

}

Фрагмент:

    #version 330 core

    in vec2 UV;
    in vec4 Color;
    in vec3 Normal;

    uniform sampler2D Diffuse;

void main()
{
    gl_FragData[0] = vec4(Color);
    gl_FragData[1] = (vec4(Normal,1)+1)/2;
}

После прохода геометрии я применяю проход SSAO с информацией о нормали и глубине.

Это моя NoiseTexture: введите здесь описание изображения

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

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

    #version 330 core

#define KERNEL_SIZE 16
uniform sampler2D NormalMap;
uniform sampler2D DepthMap;
uniform sampler2D NoiseTexture;
uniform vec2 NoiseScale;

uniform vec2 Resolution;

uniform mat4 InvertViewProjection;

uniform float g_sample_rad = 0.1;
uniform float g_intensity = 2.0;
uniform float g_scale = 0.1;
uniform float g_bias = 0.0;

vec2 CalcTexCoord()
{
    return gl_FragCoord.xy / Resolution;
}

vec3 getPosition(vec2 uv)
{
    vec4 worldpos;

    float depth = texture2D(DepthMap, uv).r;

    worldpos.x = uv.x * 2.0f - 1.0f;

    worldpos.y = uv.y * 2.0f - 1.0f;

    worldpos.z = depth * 2.0f - 1.0f;

    worldpos.w = 1.0;

    worldpos = InvertViewProjection * worldpos;

    worldpos /= worldpos.w;

    return worldpos.rgb;
}

vec3 getNormal(vec2 uv)
{
    return normalize(texture2D(NormalMap, uv).xyz * 2.0f - 1.0f);
}

vec2 getRandom(vec2 uv)
{
    return normalize(texture2D(NoiseTexture, Resolution*uv / NoiseScale).xy * 2.0f - 1.0f);
}

float doAmbientOcclusion(in vec2 tcoord, in vec2 uv, in vec3 p, in vec3 cnorm)
{
    vec3 diff = getPosition(tcoord + uv) - p;
    vec3 v = normalize(diff);
    float d = length(diff)*g_scale;

    return max(0.0, dot(cnorm, v) - g_bias)*(1.0 / (1.0 + d))*g_intensity;
}



void main()
{

    vec4 Kernels[KERNEL_SIZE] =
    vec4[](
        vec4(0.355512,  -0.709318,  -0.102371,  0.0 ),
        vec4(0.534186,  0.71511,    -0.115167,  0.0 ),
        vec4(-0.87866,  0.157139,   -0.115167,  0.0 ),
        vec4(0.140679,  -0.475516,  -0.0639818, 0.0 ),
        vec4(-0.0796121,    0.158842,   -0.677075,  0.0 ),
        vec4(-0.0759516,    -0.101676,  -0.483625,  0.0 ),
        vec4(0.12493,   -0.0223423, -0.483625,  0.0 ),
        vec4(-0.0720074,    0.243395,   -0.967251,  0.0 ),
        vec4(-0.207641,     0.414286,   0.187755,   0.0 ),
        vec4(-0.277332,     -0.371262,  0.187755,   0.0 ),
        vec4(0.63864,   -0.114214,  0.262857,   0.0 ),
        vec4(-0.184051,     0.622119,   0.262857,   0.0 ),
        vec4(0.110007,  -0.219486,  0.435574,   0.0 ),
        vec4(0.235085,  0.314707,   0.696918,   0.0 ),
        vec4(-0.290012,     0.0518654,  0.522688,   0.0 ),
        vec4(0.0975089,     -0.329594,  0.609803,   0.0 )

    );
    vec2 uv = CalcTexCoord(); //same as UV Coordinate from Vertex
    vec3 p = getPosition(uv);
    vec3 n = getNormal(uv);
    vec2 rand = getRandom(uv);

    float ao = 0.0f;
    float rad = g_sample_rad / p.z;

    for (int j = 0; j < KERNEL_SIZE; ++j)
    {
        vec2 coord = reflect(Kernels[j].xy, rand)*rad;
        ao += doAmbientOcclusion(uv, coord, p, n);
    }
    ao /= KERNEL_SIZE;

    ao = 1 - (ao);
    gl_FragColor = vec4(ao,ao,ao, 1);
}

person PuRe    schedule 03.06.2015    source источник
comment
Я не совсем понимаю, когда вы говорите, что z = 0. О каком координатном пространстве вы говорите? В пространстве NDC это будет центр вашего видимого вида, и он будет параллелен плоскости изображения, а не диагонали, как показано на скриншоте. Насколько я знаю, в других местах это может быть ваша ближняя или дальняя клипса. Одна вещь, которую я могу вам сказать, заключается в том, что большую часть этого шума можно уменьшить, если вы используете линейный/логарифмический буфер глубины вместо перспективы.   -  person Andon M. Coleman    schedule 03.06.2015
comment
Что работает для меня, так это своего рода гибридный подход. Я использую буфер глубины с плавающей запятой с инвертированным диапазоном глубины (это устраняет неоптимальное распределение точности в глубине перспективы), что устраняет эти артефакты полос, а затем различные методы постобработки на основе глубины фактически требуют линейной глубины, поэтому я также буду возьмите аппаратный буфер глубины, описанный выше, и линеаризуйте его нужным образом в шейдере.   -  person Andon M. Coleman    schedule 03.06.2015
comment
Я рассчитываю все в мировом пространстве, потому что мое освещение рассчитывается в мировом пространстве, поэтому я не хочу переключаться между пространствами. чтобы вычислить свое положение в мире, мне не нужна линейная глубина. Я протестировал его путем выборки из текстуры, в которой я сохранил позицию ранее, и оба результата дают одинаковый результат. То, что я имел в виду под z=0, это положение в мире. Если я перемещаю камеру в направлении z и смотрю на вершины, которые находятся дальше от z=0, они выглядят искаженными, как показано на изображении 2.   -  person PuRe    schedule 03.06.2015


Ответы (1)


Я понял это путем пошаговой отладки кода.

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

Ошибка здесь:

 float rad = g_sample_rad / p.z;

это вычисляет радиус выборки на основе расстояния. В пространстве обзора это вычисляет радиус выборки для расстояния от камеры. Но в мировом пространстве это вычисляет расстояние от мировой координаты, в результате чего оно выглядит странно при z=0 и еще более странно при удалении.

так что я сделал, чтобы исправить это просто:

vec4 viewpos = CamView * vec4(p,1);
float ao = 0.0f;
float rad = g_sample_rad/viewpos.z;

Я изменил точку на пространство обзора и рассчитал радиус дискретизации в пространстве обзора, при этом учитывая расстояние от камеры.

Это исправляет это.

введите здесь описание изображения

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

person PuRe    schedule 04.06.2015