🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Perspective formuila problems

Started by
2 comments, last by Sword7 1 year, 3 months ago

I have a problem with my perspective function call. I tried that but my picture looks like black hole at the center. Stretched scene around the center. It show stars in background but stretched away from the center. I tried that commented codes in middle of function call. It worked fine but… Stars in background are clipped out because they are trillion kilometers (miles) away. I tried near and far values (1.0 and 1e10).

I looked at standard perspective formula from OpenGL reference or computer graphics books and tried that. It did not work.

    template <typename T> glm::dmat4 perspective(T fov, T aspect, T zNear, T zFar)
    {
        const T half = fov / static_cast<T>(2.0);
        const T ctg = cos(half) / sin(half); // cot(half)
        const T zDelta = zFar - zNear;

        glm::dmat4 m(1.0);
        m[0][0] = ctg / aspect;
        m[1][1] = ctg;
        // m[2][2] = zFar / (zNear - zFar);
        // m[2][3] = static_cast<T>(-1.0);
        // m[3][2] = zNear * zFar / (zNear - zFar);
        m[2][2] = -(zFar + zNear) / zDelta;
        m[2][3] = static_cast<T>(-2.0) * zNear * zFar / zDelta;
        m[3][2] = static_cast<T>(-1.0);
        m[3][3] = static_cast<T>(0);

        return m;
    }

Also I tried infinite perspective. It worked fine but local area are clipped out.

    // Indefinite perspective for rendering stars
    template <typename T> glm::dmat4 infinitePerspective(T fov, T aspect, T zNear)
    {
        const T angle = fov / static_cast<T>(2.0);
        const T ctg = cos(angle) / sin(angle); // cot(angle)
    
        glm::dmat4 m(1.0);
        m[0][0] = ctg / aspect;
        m[1][1] = ctg;
        m[2][2] = static_cast<T>(0.0);
        m[2][3] = static_cast<T>(-1.0);
        m[3][2] = zNear;
        // m[2][2] = static_cast<T>(-1.0);
        // m[2][3] = static_cast<T>(-2.0) * zNear;
        // m[3][2] = static_cast<T>(-1.0);

        return m;
    }

Does anyone have any solutions for both perspective function calls?

Advertisement

I don't think you will be able to render realistic-scale space scenes as you are attempting. First, the graphics hardware works predominantly in single precision, which is only enough for a few km. Big numbers anywhere are likely to cause problems.

There are a few possible solutions to the problem of scale:

  1. Reverse floating point depth buffer. This only works in OpenGL 4.5+ because you need glClipControl. I haven't personally tried this because my computer (MacOS) doesn't support that OpenGL, but in theory it should have sufficient depth precision to render scenes of any size.
  2. Multiple render passes. I use this approach in my renderer, though I would prefer reverse depth if I could due to the simplicity of a single pass. Multi-pass rendering works by splitting the camera frustum into multiple depth slices of progressively larger sizes. You need 3-4 slices for solar-system scale, because in each slice your far plane will be ~10000x the near plane. So your first slice might be 0.1m to 1000.0m, then 1000.0 to 1e7, then 1e7 to 1e11, then finally 1e11 to 1e15. The slices are rendered in back to front order, and between each one you clear the depth buffer. Objects that intersect the depth slice planes are rendered in both slices. Farther slices are scaled down so that they fit within the same depth region as the nearest slice. This involves scaling model-view matrices, and any shader constants like light attenuation that depend on distance. The scaling is invisible to the camera if done correctly.

Ok, I now got it. I checked GLM code and noticed that same codes comparing with my both function calls. I decided to delete mine and use GLM function calls instead. GLM has glm::infinitePerspective function call as well. I tried my new logarithmic dolly movement. When I moved away from the planet, it graudally faded in the distant (a few hundreds thousand kilometers away). If I increased near value to 1000.0, it will not fade away anymore - contuning to shrinking and gone. I use OpenGL 4.3 profile. I will change to OpenGL 4.5+ profile. Linux do not support above OpenGL 4.3 as default without offical vendor drivers.

I searched more and more. I finally found logarithmic depth buffer information that can deal with very small near and very large far for planetary systems, etc. Also it can be found in my 3D Engine for Virtual Globe book. I will implement that soon. I will use multi-frustum passes instead. My book explain about many methods like multi-frustum, logarithmic depth buffer, etc.

This topic is closed to new replies.

Advertisement