Я пытался реализовать окружающее затенение экранного пространства в соответствии с инструкциями этот учебник. Я решал проблемы с моей реализацией по мере того, как сталкивался с ними, но на данный момент это поставило меня в тупик.
Мое понимание метода следующее. Фактор окружающего затенения определяется образцами, взятыми из полушария, выровненного по нормали к данному фрагменту. Чтобы определить, вносит ли образец вклад в фактор окружающего затенения, я должен сравнить глубину образца в пространстве обзора с текстурой глубины пространства обзора (включена в нижний левый угол изображений этого поста). Итак, я знаю, какие координаты получить из текстуры глубины, я должен преобразовать координаты образца из пространства просмотра в нормализованные координаты устройства (в диапазоне [-1,1]), а затем в диапазоне [0 ,1], поэтому текстура глубины эффективно «сопоставляется» с окном просмотра.
На следующем изображении моя окружающая окклюзия наложена на мою сцену. Я знаю, что у меня есть довольно очевидная проблема с самой окружающей окклюзией (я предполагаю, что полушария ориентированы неправильно), с которой я разберусь со временем, но в настоящее время мое любопытство вызывает внешний вид окклюзии. ', предполагая, что моя операция по переходу от координат образца пространства обзора к координатам текстуры неверна.
Поскольку мне не хватает стабильного отладчика шейдеров, отладка, которую я могу выполнять, ограничена тем, что я могу отобразить на экране. Следующее изображение создается с помощью следующего кода, где ndcs — это нормализованные координаты устройства для данного образца.:
if (ndcs.x > 1.0f || ndcs.y > 1.0f || ndcs.x < -1.0f || ndcs.y < -1.0f)
{
gl_FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);
}
else
{
gl_FragColor = vec4(vec3(1.0f), 1.0f);
}
Я ожидаю, что изображение будет полностью белым (точнее, биты, для которых я использую этот шейдер), однако кажется, что NDC, которые я создаю, находятся за пределами диапазона [-1,1], который я полагать, должно быть неверным. Это также не постоянная область экрана, как вы можете видеть на следующем изображении, где камера находится очень близко к поверхности:
Я никогда раньше не использовал эту процедуру для получения NDC, поэтому я уверен, что моя логика где-то ошибочна. Я скачал демонстрационный код, прилагаемый к руководству, и не вижу, чем мой код отличается. Я также искал в Интернете (в том числе на этом самом сайте) и, похоже, не смог найти никого с такими же симптомами, как у меня.
Вот соответствующий код из моих шейдеров:
Вертикальный шейдер:
v_eye_space_position = u_mvpMatrix * a_position;
v_world_space_normal = normalize(u_rotationMatrix * a_normal);
v_eye_space_normal = normalize(u_mvpMatrix * a_normal);
gl_Position = v_eye_space_position;
Фрагмент шейдера:
// --- SSAO Testing ---
// Convert from the noise texture back to [-1,1] range
// We want the noise texture to tile across the screen.
vec3 kernel_rotation = (texture2D(s_noise, gl_FragCoord.xy * u_noise_scale) * 2.0f - 1.0f).xyz;
vec3 eye_space_tangent = normalize(kernel_rotation - v_eye_space_normal.xyz * dot(kernel_rotation, v_eye_space_normal.xyz));
vec3 eye_space_bitangent = cross(v_eye_space_normal.xyz, eye_space_tangent);
mat3 tbn = mat3(eye_space_tangent, eye_space_bitangent, v_eye_space_normal);
float ambient_occlusion = 0.0f;
const float hemisphere_radius = 0.05f;
for (int i=0; i<16; i++)
{
vec3 kernel_sample = tbn * u_ssao_kernel[i];
kernel_sample = kernel_sample * hemisphere_radius + v_eye_space_position.xyz;
// Project the sample position into screen space.
vec4 offset = vec4(kernel_sample, 1.0f);
offset = u_projection_matrix * offset;
offset.xy /= offset.w;
vec4 ndcs = offset;
offset.xy = 1.0f - (offset.xy * 0.5f + 0.5f);
// Find the depth at this sample point.
float sample_depth = texture2D(s_camera_depth, offset.xy).z;
// Check if the sample point is occluded.
float range_check = 0.0f;
float linear_eye_space_position = (v_eye_space_position.z - u_near_plane)/(u_far_plane - u_near_plane);
// Range check.
if (abs(linear_eye_space_position - sample_depth) < hemisphere_radius)
{
range_check = 1.0f;
}
float linear_kernel_sample_depth = (kernel_sample.z - u_near_plane)/(u_far_plane - u_near_plane);
if (sample_depth <= linear_kernel_sample_depth)
{
ambient_occlusion += 1.0f * range_check;
}
}
// Average and invert the ambient occlusion.
ambient_occlusion = 1.0f - (ambient_occlusion/16.0f);
Я рассмотрел каждый элемент по отдельности и не вижу в них проблемы.
- Я подставил собственное положение фрагмента в пространстве обзора в проекцию (вместо положения образца в пространстве обзора) и получил те же результаты.
- Матрица проекции — это матрица перспективы, которую я использую для преобразования вершин модели (я построил матрицу MVP в своем вершинном шейдере, чтобы гарантировать, что матрица проекции достигает программы шейдера в такте, и это так).
- Сама операция проецирования - это не то, что я делал раньше, но я читал статьи в Интернете и вопросы от людей с проблемами проецирования, и я не вижу, что я делаю неправильно.
Таким образом, я могу только заключить, что в моем понимании перспективной проекции должно быть что-то фундаментальное, что ошибочно, но я просто не могу понять, что именно. Если кто-нибудь из вас, ребята, может пролить свет на проблему или дополнительные возможности для проверки, я был бы очень признателен. Если есть какая-то полезная информация, которую я пропустил, или что-то, что я могу уточнить, просто дайте мне знать.