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

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

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

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

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

Оригинальная 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;
}

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


person Chris    schedule 31.08.2013    source источник


Ответы (1)


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

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


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

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



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

person Andon M. Coleman    schedule 01.09.2013
comment
Судя по тому, что я видел (и поскольку это солнечный свет), ортоперспектива больше подходит для того, что я хочу. Я понимаю, что трехмерная перспектива была бы лучше для позиционированных источников света. - person Chris; 01.09.2013
comment
@Chris: Определенно, я не говорю, что орфографическая проекция неуместна. Просто вам, вероятно, придется реализовать оба типа карт теней в будущем. Точечные и позиционные прожекторы должны использовать перспективу. Кроме того, чтобы решить проблему в полдень, я бы предложил, чтобы ваше солнце двигалось по небу сферическим движением (например, положение x, y и z было бы функцией времени, а не только двумя из трех, как вы описали в вопросе ) - person Andon M. Coleman; 01.09.2013
comment
Я переключался между glFragCoord.z ​​и pass_Position, потому что мне не хватало понимания разницы, но вы прояснили это для меня. Наконец, объяснение накладных расходов является точным и, по крайней мере, частью того, что мне не хватало. Однако на самом деле проблема начинается раньше. Как будто карта теней отваливается от буфера. Я опубликую несколько изображений, чтобы посмотреть, смогу ли я объяснить это лучше таким образом. - person Chris; 01.09.2013
comment
Я думаю, что мне действительно нужно пересмотреть это больше. Координаты тени не работают, как в проекции. Я вижу странные тени в неправильных местах и ​​только до тех пор, пока не доберусь до источника. Отрицательный x полностью освещен. Я скажу, что перемещение источника света ближе к источнику при выполнении lookAt, похоже, очень помогло. Хотя я не уверен, что могу сказать, почему... Мне предстоит пройти долгий путь, чтобы начать понимать эти тени, но я ценю помощь. - person Chris; 01.09.2013