- Home /
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: http://answers.unity3d.com/questions/21921/rotate-point-a-on-sphere-to-point-b-on-sphere.html but that gives me incorrect results :/
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 :/
Answer by Bunny83 · Jul 12, 2011 at 03:41 PM
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 ;) here
In this example i nested the actual mesh to correct the rotation (so that the visual 0,0 is at 0,0 ;) )
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 + "\n\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);
This example looks really nice-exactly such effect I'd like to archieve.
But please be more exact-what is localPos?And what shoud be done futher with that calculated latitude?
Well according to your question it's the point your talking about. It's in the local space of the sphere. The point you clicked. The question was about getting the two angles but if you want to see the whole project i also put a link to the source package below the unityplayer on the example-webpage.
Thanks Bunny.$$anonymous$$y spine fuckin' pains me,so I'm not as bright as usual :/
Well,the solution does not work :O.I suspect beacuse of that assu$$anonymous$$g.
Damn,using the source as it is does not work in $$anonymous$$e project,and I don't understand the algorithm :/
Please explain it step by step Bunny