OpenGL 3+ с ортографической проекцией направленного света

У меня сейчас проблема с картами теней направленного света от движущегося (подобного солнцу) источника света.

Когда я изначально реализовал, матрица проекции света была вычислена как 3D, и карта теней выглядит красиво. Затем я узнал, что для того, что я пытаюсь сделать, ортографическая проекция будет работать лучше, но мне трудно заменить правильную матрицу проекции.

Каждый тик солнце движется на определенную величину по кругу, как и следовало ожидать. Я использую доморощенный метод "lookAt", чтобы определить правильную матрицу просмотра. Так, например, дневной свет происходит с 6 утра до 6 вечера. Когда солнце находится в положении 9 утра (45 градусов), оно должно смотреть на начало координат и отображать карту теней в кадровый буфер. Похоже, что с орфографической проекцией происходит то, что она не "наклоняется" вниз к источнику. Вместо этого он просто смотрит прямо вниз по оси Z. Все выглядит хорошо в 6 утра и 6 вечера, но 12 часов, например, абсолютно ничего не показывают.

Вот как я это настраиваю:

Оригинальная матрица 3D проекции:

Matrix4f projectionMatrix = new Matrix4f();
float aspectRatio = (float) width / (float) height;

float y_scale = (float) (1 / cos(toRadians(fov / 2f)));
float x_scale = y_scale / aspectRatio;
float frustum_length = far_z - near_z;

projectionMatrix.m00 = x_scale;
projectionMatrix.m11 = y_scale;
projectionMatrix.m22 = (far_z + near_z) / (near_z - far_z);
projectionMatrix.m23 = -1;
projectionMatrix.m32 = -((2 * near_z * far_z) / frustum_length);

LookAt метод:

public Matrix4f lookAt( float x, float y, float z,
                      float center_x, float center_y, float center_z ) {
  Vector3f forward = new Vector3f( center_x - x, center_y - y, center_z - z );
  Vector3f up      = new Vector3f( 0, 1, 0 );

  if ( center_x == x && center_z == z && center_y != y ) {
    up.y = 0;
    up.z = 1;
  }

  Vector3f side = new Vector3f();

  forward.normalise();

  Vector3f.cross(forward, up, side );
  side.normalise();

  Vector3f.cross(side, forward, up);
  up.normalise();

  Matrix4f multMatrix = new Matrix4f();
  multMatrix.m00 = side.x;
  multMatrix.m10 = side.y;
  multMatrix.m20 = side.z;
  multMatrix.m01 = up.x;
  multMatrix.m11 = up.y;
  multMatrix.m21 = up.z;
  multMatrix.m02 = -forward.x;
  multMatrix.m12 = -forward.y;
  multMatrix.m22 = -forward.z;

  Matrix4f translation = new Matrix4f();
  translation.m30 = -x;
  translation.m31 = -y;
  translation.m32 = -z;

  Matrix4f result = new Matrix4f();

  Matrix4f.mul( multMatrix, translation, result );
  return result;
}

Ортогональная проекция (используя ширину 100, высоту 75, около 1,0, далеко за 100) Я пробовал это со многими многими различными значениями:

Matrix4f projectionMatrix = new Matrix4f();

float r = width * 1.0f;
float l = -width;
float t = height * 1.0f;
float b = -height;

projectionMatrix.m00 = 2.0f / ( r - l );
projectionMatrix.m11 = 2.0f / ( t - b );
projectionMatrix.m22 = 2.0f / (far_z - near_z);
projectionMatrix.m30 = - ( r + l ) / ( r - l );
projectionMatrix.m31 = - ( t + b ) / ( t - b );
projectionMatrix.m32 = -(far_z + near_z) / (far_z - near_z);
projectionMatrix.m33 = 1;

Вершинный шейдер карты теней:

#version 150 core

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;

in vec4 in_Position;

out float pass_Position;

void main(void) {
  gl_Position = projectionMatrix * viewMatrix * modelMatrix * in_Position;
  pass_Position = gl_Position.z;
}

Фрагмент шейдера карты теней:

#version 150 core

in vec4 pass_Color;
in float pass_Position;

layout(location=0) out float fragmentdepth;

out vec4 out_Color;

void main(void) {
  fragmentdepth = gl_FragCoord.z;
}

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

1 ответ

Решение

На самом деле, кто вам сказал, что использование ортогональной проекционной матрицы будет хорошей идеей для карт теней? Это может работать для таких вещей, как солнце, которые фактически бесконечно далеки, но для местных источников света перспектива очень важна. Вы должны быть осторожны с перспективными проекциями и теневыми картами, потому что частота выборки меняется в зависимости от расстояния, и вы получаете большую точность на некоторых расстояниях и недостаточно на других, если вы не используете такие вещи, как каскад или деформация перспективы в целом; это, вероятно, больше, чем вы должны думать в данный момент, хотя:)

Кроме того, матрицы ортогональной проекции являются не более или не менее трехмерными, чем перспективные, поскольку они работают, проецируя трехмерное "изображение" на плоскость двухмерного просмотра... единственное различие между ними и перспективой состоит в том, что параллельные линии остаются параллельными. Иными словами, (x,y, рядом) и (x, y, далеко) в идеале проецируйте в одну и ту же позицию на экране в ортографической проекции.


Ваше использование gl_FragCoord.z во фрагменте шейдер необычный. Поскольку это значение записывается в буфер глубины, вы также можете написать НИЧТО в своем фрагментном шейдере и повторно использовать буфер глубины. Если ваша реализация не поддерживает буфер глубины с плавающей точкой, вы теряете пропускную способность памяти, записывая глубину в два места. Проход только на глубину с glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE) обычно дает вам гораздо большую пропускную способность при построении карт теней.

Если вы на самом деле использовали значение pass_Position (это ваша Z-скорректированная координата Z в пространстве клипа), я мог бы увидеть, используя отдельное цветовое вложение, чтобы написать это, но вы пишете глубину, скорректированную с учетом глубины в перспективе (gl_FragDepth) В настоящее время.



В любом случае, когда солнце находится прямо над головой и вы используете ортогональную проекцию, следует ожидать, что тени не отбрасываются. Это восходит к свойству, которое я объяснил ранее, где параллельные линии остаются параллельными. Так как расстояние, на которое объект находится от солнца, не влияет на то, где объект проецируется (орфографически), если он находится прямо над головой, вы не увидите никаких теней. Попробуйте отследить положение солнца вдоль сферы вместо круга, чтобы минимизировать это.

Другие вопросы по тегам