5
\$\begingroup\$

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?

\$\endgroup\$
5
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.