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:
And a spherical world, created by wrapping the vertices into a sphere:
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