Я следую за уроком Джона Чепмена (http://john-chapman-graphics.blogspot.nl/2013/01/ssao-tutorial.html) реализовать SSAO в отложенном рендерере. Входные буферы для шейдеров SSAO:
Сначала я перечислю полный шейдер, а затем кратко пройдусь по шагам:
#version 330 core
in VS_OUT {
vec2 TexCoords;
} fs_in;
uniform sampler2D texPosDepth;
uniform sampler2D texNormalSpec;
uniform sampler2D texNoise;uniform vec3 samples[64];
uniform mat4 projection;
uniform mat4 view;
uniform mat3 viewNormal; // transpose(inverse(mat3(view)))
const vec2 noiseScale = vec2(800.0f/4.0f, 600.0f/4.0f);
const float radius = 5.0;
void main( void )
{
float linearDepth = texture(texPosDepth, fs_in.TexCoords).w;
// Fragment's view space position and normal
vec3 fragPos_World = texture(texPosDepth, fs_in.TexCoords).xyz;
vec3 origin = vec3(view * vec4(fragPos_World, 1.0));
vec3 normal = texture(texNormalSpec, fs_in.TexCoords).xyz;
normal = normalize(normal * 2.0 - 1.0);
normal = normalize(viewNormal * normal); // Normal from world to view-space
// Use change-of-basis matrix to reorient sample kernel around origin's normal
vec3 rvec = texture(texNoise, fs_in.TexCoords * noiseScale).xyz;
vec3 tangent = normalize(rvec - normal * dot(rvec, normal));
vec3 bitangent = cross(normal, tangent);
mat3 tbn = mat3(tangent, bitangent, normal);
// Loop through the sample kernel
float occlusion = 0.0;
for(int i = 0; i < 64; ++i)
{
// get sample position
vec3 sample = tbn * samples[i]; // From tangent to view-space
sample = sample * radius + origin;
// project sample position (to sample texture) (to get position on screen/texture)
vec4 offset = vec4(sample, 1.0);
offset = projection * offset;
offset.xy /= offset.w;
offset.xy = offset.xy * 0.5 + 0.5;
// get sample depth
float sampleDepth = texture(texPosDepth, offset.xy).w;
// range check & accumulate
// float rangeCheck = abs(origin.z - sampleDepth) < radius ? 1.0 : 0.0;
occlusion += (sampleDepth <= sample.z ? 1.0 : 0.0);
}
occlusion = 1.0 - (occlusion / 64.0f);
gl_FragColor = vec4(vec3(occlusion), 1.0);
}
Результат, однако, не радует. Буфер окклюзии в основном весь белый и не показывает никакой окклюзии. Однако, если я подойду очень близко к объекту, я получу странные шумоподобные результаты, как вы можете видеть ниже:
Это явно не правильно. Я проделал большую часть отладки и считаю, что все соответствующие переменные правильно переданы (все они визуализируются как цвета). Я делаю вычисления в view-space.
Я кратко пройдусь по шагам (и выборам), которые я предпринял на случай, если кто-то из вас поймет, что что-то пошло не так на одном из шагов.
позиции в пространстве вида / нормали
Джон Чепмен извлекает позицию в пространстве вида, используя луч обзора и линеаризованное значение глубины. Так как я использую отложенный рендер, у которого уже есть позиции в мировом пространстве на фрагмент, я просто беру их и умножаю их на матрицу вида, чтобы получить их в пространстве вида.
Я использую аналогичный подход для нормальных векторов. Я беру нормальные векторы мирового пространства из буферной текстуры, преобразую их в диапазон [-1,1] и умножаю их на транспонированную (обратную (mat3 (..))) матрицу вида.
Положение в пространстве и нормали визуализируются, как показано ниже:
Это выглядит правильно для меня.
Ориентировать полушарие вокруг нормального
Шаги для создания tbn
Матрица такая же, как описано в руководстве Джона Чепмена. Я создаю текстуру шума следующим образом:
std::vector<glm::vec3> ssaoNoise;
for (GLuint i = 0; i < noise_size; i++)
{
glm::vec3 noise(randomFloats(generator) * 2.0 - 1.0, randomFloats(generator) * 2.0 - 1.0, 0.0f);
noise = glm::normalize(noise);
ssaoNoise.push_back(noise);
}
...
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, 4, 4, 0, GL_RGB, GL_FLOAT, &ssaoNoise[0]);
Я могу визуализировать шум в фрагментном шейдере, чтобы он работал.
глубина образца
Я преобразую все выборки из тангенса в пространство обзора (выборки являются случайными между [-1,1] на оси xy и [0,1] на оси z и транслирую их в текущую позицию фрагмента в пространстве вида (начало координат).
Затем я делаю выборку из линеаризованного буфера глубины (который я визуализирую ниже, если смотреть близко к объекту):
и, наконец, сравните выбранные значения глубины со значением глубины текущего фрагмента и добавьте значения окклюзии. Обратите внимание, что я не выполняю проверку диапазона, так как не верю, что это является причиной такого поведения, и я бы предпочел сделать его как можно более минимальным на данный момент.
Я не знаю, что вызывает такое поведение. Я считаю, что это где-то в выборке значений глубины. Насколько я могу судить, я работаю в правильной системе координат, линеаризованные значения глубины также находятся в пространстве вида, и все переменные установлены несколько правильно.
Задача ещё не решена.