OpenGL: проецирование координат пространства обзора на NDC, результаты, по-видимому, выходят за пределы диапазона [-1,1]

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

Мое понимание метода следующее. Фактор окружающего затенения определяется образцами, взятыми из полушария, выровненного по нормали к данному фрагменту. Чтобы определить, вносит ли образец вклад в фактор окружающего затенения, я должен сравнить глубину образца в пространстве обзора с текстурой глубины пространства обзора (включена в нижний левый угол изображений этого поста). Итак, я знаю, какие координаты получить из текстуры глубины, я должен преобразовать координаты образца из пространства просмотра в нормализованные координаты устройства (в диапазоне [-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 в своем вершинном шейдере, чтобы гарантировать, что матрица проекции достигает программы шейдера в такте, и это так).
  • Сама операция проецирования - это не то, что я делал раньше, но я читал статьи в Интернете и вопросы от людей с проблемами проецирования, и я не вижу, что я делаю неправильно.

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


person Richard Williams    schedule 02.10.2014    source источник


Ответы (1)


Из вашего вершинного шейдера:

v_eye_space_position = u_mvpMatrix * a_position;
[...]

pMatrix * a_normal); gl_Position = v_eye_space_position;

Из этого мы видим, что v_eye_space_position — это не положение вершины в пространстве глаза, а положение в пространстве отсечения, которое также необходимо назначить gl_Position. Название вашего матричного униформа также предполагает, что это ModelViewProjection-Matrix.

В вашем фрагментном шейдере вы в основном умножаете результат на матрицу проекции снова (поскольку вы, кажется, предполагаете, что она находится в пространстве глаза).

Таким образом, правильный код будет таким:

v_eye_space_position = u_mvMatrix * a_position;
[...]
gl_Position = u_projection_matrix * v_eye_space_position;

Теперь вы можете снова применить проекцию к v_eye_space_position во фрагментном шейдере. Но мой вопрос: зачем делать это снова? Если вы хотите работать в пространстве экрана, gl_FragCoord уже находится в пространстве окна. Вам нужно только multipliy-add, чтобы перейти из оконного пространства в NDC, просто инвертируя преобразование области просмотра (и диапазона глубины).

person derhass    schedule 02.10.2014
comment
Большое спасибо за вашу помощь в этом. После реализации ваших предложений, результаты, которые я ожидал. Забавно, как я ослепил себя до такой простой ошибки. Усвоенный урок: всегда проверяйте «простые» вещи (особенно координаты пространств). Еще раз большое спасибо - вы избавили меня от огромного стресса. Хорошего дня! :) - person Richard Williams; 03.10.2014