Skip to content

Using Expressions to drive camera angle

In this example, we will build a system which drives the rotation of a camera to always point at a specific screen.

The goal is to ensure that a camera will always point at a given screen, no matter where either one moves along the floor. The example doesn’t attempt to compensate for changes in height, only position changes on the X and Z axis within the stage.

camera tracking demo

How the example works

The idea is to build a right-angled triangle laying on the floor between the camera and the target screen. Using trigonometry, we are able to calculate the angle from the camera to the screen.

The basic functionality of this example is provided by the function atan2. We use it to calculate the opposite angle of the triangle we construct from the relative positions of the camera and target screen.

overhead view

Determining the angle

In order to construct the triangle above, we must build the two straight sides, one for the X axis (side to side) and one for the Z axis (forward and back.) We will do this by first creating an ExpressionVariables device to hold our variables, and define each side individually.

Constructing the triangle sides

The two expressions will be named cam_screen_offset_x and cam_screen_offset_z. These will be function variables, that is they are small expressions which can be used later in other expressions to aid readability, validate the results are as expected and show the thought process which built the system.

Let’s look in detail at how to calculate cam_screen_offset_x

ledscreen:screen.offset.x - camera:cam_1.offset.x

This is the difference between the camera’s position on the X axis compared to the screen’s X position. This creates the opposite side of the triangle above.

We can repeat this for the Z axis by simply replacing the .x with .z for both the screen and camera. This creates the adjacent side of the triangle.

Calculating the angle

Once the sides of the triangle are constructed, we are able to use trigonometry to build the desired angle. We will again use an expression variable to calculate this and give it a name we can use elsewhere, in this example, we will use cam_screen_y_angle.

This expression needs to convert from the side lengths to the desired angle. This is accomplished by using the atan2 function, which takes 2 arguments, x and y. It computes the inverse of the tangent which would be created by x / y, however it is also aware of the sign of these values and can compensate for that, giving accurate values for any inputs.

Since we know that the tangent of the angle is equal to opposite / adjacent, we can use the cam_screen_offset_x() as the opposite, and cam_screen_offset_z() as the adjacent. Therefore:

atan2(cam_screen_offset_x(), cam_screen_offset_z()) will return the opposite angle, in radians. Since we want to convert to degrees we can wrap that further in a call to degrees, like so:

degrees(atan2(cam_screen_offset_x(), cam_screen_offset_z()))

The expressions in place

Once the above expressions are determined, the expressions variable device will look something like the following:

expressions written

Applying the angle

Once the angle has been determined, the expression cam_screen_y_angle() would evaluate to the angle the camera’s Y rotation field would need to be set to in order to point at the target screen.

We can accomplish this by creating a position receiver device, and an axis expression to control the camera’s Y rotation.

expressions written

Further work

It can be possible to extend systems like this, for example the target of the camera can be switched by replacing the ledscreen:screen with yet another function variable which returns the target screen. This itself could be driven by an External protocol and an if function to select which screen is returned.

Alternatively, the system could be duplicated to allow for targeting upon changing the relative height of the camera & screen.