OpenGL 3+,具有定向光的正交投影 [英] OpenGL 3+ with orthographic projection of directional light

查看:118
本文介绍了OpenGL 3+,具有定向光的正交投影的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm currently having an issue with directional light shadow maps from a moving (sun-like) light source.

当我最初实现时,光投影矩阵被计算为3D,并且阴影贴图看起来很漂亮.然后我了解到,对于我想做的事情,正交投影会更好地工作,但是我很难替换适当的投影矩阵.

When I initially implemented, the light projection matrix was computed as 3D, and the shadow map appears beautifully. I then learned that for what I'm trying to do, an orthographic projection would work better, but I'm having a hard time substituting the proper projection matrix.

每次打勾时,太阳都会沿圆周移动一定量,正如人们所期望的那样.我使用本地的"lookAt"方法确定正确的查看矩阵.因此,例如,白天发生在上午6点至下午6点.当太阳位于9AM位置(45度)时,应查看原点并将阴影贴图渲染到帧缓冲区.正射投影似乎正在发生的事情是,它并没有向原点倾斜".相反,它只是一直沿Z轴向下看.早上6点和下午6点一切正常,例如,中午12点,则什么也没显示.

Each tick, the sun moves a certain amount along a circle, as one would expect. I use a homegrown "lookAt" method to determine the proper viewing matrix. So, for instance, daylight occurs from 6AM to 6PM. When the sun is at the 9AM position (45 degrees) it should look at the origin and render the shadow map to the framebuffer. What appears to be happening with the orthographic projection is that it doesn't "tilt down" toward the origin. It simply keeps looking straight down the Z axis instead. Things look fine at 6AM and 6PM, but 12 noon, for instance, show absolutely nothing.

这是我进行设置的方式:

Here's how I'm setting things up:

原始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),我尝试使用许多不同的值进行此操作:

Orthographic projection (using width 100, height 75, near 1.0, far 100 ) I've tried this with many many different values:

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投影矩阵中可以很好地工作,但是我希望阴影随着用户在世界各地旅行而保持恒定,这对于定向照明以及正交投影是有意义的.

I feel that I'm missing something very simple here. As I said, this works fine with a 3D projection matrix, but I want the shadows constant as the user travels across the world, which makes sense for directional lighting, and thus orthographic projection.

推荐答案

实际上,谁告诉您使用正交投影矩阵对阴影贴图是个好主意?这对于像太阳这样的事物可能有效,它们实际上无限地遥远,但是对于局部照明而言,透视非常重要.但是,您必须谨慎处理透视投影和阴影贴图,因为采样频率会随距离而变化,并且最终会在某些距离获得很多精度,而在其他距离则没有足够的精度,除非您通常使用级联或透视变形之类的方法.不过,这可能比您目前应该考虑的要多:)

Actually, who told you that using an orthographic projection matrix would be a good idea for shadow maps? This might work for things like the sun, which are effectively infinitely far away, but for local lights perspective is very relevant. You have to be careful with perspective projection and shadow maps though, because the sample frequency varies with distance and you wind up getting a lot of precision at some distances and not enough at others unless you use things like cascading or perspective warping in general; this is probably more than you should be thinking about at the moment though :)

此外,正交投影矩阵与透视图相同或更多,只要它们通过将3D图像"投影到2D观察平面上即可工作……它们和透视图之间的唯一区别是平行线保持平行.换句话说,(x,y,near)和(x,y,far)在正投影中理想地投影到屏幕上的相同位置.


您在片段着色器中使用gl_FragCoord.z是不寻常的.由于这是写入深度缓冲区的值,因此您最好在片段着色器中写 NOTHING 并重新使用深度缓冲区.除非您的实现不支持浮点深度缓冲区,否则您将深度写入两个位置会浪费内存带宽.构造阴影贴图时,使用glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)进行的仅深度传递通常会为您带来更高的吞吐量.

Also, orthographic projection matrices are no more or no less 3D than perspective, insofar as they work by projecting a 3D "image" onto a 2D viewing plane... the only difference between them and perspective is that parallel lines remain parallel. Put another way, (x,y,near) and (x,y,far) ideally project to the same position on screen in an orthographic projection.


Your use of gl_FragCoord.z in the fragment shader is unusual. Since this is the value that is written to the depth buffer, you might as well write NOTHING in your fragment shader and re-use the depth buffer. Unless your implementation does not support a floating-point depth buffer you are wasting memory bandwidth by writing the depth to two places. A depth-only pass with glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE) will usually get you much higher throughput when constructing shadow maps.

如果您实际上使用了pass_Position的值(这是您在剪贴空间中非透视校正的Z坐标),则可以看到使用了一个单独的颜色附件来编写它,但是您是在编写透视校正的深度范围调整后的深度(gl_FragDepth)当前.

If you actually used the value of pass_Position (which is your non-perspective corrected Z coordinate in clip-space), I could see using a separate color attachment to write this, but you're writing the perspective-correct depth-range adjusted depth (gl_FragDepth) currently.



在任何情况下,当太阳直接在头顶上方并且您使用正投影时,都不会投射阴影.回到我之前解释的属性,在该属性中,平行线保持平行.由于物体与太阳的距离对投射物体的位置(正交)没有影响,因此,如果直接在上方,您将看不到任何阴影.尝试沿着球形而不是圆形跟踪太阳的位置,以将其最小化.



In any case, when the sun is directly overhead and you are using orthographic projection it is to be expected that no shadows are cast. This goes back to the property I explained earlier where parallel lines remain parallel. Since the distance an object is from the sun has no affect on where the object is projected (orthographically), if it is directly overhead you will not see any shadows. Try tracking the sun's position along a sphere instead of a circle to minimize this.

这篇关于OpenGL 3+,具有定向光的正交投影的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆