Finite difference normal calculation for sphere

Started by
4 comments, last by JoeJ 6 years, 6 months ago

Hi everyone,

I'm currently adapting a planar quadtree terrain system to handle spherical terrain (i.e. I want to render planets as opposed to just an endless terrain stretching in the X-Z plane).

A the moment, I use this algorithm to calculate the normal for a given point on the terrain from the height information (the heights are generated using simplex noise):


	public Vector3 GetNormalFromFiniteOffset(float x, float z, float sampleOffset)
        {
            float hL = GetHeight(x - sampleOffset, z);
            float hR = GetHeight(x + sampleOffset, z);
            float hD = GetHeight(x, z - sampleOffset);
            float hU = GetHeight(x, z + sampleOffset);
	            Vector3 normal = new Vector3(hL - hR, 2, hD - hU);
            normal.Normalize();
	            return normal;
        }
	

The above works fine for my planar quadtree, but of course it won't work for spherical terrain because it assumes the Y direction is always up. I guess I need to move the calculation into the plane which lies at a tangent on the sphere for the point I'm evaluating. I was wondering if anyone knows of a good or proven way to transform this calculation for any point on a sphere, or if there's a better way to calculate the normal for a point on a sphere which has been radially displaced using a noise-based height field?

I'm using XNA by the way. Thanks very much for looking!

Advertisement

A quick addition - I tried creating my own adaptation:


	public Vector3 GetNormalFromFiniteOffset(Vector3 location, float sampleOffset)
        {
            Vector3 normalisedLocation = Vector3.Normalize(location);
            Vector3 arbitraryUnitVector = Math.Abs(normalisedLocation.Y) > 0.999f ? Vector3.UnitX : Vector3.UnitY;
	            Vector3 tangentVector1 = Vector3.Cross(arbitraryUnitVector, normalisedLocation);
            tangentVector1.Normalize();
            Vector3 tangentVector2 = Vector3.Cross(tangentVector1, normalisedLocation);
            tangentVector2.Normalize();
	            float hL = GetHeight(location - tangentVector1*sampleOffset);
            float hR = GetHeight(location + tangentVector1*sampleOffset);
            float hD = GetHeight(location - tangentVector2*sampleOffset);
            float hU = GetHeight(location + tangentVector2*sampleOffset);
	            Vector3 normal = 2*normalisedLocation + (hL - hR)*tangentVector1 + (hD - hU)*tangentVector2;
            normal.Normalize();
	            return normal;
        }
	

I can't test it yet, but I wonder if anyone thinks this looks like a decent approach, or are there obvious issues with how I'm doing this?

Thanks again!

I assume you get discontinuities because of the arbitary tangents. It's impossible to move a orientation over the surface of a sphere without singularities.

If you use normals at discrete offsets (e.g. per vertex or per face) the dicontinuity might not matter, but if yo do it per pixel i expect a visible seam.

In the latter case the solution is to consider the singularities when calculating the normal - similar to a cubemap where in the corner you need to sample from 3 instead 4 texels, and projected angles become 120 instead 90 degrees. (The orthogonality you assume in your code breaks at the corners.)

Thanks for the reply - yes, I thought about what would happen if I used the same arbitrary vectors for every position, which is why I choose between the X direction or the Y direction depending on the Y component of the position. I was thinking this would ensure that the tangents always form an orthonormal basis around the point on the sphere.

it will result in different tangents for different positions, but I was thinking this wouldn't matter as long as I was taking samples across two perpendicular directions in the tangent plane.

The tangents are orthogonal, but if you would rotate them slightly, you get slightly different results because you sample from different neighbouring positions. And this is what happens when the branch alternates between X and Y, you get large discontinuities in tangent directions at the branch point, and this can cause a visible seam.

The problem is unavoidable - you can't cover a sphere with a net of orthonormal isolines without introducing singularities, so you can't calculate a orthonormal basis around the sphere without discontinuity in orientation. You could however trick the issue by ensuring the jumps in orientation are always 90 degrees. (Might be more efficient than special cases for edges / corners, but the math won't be super easy.)

You would be surprised how much impact this problem has on topics like surface parametrization, remeshing etc.

But as said chances are that in practice you won't notice the seam - i'm just nit picking. See how your solution works for you...

This topic is closed to new replies.

Advertisement