Centering to main camera a clicked point on sphere

Hi again

First the background-I have a sphere which I rotate horizontally in local space and vertically in world space.

Now I’d like to rotate my globe that clicked by mouse point was directly before Camera.main.forward.

I find such: Rotate Point A on Sphere to Point B on Sphere - Questions & Answers - Unity Discussions but that gives me incorrect results :confused:

So I started to develop own solution,which is find the angle between center and clicked points around OX and OY,then make Rotate(angleX,0,0) and Rotate(0,angleY,0).
Well,but there is a problem-how can I find those angles?I found only Vector3.Angle(),but this gives me idiotic results :confused:

Relative rotations will drive you crazy, use absolute angles.

Vector3 longDir = localPos;
longDir.y = 0;

float longitude = Vector3.Angle(-Vector3.forward,longDir);
if (longDir.x<0)
    longitude = -longitude;
float latitude = Mathf.Asin(localPos.normalized.y)*Mathf.Rad2Deg;

That will give you the lat / lon of the given point assuming that 0,0 is at -forward

I’ve built another example including source package :wink: here

In this example i nested the actual mesh to correct the rotation (so that the visual 0,0 is at 0,0 :wink: )

edit

I used the same coordinates as we use on our Earth. Longitude is the rotation around y-axis and is in range [-180, +180]. Latitude specifies “the angular distance of that location south or north of the Equator” and is in range [-90(south), 90(north)]. I guess that should be obvious.

I choose as reference point -forward as Lat=0, Lon=0.

If you have a vector from the earth center to an location somewhere on earth you get the longitude by projecting the vector to the equator plane (just zero the y-part of the vector) and then use Vector3.Angle to get the angle between my 0,0 reference and our x-z-Vector. The angle itself is always positive, so i check if the location is on the left side (x<0) i just negate the angle. Now we’ve got the longitude.

The latitude is only affected by the y-part of our location. y(normalized) = Sin(latitude).
To get the latitude angle from the normalized y value you just need to use Asin(y) to get the angle. In most math libraries (also Mathf) the trigonometric functions doesn’t work with angles in degree [0…360], they use radiant [0…2*PI] to specify an angle. Mathf comes with a conversion constant that can be used to convert radiants into degree Mathf.Rad2Deg.

When i click i just save the current desired location in targetLat and targetLon. Those two angles represents the point we’ve clicked on our sphere in local sphere-coordinates.

In the next step i smooth the “movement” from the old position to the new one. I use the “Lerp-smoothing-trick”. LerpAngle have to big advantage that it always moves the shortest distance. Angles have the little problem that -180° and 180° is the exact same angle. So if you want to move from -170 to 160 the obvious visual distance is 30° but the normal Lerp would say it’s 330.

currentLat and currentLon holds the position we want to see in front of us. Now we just need to rotate the sphere “currentLon” degrees around the y-axis and “currentLat” degrees around the x-axis. The rotation in Unity a positive angle will rotate the earth clockwise (looking from north) so the longitude is correct but a positive angle in x will rotate the earth upwards so the south gets visible but that’s the wrong way. The positive angles are on the northern hemisphere so we need to invert the angle by using -currentLat.

The order how you combine the quaternions is important, if you swap the two rotations it would rotate first around x and after that around y what would result in a wrong turned sphere.

    // perform raycast to get a point on the sphere
    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    RaycastHit hit;
    if (collider.Raycast(ray, out hit,float.PositiveInfinity))
    {
        // draw a line in the editor to visualize the hit point
        Debug.DrawRay(hit.point,hit.normal,Color.yellow);
        // convert the hit point into local coordinates
        Vector3 localPos = transform.InverseTransformPoint(hit.point);
        Vector3 longDir = localPos;
        // zero y to project the vector to the x-z-plane
        longDir.y = 0;

        //calculate the angle between our reference and the hitpoint
        float longitude = Vector3.Angle(-Vector3.forward,longDir);
        // if our point is on the western hemisphere negate the angle
        if (longDir.x<0)
            longitude = -longitude;
        // calculate the latitude in degree
        float latitude = Mathf.Asin(localPos.normalized.y)*Mathf.Rad2Deg;

        positionText.text = "longitude:"+longitude + "

\r" +
“latitude:”+latitude;

        if (Input.GetMouseButtonDown(0))
        {
            targetLat = latitude;
            targetLon = longitude;
        }
    }

    // slowly move the currentLat / Lon towards our targetLat / Lon
    currentLat = Mathf.LerpAngle(currentLat,targetLat,Time.deltaTime*5);
    currentLon = Mathf.LerpAngle(currentLon,targetLon,Time.deltaTime*5);

    // build our rotation from the two angles
    transform.localRotation =
        Quaternion.AngleAxis(-currentLat,Vector3.right)*
        Quaternion.AngleAxis(currentLon,Vector3.up);