Precision
Dealing with precision loss:
GPU precision:
In order to actually display a large scale terrain system I had to address the issue of precision loss, originally I was translating the camera around like it is done in most games, but when the camera was very near the surface the more detailed chunks would start to jitter about.
To combat this I am now subtracting the camera’s position from each object’s position prior to rendering said object. In the shader I only need to rotate the object, and now everything is relative to the origin of (0,0,0) and precision loss is minimized. On the CPU side I could use doubles, but as of yet it has not been necessary, and the jitter is completely gone.
Noise precision:
Another place where precision has become an issue is in the generation of the noise data.
Essentially noise assigns random values to points in space and then interpolate between them
to create a smooth signal. By adding these octaves/signals together you can create something
that looks like a mountain range etc. I am using many octaves for my noise, each using a
higher frequency and lower amplitude then the previous.
In order to create a spherical planet I am running a short post process algorithm on the
noise results which alters the signal based on its distance from the surface of a
perfect sphere, points under the surface have their density increased and points above
have their density decreased. I was using SSE to process 32bitx4 floats, since the
process involves a square root(I could use distance squared, and may switch to it at
some point, but it was less stable when I tested it out; the drop off from solid->air
was exponential instead of linear).
SSE, unlike x86, which has 80 bits internally, really only has 32 bits of precision so
as the camera descended toward the planets surface some visible stair step effects
could eventually been seen.
I have switched to using SSE2 64bitx2 doubles during the “make spherical” step,
and happily the stair step effect is gone.
Here is the inner loop in SSE2, this gives me chance to test out posting of source
code…(sorry the syntax highlighting is terrible.. and the spacing..this thing
fails at understanding tabs..)
NOTE: All the input(position/radius) was scaled down prior to running this code in order
to increase the range from 100% solid to 100% air.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | for(int i = 0;i<8;++i) { for(int j =0;j<8;++j) { zyPosSqrDist = (zSqrBuf[i] + ySqrBuf[j]); //Sum square Z and Y positions SquareDistZY = _mm_set1_pd((zyPosSqrDist)); for(int m = 0;m<4;m+=2,bufOffset+=4) { CurBufferValue = _mm_load_ps(&buffer[bufOffset]); //Load the 4 32bit noise values SurfaceVal =_mm_sub_pd(Radius, //Subtract distance from planets radius _mm_sqrt_pd( //sqrt to get real distance _mm_add_pd(SquareDistZY, //Add ZY square to X square to get distance squared xSqrBufSSE[m]))) ; //X square distance for this row LowerBuf = _mm_cvtpd_ps(SurfaceVal); //Set lower 2 spherical offsets SurfaceVal =_mm_sub_pd(Radius, //Subtract distance from planets radius _mm_sqrt_pd( //sqrt to get real distance _mm_add_pd(SquareDistZY, //Add ZY square to X square to get distance squared xSqrBufSSE[m+1]))) ; //X square distance for this row UpperBuf = _mm_cvtpd_ps(SurfaceVal); //Set upper 2 spherical offsets OutBuffer = _mm_movelh_ps(LowerBuf,UpperBuf); //pack them together //Add the 4 spherical offsets to the 4 noise values CurBufferValue = _mm_add_ps(CurBufferValue,OutBuffer); //Now write back to buffer _mm_store_ps(&buffer[bufOffset],CurBufferValue); } } } |
There is still a very faint darkened line that appears when the camera is extremely
close to the surface, basically when the near plane is intersecting with it.
I need to investigate further but I suspect it may caused by the extremely
small intervals which are being interpolated in the noise once ~15 octaves has
been reached.
The terrain normals are currently just 3x8bit normals, which so far has
worked surprisingly well, but I may switch to something along the lines of 10/11bits
for X/Y and packing the sign of Z into the leftover bits. More work in the shader
to decompress it but possibly worth the processing time.
Terrain Improvements:
Sliver removal: Marching cubes tends to generate many small triangles,
some so small they contribute nothing to the overall model, and in truth make it look
worse as they create shimmering when the camera moves. So I am now processing
the chunks to remove all sliver triangles. The process is controlled by a float
specifying the relative minimum allowed distance between vertices from 0.0-1.0.
At a moderate setting it removes around 20%-30% of the triangles and I cannot
really see any reduction in the quality of the terrain.
And finally..
Balancing the amplitude of each octave so that it doesn’t introduce more information to the signal than the visuals can support is turning out to be very important. This greatly reduces the amount of “floaters” and other unsightly looking problems found in the image.
In the following screens I’ve found a much better balance then in any of the previous screens
I have posted, though a few small floaters are visible and the sharp edge issue is still present.
Again, after the light has been moved.

This shot was taken prior to the precision fix(actually they all were), if you zoom in
on the lower right corner you can see some ugly tearing/stair step starting to show.















