How to calculate the viewing angle of an object in 3D

1.9k views Asked by At

As shown below, assume there is a virtual camera pointing at an object and that both are defined by an orientation and position with respect to a global coordinate system. In the figures, black denotes the GCS and blue denotes a LCS.

Problem

Some objects, such as screens or LEDs in this case, have a viewing angle. They can only be seen, for example, if the angle to the observer is within ±70°

To solve this, so far I have taken the steps demonstrated by the following pseudo code. Where applicable, we are working in a right-handed coordinate system, using extrinsic rotations applied in the order ZYX.

LEDToWorldTransform = LED.Transform;
CameraToWorldTransform = Camera.Transform;
WorldToCameraTransform = CameraToWorldTransform.Inverse;
CameraToLEDTransform = WorldToCameraTransform * LEDToWorldTransform;

As I understand, CameraToLEDTransform should now contain the translation and rotation of the LED with respect to the camera coordinate system. For visualization purposes:

Partial Solution

Now this is where I get stuck, looking at the euler components of CameraToLEDTransform I think we can conclude that the Z component of the rotation with respect to the camera's coordinate system does not affect the viewing of the LED. I tried the following which I'm not sure is correct. Note, in the third step I'm trying to flip the direction of one of the Z axes such that they point in the same direction

R_Z, R_Y, R_X = CameraToLEDTransform.Rotation.Eulers
CameraToLEDTransform.Rotation.Eulers = 0, R_Y, R_X
CameraToLEDTransform.Rotation *= Rotation.AngleAxis[180deg,X]
Angle, Axis = CameraToLEDTransform.Rotation.AngleAxis

This gives an answer which sort of looks correct, however I suspect that something maybe wrong as following my logic I should also be able to replace the third step with:

CameraToLEDTransform.Rotation *= Rotation.AngleAxis[180deg,Y]

However, this is not the case and I get a different (and certainly wrong) answer. This approach may be completely wrong, and I certainly do not understand why rotating 180 around X differs from rotating 180 around Y in this situation. Perhaps an alternative approach would be to use the Dot product to calculate the angle, however I think this only works between two vectors not two rotations. It seems that projecting two new vectors based on the orientation of the two objects would be a very around about approach...

1

There are 1 answers

1
EvilTak On BEST ANSWER

Using the dot product is not a round about approach, it is the correct approach. You'll have to use the dot product between the view direction vector of the camera and the normal (i.e. vector from the observer to the LED/object in question) to get the cosine of the required angle. If the object is a surface like a screen, you may want to use the normal of the surface instead.

// normalized returns a unit vector
Vector3 normal = (led.position - observer.position).Normalized(); 
Vector3 lookDirection = observer.lookDirection; // Should be a unit vector

float cosAngle = Vector3.Dot(normal, lookDirection);

While checking if the angle falls within the required range, we can use the following property of the cos function to avoid an acos call:

If -angle <= theta <= angle, cos(theta) >= cos(angle). (Try it out!)

Your check would be as follows:

// You can constantize or precompute this
float cosViewingAngle = cos(deg2rad(70));
bool inViewingAngle = cosAngle >= cosViewingAngle;