Small note about precision

So as anyone who’s worked on a planet renderer knows, precision becomes an issue very early in the process. When you’re rendering a planet with a radius of 6378000 units, 32 bit float Vector3’s simply aren’t precise enough to keep all those little vertices in the right spot at the meter-level. When you go near them the imprecision shows its ugly head by what could aptly be described as wobbling.

The problem needs a little bit of explanation of the regular way to render in order to illustrate the differences between the two, and how the resulting solution addresses the problem. The camera position is assumed to be at {0,0,0} in the following examples.

The regular way

You create your vertex at say, {0,0,6378000} in object-space during initialisation. When drawing, you pass the world matrix for the Planet that this vertex belongs to into the shader, which is basically just a translation since we aren't applying any scaling or rotation at the moment. If our planet was at {0,0,-6379000} our vertex would end up being drawn at {0,0,1000} after transformation via the planets world matrix. The problem is, having the GPU operate on such large FP32 values results in imprecision, so instead of the vert ending up at {0,0,1000} it may end up at {0,0,999.98} or {0,0,1000.02} (slightly exaggerated) which is not consistent between frames. So if you just look around using the camera without moving, you will see the vert wiggle all over the place within a certain range of its original position. Even if you apply the transformation on the CPU side and send through the translated position, you’re still using FP32 Vec3’s and the same imprecision occurs.

The solution

The solution is fairly straight forward, more precision is required. All positions on all objects are now stored using double-precision (64-bit) Vector3’s (created from the Vector3 class shipped with XNA, reflected and exported), including the camera position, planet position, and all vertex positions. But there is a problem, XNA internally only works with Fp32Vec3’s as does the graphics hardware, they just don’t know what a double is. So, when we create the vertex for the planet we store two positions, the Fp64Vec3 which is outside the VertexBuffer, and the regular Fp32Vec3 in the vertex buffer. Each frame, if the camera moves, we get the relative position of the vertex to the camera position (both stored in Fp64Vec3) and we update the Fp32Vec3 in the vertex buffer with that relative position. This means that given the example above, the vertex passed to the GPU would be at position {0,0,1000} and not {0,0,6378000} and then translated. This means that the precision still occurs, but it happens so far away from the camera that the player will never ever see it.

The above solution is what I’ve implemented after I saw the symptomatic wobbling vertices on my planet when up close at high LOD, only took around an hour to implement a fix and now I have zero-wobble even at CM off the surface. I was expecting a decent performance hit, having to update thousands of verts positions each frame, but was surprised when there was literally, no noticeable hit. Unfortunately I didn’t take pics/vids of the wobbling to illustrate what to look for, oh well. :)


Tags:
Categories: XNA

38 Comments
Actions: E-mail | Permalink | Comment RSSRSS comment feed

Comments