为什么从上方直接俯视形状时不会渲染?

Jam*_*mes 3 c++ opengl glfw glm-math

我从http://www.opengl-tutorial.org/beginners-tutorials/tutorial-4-a-colored-cube/稍微调整了tutorial04.cpp

我制作了一个金字塔形状,我想将相机放置在金字塔上方并俯视它,但我发现当相机位于金字塔正上方时,它根本不会渲染。如果我将 X 值更改为 0 以外的任何值,则效果会很好。

当相机位于 1, 10, 0 时的金字塔

如果我将 X 设置为 -0.0000001f 或 0.0000001f,它也渲染得很好。

下面的代码不起作用。

glm::mat4 View = glm::lookAt(
    glm::vec3(0,10,0), // Camera
    glm::vec3(0,0,0), // and looks at the origin
    glm::vec3(0,1,0)  // Head is up (set to 0,-1,0 to look upside-down)
);
Run Code Online (Sandbox Code Playgroud)

这段代码工作正常

glm::mat4 View = glm::lookAt(
    glm::vec3(1,10,0), // Camera
    glm::vec3(0,0,0), // and looks at the origin
    glm::vec3(0,1,0)  // Head is up (set to 0,-1,0 to look upside-down)
);
Run Code Online (Sandbox Code Playgroud)

我有 5 个点,用来绘制 6 个三角形。

一个:0,1,0

b:1,0,-1

c:1,0,1

d:-1,0,1

e:-1,0,-1

三角形 1:a、b、c

三角形 2:a、c、d

三角形 3:a、e、b

三角形 4:a、e、b

三角形 5:b、d、c

三角形 6:d、b、e

static const GLfloat g_vertex_buffer_data[] = {
0,1,0,
1,0,-1,
1,0,1,//a,b,c
0,1,0,
1,0,1,
-1,0,1,//a,c,d
0,1,0,
-1,0,1,
-1,0,-1,//a,d,e
0,1,0,
-1,0,-1,
1,0,-1,//a,e,b
1,0,-1,
-1,0,1,
1,0,1,//b,d,c
-1,0,1,
1,0,-1,
-1,0,-1//4,2,5
};
Run Code Online (Sandbox Code Playgroud)

Ale*_*nov 5

不过,您不是第一个受到这种影响的人:链接

有两种解决方案:

  • 要么根本不使用lookAt函数并用四元数表示相机的旋转;
  • 或跟踪,并且永远不要为相机应用“垂直向上”(0,X,0) 或“垂直向下”(0,-X,0) 观看方向。

到底是什么问题呢?我尝试深入研究glm代码,看起来像lookAt这样

    template<typename T, qualifier Q>
    GLM_FUNC_QUALIFIER mat<4, 4, T, Q> lookAtLH(vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up)
    {
        vec<3, T, Q> const f(normalize(center - eye));
        vec<3, T, Q> const s(normalize(cross(up, f)));
        vec<3, T, Q> const u(cross(f, s));

        mat<4, 4, T, Q> Result(1);
        Result[0][0] = s.x;
        Result[1][0] = s.y;
        Result[2][0] = s.z;
        Result[0][1] = u.x;
        Result[1][1] = u.y;
        Result[2][1] = u.z;
        Result[0][2] = f.x;
        Result[1][2] = f.y;
        Result[2][2] = f.z;
        Result[3][0] = -dot(s, eye);
        Result[3][1] = -dot(u, eye);
        Result[3][2] = -dot(f, eye);
        return Result;
    }
Run Code Online (Sandbox Code Playgroud)

如果你的相机看起来笔直向上或向下,那么f将是(0,1,0),s将始终是(0,0,0),因为upf是共线的,所以它们的叉积为零,并且u是(0,0, 0) 也是,因为s是。所以你得到的结果矩阵是:

[0 0 0 0]
[0 0 1 0]
[0 0 0 0]
[0 0 0 1]
Run Code Online (Sandbox Code Playgroud)

如果你了解一些线性代数,你就会知道这个矩阵的秩为 2,当它应用于图形旋转和平移时,它会将你看到的所有内容折叠到低维线性空间中(我对数学没有信心,但是这个是这个想法)。您基本上会丢失一个或多个维度的 3D 空间,然后当它投影到 2D 屏幕上时,您可能会看到一个 1D 图像,它是一条细线,根本看不到。

这也可能是万向节锁的隐式特殊情况- 当直观的“XYZ”旋转可能最终失去 1 个或多个自由度时产生的效果,从而无法“取消旋转”空间。这就是四元数出现的地方,它们不会受到万向节锁的影响,与 mat4 相比,它们需要更少的内存来存储,它们计算速度更快,它们更难以解释和理解(但有glm它的实现,所以用它)。请记住,您不应自行修改四元数的组成部分,否则它将变得无效,因此只能使用知道如何修改它们的函数)。

顺便说一句,它完美地解释了为什么在视频游戏中你不能直接转动相机从弓上射击,然后用头接箭。箭头总是会偏离垂直方向一点,因为你的相机永远不会直立