WilyC
December 21, 2019, 10:49pm
1
I am trying to get the location of a point that would be on the upper right side of a sphere’s horizon from the perspective of a given camera.
For example, this code works correctly for objects roughly in the middle of the field of view:
private Vector3 GetEdgePoint(Camera camera, Vector3 possition, float diameter){
var shift = (diameter/2) / SQRT_TWO;
var edgePoint = possition + (camera.transform.up.normalized * shift) + (camera.transform.right.normalized * shift);
return edgePoint;
}
I understand why this doesn’t work for objects near the edge of the field of view - the ‘direction’ of the camera’s transform is still directly ahead so I can’t use it to determine the plane on which to shift my point.
I suspect I could solve the problem by making an empty GameObject, copying in the position and rotation of the camera, performing a LookAt to the target and then using the direction of that GameObject as the normal for my cross section plane for the sphere. The thing is, that kinda seems like a hack and I feel there must be a better way to do it. It would happen for a number of onscreen targets each frame so I’m looking to keep it pretty efficient (which I may already not be doing - I’m fully amateur at this).
Any suggestions?
Why create a GO and copy the information?
Just do the math that is like doing all that.
Quaternion.LookRotation is effetively like LookAt, you just need to calculate the direciton:
The direction would be (targetPosition - cameraPosition).
So:
var q = Quaternion.LookRotation(targetPosition - cameraPosition);
(I guess in your code cameraPosition would be possition)
As for getting the ‘up’ and ‘right’, you can see from the source code it’s really just the rotation * Vector3.up, or Vector3.right:
// The rotation as Euler angles in degrees.
public Vector3 eulerAngles { get { return rotation.eulerAngles; } set { rotation = Quaternion.Euler(value); } }
// The rotation as Euler angles in degrees relative to the parent transform's rotation.
public Vector3 localEulerAngles { get { return localRotation.eulerAngles; } set { localRotation = Quaternion.Euler(value); } }
// The red axis of the transform in world space.
public Vector3 right { get { return rotation * Vector3.right; } set { rotation = Quaternion.FromToRotation(Vector3.right, value); } }
// The green axis of the transform in world space.
public Vector3 up { get { return rotation * Vector3.up; } set { rotation = Quaternion.FromToRotation(Vector3.up, value); } }
// The blue axis of the transform in world space.
public Vector3 forward { get { return rotation * Vector3.forward; } set { rotation = Quaternion.LookRotation(value); } }
// The rotation of the transform in world space stored as a [[Quaternion]].
public extern Quaternion rotation { get; set; }
// The rotation of the transform relative to the parent transform's rotation.
public extern Quaternion localRotation { get; set; }
public Vector3 up { get { return rotation * Vector3.up; } set { rotation = Quaternion.FromToRotation(Vector3.up, value); } }
(note you also don’t need to normalize those, they’re already unit vectors)
And since we calculated the rotation that would be looking at the target position as ‘q’, our up and right would be:
var up = q * Vector3.up;
var right = q * Vector3.right;
Now you have your values for your formula.
WilyC
January 1, 2020, 5:17pm
3
lordofduct:
As for getting the ‘up’ and ‘right’, you can see from the source code it’s really just the rotation * Vector3.up, or Vector3.right:
https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Transform/ScriptBindings/Transform.bindings.cs#L51
public Vector3 up { get { return rotation * Vector3.up; } set { rotation = Quaternion.FromToRotation(Vector3.up, value); } }
(note you also don’t need to normalize those, they’re already unit vectors)
And since we calculated the rotation that would be looking at the target position as ‘q’, our up and right would be:
var up = q * Vector3.up;
var right = q * Vector3.right;
Now you have your values for your formula.
Thanks for the reply, but this doesn’t work when I switch my camera to view the objects top down. The point seems to assume it was from the perspective of a side camera even when the camera is above (my camera can observe its target from any point in a surrounding sphere).
If I implement my edge point as follows (where ‘phantomTransform’ is a from a placeholder GameObject), it all works fine - so I’d love to be able to implement the equivalent of this but using the math directly as you suggest:
private Vector3 GetEdgePointPhantom(Camera camera, Vector3 possition, float diameter){
var shift = (diameter/2) / SQRT_TWO;
phantomTransform.position = camera.transform.position;
phantomTransform.LookAt(possition, camera.transform.up);
var edgePoint = possition + (phantomTransform.up.normalized * shift) + (phantomTransform.right.normalized * shift);
return edgePoint;
}
WilyC
January 1, 2020, 5:23pm
4
OK - this seems to work. Just added the camera’s up to the LookRotation. Thanks for your help!
private Vector3 GetEdgePoint(Camera camera, Vector3 possition, float diameter){
var shift = (diameter/2) / SQRT_TWO;
var q = Quaternion.LookRotation(possition - camera.transform.position, camera.transform.up);
var up = q * Vector3.up;
var right = q * Vector3.right;
var edgePoint = possition + (up * shift) + (right * shift);
return edgePoint;
}