Planet Surface Rendering: Precision issues

by RedRuin   Last Updated February 11, 2019 04:13 AM

I have been experimenting with C++ and OpenGL, and I am currently writing a program that renders the surface of a planet; specifically, our moon. It's a modified version of the GPU Gems implementation of Geometry Clipmaps which is normalized to the unit sphere and then offset by a heightmap in the terrain's vertex shader. On small scale scale this works rather well:

Rendering with a planet radius of 1,000 (heights exaggerated for clarity)

Radius = 10,000; side of the slope is still temporally smooth

However, I want to scale this up to the size of the actual moon. Unfortunately, when I do so, stepping artifacts appear:

Stepping artifacts on the side of a large crater

Now I am (almost) certain that this is simply an issue with floating point precision; 32-bit floats just simply don't have enough precision for planets of this size. However, I am in an interesting situation where I cannot simply change one of my numbers to a double to gain more precision. My vertex shader code is this:

layout (location = 0) in vec3 VertexPosition;

uniform mat4 MVP; //projection and camera rotation matrix
uniform mat4 PLM; //scale and translation matrix
uniform dmat4 SurfaceRotationMatrix; //transforms vertices to be the correct position on surface

uniform samplerCube bump_map; //heightmap cubemap

uniform dvec3 PlayerLocation;

uniform double PlanetRadius;
uniform double TerrainHeightScalar;

void main(){

    //find the world space position of the flat grid (high precision)
    dvec4 worldSpacePosition = PLM * vec4(VertexPosition, 1.0);

    ...

    //offset the y position by the planets radius
    worldSpacePosition.y += PlanetRadius;

    //vertex position in relation to camera (located at origin)
    dvec4 OriginNormal = normalize(worldSpacePosition); 
    //vertex position in relation to planets surface
    dvec4 surfaceHighP = SurfaceRotationMatrix * OriginNormal;  
    //also calculate surface normals at this point
    dvec4 tangentHighP = SurfaceRotationMatrix * vec4(1.0, 0.0, 0.0, 1.0);
    dvec4 bitangentHighP = dvec4(cross(surfaceHighP.xyz, surfaceHighP.xyz), 1.0);

    // vvv I think this is the problem vvv
    // dvec4 surfaceHighP has to be cast to a vec3 (float) in order for the 
    // shader to compile
    double height = texture(bump_map, vec3(surfaceHighP.xyz)).x; 

    //set position by multiplying the normal by the distance the point should be from the surface
    worldSpacePosition.xyz = (PlanetRadius + height * TerrainHeightScalar) * OriginNormal.xyz; 

    //move the position back to the origin
    worldSpacePosition.y -= PlanetRadius;

    //offset y position by the player's height (both statements are split for best precision)
    worldSpacePosition.y -= PlayerLocation.y;

    //final position: multiply by projection matrix
    gl_Position = MVP * vec4(worldSpacePosition);
}

OpenGL's texture() function only seems to take float parameters as an input. This makes me believe that this stepping is a result of the cast down to a lower-precision vector, where the program cannot distinguish the tiny change in angle between each grid position. At first I thought it might have been a texture precision error, so I tried switching the heightmap texture to an 8-bit copy but I didn't see any difference in step size or quantity. The texture filtering is linear, so the heights should be smoothly blended between one another, and at large scales big landforms like mountains and hills seem to have no associated problems.

Can anyone confirm that this cast in the texture() call is the reason for the stepping? If so, is there a way to pass high-precision numbers into a GLSL texture call, or, failing that, a solution to this problem that doesn't involve a complete redesign? I was thinking about maybe getting a weighted average of each neighboring "step", and then linearly interpolating between them, averaging between the areas where the float breaks down. Would something like that be possible, or is there a better alternative?



Related Questions




Any ideas on cloud rendering in Sky: Light Awaits?

Updated October 23, 2018 03:13 AM

Depth aware blur - Kawase

Updated December 26, 2017 23:13 PM