Reputation: 15
I write a program to draw one line.
The line sometimes disappear when I move camera to positive z-axis (especially when z-axis greater than 10000).
There are some test result.
When z set 20541, the line can be seen.
When z set 20542, the line CAN'T be seen.
When z set 30320, the line can be seen.
When z set 30321, the line CAN'T be seen.
and so forth ...
The code is attached. What's wrong?
P.S. The code is written by OpenGL 1.0, but I can still get the same test result when written by OpenGL 3.0 + glm library.
#include <glut.h>
/*
System Info
-------------
OS: Win7 professional 64-bit SP1
CPU: Intel i3-4170 @ 3.70GHz
GPU: HD Graphics 4400
*/
void display(void) {
// 20541 ok, 20542 not visible
// 30320 ok, 30321 not visible
const GLfloat z = 20541;
const GLfloat far = 1000, near = 0.1;
GLfloat vertices[4 * 3] = {
-far, -far, z - far,
far, far, z - far,
};
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0, 0, z, 0, 0, z - 1, 0, 1, 0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-0.1, 0.1, -0.1, 0.1, near, far);
glColor3f(0, 1, 1); // blue
glBegin(GL_LINES);
glVertex3f(vertices[0], vertices[1], vertices[2]);
glVertex3f(vertices[3], vertices[4], vertices[5]);
glEnd();
glFlush();
}
int main() {
glutCreateWindow("");
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
Upvotes: 1
Views: 758
Reputation: 22176
This issue seems to be a numerical instability of the floating point arithmetic. Since you are projecting points that are exactly on the far-plane, they get clipped when the floating-point result is a little bit larger than the expected result.
Let's assume a C++ implementation of what the gpu basically does:
glm::vec4 test_fp(float z)
{
//Construct matrices
auto ortho = glm::frustum(-0.1f, 0.1f, -0.1f, 0.1f, 0.1f, 1000.0f);
auto lookat = glm::lookAt(glm::vec3(0, 0, z), glm::vec3(0, 0, z - 1.0f), glm::vec3(0, 1, 0));
//We are only interested in the z-value
glm::vec4 tvec(0, 0, z - 1000.0f, 1);
//Calculate ndc vector
auto result = ortho * lookat * tvec;
//Homogenize
result /= result.w;
return result;
}
When now calling this function with the values you provided we get the following results:
auto a = test_fp(20541.0); //< [0, 0, 1.00000000, 1]
auto b = test_fp(20542.0); //< [0, 0, 1.00000191, 1]
auto c = test_fp(30320.0); //< [0, 0, 1.00000000, 1]
auto d = test_fp(30321.0); //< [0, 0, 1.00000191, 1]
As you can see, the results of b
and d
diverge from the mathematical correct result and are slightly above 1.0. Since values above 1.0 are behind the far-plane, they are clipped away and are not visible, which is exactly the behavior you have.
Upvotes: 1