OpenGL 3+,具有定向光的正交投影

Posted

技术标签:

【中文标题】OpenGL 3+,具有定向光的正交投影【英文标题】:OpenGL 3+ with orthographic projection of directional light 【发布时间】:2013-08-31 03:08:50 【问题描述】:

我目前遇到了来自移动(类太阳)光源的定向光影贴图的问题。

当我最初实现时,光投影矩阵被计算为 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;

我觉得我在这里遗漏了一些非常简单的东西。正如我所说,这适用于 3D 投影矩阵,但我希望阴影在用户环游世界时保持不变,这对于定向照明和正交投影是有意义的。

【问题讨论】:

【参考方案1】:

实际上,谁告诉你使用正交投影矩阵对阴影贴图来说是个好主意?这可能适用于像太阳这样的物体,它们实际上是无限远的,但对于局部光而言,透视是非常相关的。但是,您必须小心透视投影和阴影贴图,因为采样频率会随距离而变化,并且您最终会在某些距离处获得很高的精度,而在其他距离处则不够精确,除非您通常使用级联或透视扭曲之类的东西;不过,这可能比你现在应该考虑的要多:)

此外,正交投影矩阵与透视图一样是 3D 矩阵,因为它们通过将 3D“图像”投影到 2D 观察平面上来工作......它们与透视图之间的唯一区别是平行线保持平行.换句话说,(x,y,near) 和 (x,y,far) 在正交投影中理想地投影到屏幕上的相同位置。 您在片段着色器中使用gl_FragCoord.z 是不寻常的。由于这是写入深度缓冲区的值,您不妨在片段着色器中写入 NOTHING 并重新使用深度缓冲区。除非您的实现不支持浮点深度缓冲区,否则您通过将深度写入两个位置来浪费内存带宽。在构建阴影贴图时,使用 glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE) 的仅深度通道通常会为您带来更高的吞吐量。

如果您实际上使用了 pass_Position 的值(这是您在剪辑空间中的非透视校正 Z 坐标),我可以看到使用单独的颜色附件来编写此内容,但您正在编写透视正确depth-range 调整深度 (gl_FragDepth) 当前。

在任何情况下,当太阳直接在头顶并且您正在使用正交投影时,预计不会投射任何阴影。这可以追溯到我之前解释的平行线保持平行的属性。由于物体与太阳的距离不会影响物体的投影位置(正交),如果它直接在头顶上,您将看不到任何阴影。尝试沿着球体而不是圆圈跟踪太阳的位置,以尽量减少这种情况。

【讨论】:

从我所看到的(因为这是一种类似太阳的光),正交透视更适合我想要的。我明白 3D 透视对于定位光会更好。 @Chris:当然,我并不是说正交投影不合适。只是将来您可能必须实现这两种类型的阴影贴图。点光源和位置聚光灯应该使用透视。另外,要在中午解决问题,我建议您的太阳以球面运动在天空中移动(例如,x、y 和 z 位置都是时间的函数,而不是您在问题中描述的三个中的两个) glFragCoord.z 与 pass_Position 是我一直在切换的东西,因为我缺乏对差异的理解,但你已经为我解决了这个问题。最后,对开销的解释是正确的,并且至少是我所缺少的一部分。不过,这个问题确实比这更早开始。就像阴影贴图从缓冲区“掉下来”一样。我会发布一些图片,看看我是否可以更好地解释它。 我认为我真的需要重新审视这一点。阴影坐标不像在投影中那样工作。我在错误的地方看到奇怪的阴影,然后直到它到达原点。负 x 完全点亮。我会说,在进行“lookAt”时将光线移近原点似乎对此有很大帮助。虽然我不确定我能说出原因...我要开始理解这些阴影还有很长的路要走,但我非常感谢您的帮助。

以上是关于OpenGL 3+,具有定向光的正交投影的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL - 正交投影矩阵,glViewport

如何使用正交投影在 OpenGL Core 3.3 中绘制一个圆?

OpenGL中的正交投影问题

opengl中的正交投影和纹理坐标

在 C++ 中的 OpenGL 中将坐标从 3D 透视投影映射到 2D 正交投影

opengl 的投影和正交矩阵