Monday, November 25, 2019

xna - Generating spherical world from heightmapped terrain


I am using a standard heightmapped procedural terrain in my project. However, I want the terrain to appear spherical when the user zooms out. This is an attempt to simulate the appearance of a spherical planet. Here is my current algorithm:


        //get the direction from the "planet" center to this vert
Vector3 sphereCentertoVertPosition = vert.Position - SphereCenter;
sphereCentertoVertPosition.Normalize();


//our rotation axis is the cross between the direction from this vert to the planet center and the root (center of the terrain) to the planet center
RotationAxis = Vector3.Cross(sphereCentertoVertPosition, sphereCenterToRootPosition);

//the amount we rotate is based on the distance of this vert from the center of the terrain
Vector3 fromCenter = vert.Position - Root.Position;
float amount = (fromCenter.Length() / ((myTextureWidth / Scale) / 2)) * (float)Math.PI;

Quaternion rot = Quaternion.CreateFromAxisAngle(RotationAxis, amount);


Vector3.Transform(ref vert.Position, ref rot, out vert.Position);

My main concern is that the rotation axis is not correct. Theoretically, should it be the cross between the vert-to-planet-center and the terrain-center-to-planet-center?



Answer



Hmm, I'm not so sure about rotating the vertices. Perhaps this is not the answer you're looking for, but I can suggest an alternate method.


I've done something similar with maps in my game. I wrote a shader to wrap terrain into a sphere, but it can easily be done outside the graphics card too. It's as simple as mapping the Cartesian coordinates to spherical coordinates. The code would look something like:


public static Vector3f MapToSphere(Vector3f coords, WorldMap MAP) {
float pi = 3.14159265f;
float thetaDelta = ((2 * pi) / (MAP.XSize - 1));
float phiDelta = ((pi) / (MAP.ZSize - .5f));

float RadiusBase = ((MAP.XSize) / pi / 2f);
float theta = (coords.z * thetaDelta);
float phi = (coords.x * phiDelta);

//Limit the map to half a sphere
if(theta > pi) {theta = theta - (pi);}

if(theta < 0.0) {theta = theta + (pi);}

if (phi > 2*pi) {phi = phi - (2*pi);}


if (phi < 0.0) {phi = phi + (2*pi);}

Vector3f coords2 = new Vector3f();
coords2.x = (float) (((RadiusBase) * Math.sin(theta) * Math.cos(phi)) + MAP.XSize / 2f);
coords2.y = (float) ((RadiusBase) * Math.sin(theta) * Math.sin(phi));
coords2.z = (float) (((RadiusBase) * Math.cos(theta)) + MAP.ZSize / 2f);
return coords2;
}


This maybe "too spherical" for you. If so, you can try padding the edges of your map so that the portion turned into a sphere only represents a portion of the surface of the sphere.


Below is an example of the spherical mapping results.


A "flat" map:


enter image description here


And a spherical world, created by wrapping the vertices into a sphere:


enter image description here


Note that there will be a large amount of distortion at the poles if you don't follow the suggestion above to limit the surface area of the sphere this map is stretched over.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...