I've tweaked tutorial04.cpp a bit from http://www.opengl-tutorial.org/beginners-tutorials/tutorial-4-a-colored-cube/
I've made a pyramid shape and I wanted to place the camera above the pyramid and look down at it, but what I found is when the camera is directly above the pyramid it doesn't render at all. If I change my X value to be anything other than 0, it renders fine.
If I set X to be -0.0000001f or 0.0000001f, it renders fine as well.
The code below does not work.
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)
);
This code works fine
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)
);
I have 5 points that I am using to draw 6 triangles.
a: 0,1,0
b: 1,0,-1
c: 1,0,1
d: -1,0,1
e: -1,0,-1
Triangle 1: a, b, c
Triangle 2: a, c, d
Triangle 3: a, e, b
Triangle 4: a, e, b
Triangle 5: b, d, c
Triangle 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
};
You're not the first one to fall for this effect though: link
There're two solutions:
lookAt
function at all and represent your camera's rotations with a quaternion;What is the problem exactly?
I tried to dig into glm
code, and there's how lookAt
looks like
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;
}
If your camera looks straight up or down, then f
would be (0,1,0), s
would be always (0,0,0), because up
and f
are collinear, so their cross product is zero, and u
is (0,0,0) too, because s
is. So the result matrix you get is:
[0 0 0 0]
[0 0 1 0]
[0 0 0 0]
[0 0 0 1]
If you know some linear algebra, you know this matrix has rank 2, and when it's applied for graphics rotations and translations, it collapses everything you would see into a low-dimensional linear space (I'm not confident about the maths, but this is the idea). You basically lose one or more dimensions of 3D space, and as it's then projected on your 2D screen you might see a 1D image which is a thin line, that cannot be seen at all.
This might be also an implicit particular case of a Gimbal Lock - an effect when intuitive "X-Y-Z" rotations might end up loosing 1 or more degree of freedom, making it impossible to "unrotate" the space back. That's where quaternions come up, they don't suffer from Gimbal Lock, they require less memory to store in comparison to mat4, they're faster to compute, they're harder to explain and understand (but glm
has an implementation of it, so use it). Keep in mind that you shouldn't modify components of a quaternion on your own, or it'll become invalid, so instead only use functions that know how to modify them).
As a side note, it perfectly explains why in video games you can't turn your camera straight up to shoot from a bow up and then catch your arrow with your head. Arrow always flies a bit off vertical, because your camera is never straight up