I am working on a new racing game for mobile devices.
In this game I have a plane for the ground which stretch to the horizon and a spherical sky panorama.
As you may know you can’t really render into infinite in OpenGL and even if you could the rasterization and resolution limitation would still make the horizon look jaggy and aliased.
In order to make the ground plane blend with the sky sphere I needed to make the ground pixels near the edge of the render frustum to blend with the background.
However, most of the ground does not need to blend with the background but rather a thin stripe near the far end of the view.
We can render the ground in two phases. One with regular shading where there is no fading and one from where the fading begins.
The fading is dependent on depth so we need to split the rendering based on depth.
Notice: Splitting the render based on depth can be useful performance wise or can be useful to simplfy shaders. In my case there wasn’t a big difference in performance in rendering the ground in two pieces(with and without blending).
So what we need is Depth Clipping.
Our frustum box already does clipping. It clips whatever we project into it that is outside the box of [-1..1]x[-1..1]x[-1..1]
How would we clip based on depth that is smaller than 1?
When rendering the scene in the racing game we are using a perspective projection matrix which is created with the following parameters:
Field of View angle(on Y axis), Aspect ratio, Near plane and Far plane.
For instance we can have a perspective camera with a field of view of 45 degrees, an aspect ratio of 4:3, a near plane of 1 and a far plane of 500.
Lets say we want to render an object in the scene but clip it on depth of 450 instead of 500.
We can create a separate projection matrix with the exact same parameters as in the example but with 450 in the far parameter instead of 500.
This will render the object clipped to 450.
However, notice that we said the frustum only clips at -1 and 1 on the depth axis.
With the original projection matrix we projected 500 into 1. With the current matrix we project everything the same(the x and y into [-1..1]x[-1..1]) but the depth is projected from 450 to 1.
While rendering the object to the screen with the 450 depth matrix is rendered correctly, it’s depth values are rendered inconsistently with the depth values of the original matrix(with the far set to 500).
This will cause the Z Buffer to behave incorrectly.
In order to fix this we change the range of the depth values rendered into the ZBuffer from [-1..1] into [a..b] where we choose the new a, b.
We do this by using the function glDepthRangef.
glDepthRangef accepts values between 0 and 1. Our frustum box depth values are between -1 and 1.
Which means in glDepthRangef 0 is mapped to -1 and 1 is mapped to 1.
How do we choose the range for glDepthRangef so the depth values with the 450 depth projection will match all the other objects with the original projection?
We use the original matrix to calculate where 450 is projected into the frustum. Like so:
vector4 p = OriginalProjection.MulPosition (vector3(0, 0, 450)); float newFar = p.z/p.w; newFar = 0.5*(newFar+1.0); glDepthRangef (0, newFar);
Another thing to remember:
glDepthRangef change the depth values written to the Z Buffer.
This means the vertex shader still need to write into depth values of [-1..1].
In addition, the fragment shader will still see the depth values between -1 and 1 and not the ones glDepthRangef set them to.