What is the difference between camera orientation and fixed frame transforms?


In our product we produce a view_matrix that represents the orientation of a drone as it takes a picture. This works fairly well, we compute the view simply like this:

 getView(): any {
    const destination = Matrix4.multiplyByPoint(this.view_matrix, Cartesian3.ZERO, new Cartesian3())
    const direction = Matrix4.multiplyByPointAsVector(this.view_matrix, Cartesian3.UNIT_Z, new Cartesian3())
    const up = Matrix4.multiplyByPointAsVector(this.view_matrix, Cartesian3.UNIT_Y, new Cartesian3())

    const view = {
      orientation: { direction, up },
    return view

However we now also have some drone positions for which we only have heading, pitch and roll, not a view_matrix. So to make things uniform I want to compute the view_matrix from the HeadingPitchRoll.

There’s some functions in Cesium.Transforms that seem to implement this, but none of them seem to produce the same results.

Below is an attempt to compute the HeadingPitchRoll from the view_matrix and you can see that the Cesium.Transforms.fixedFrameToHeadingPitchRoll function does not produce the same results.

Does anyone have an idea about what this type of view matrix is called, and maybe what set of functions do work with it? I tried a bunch of stuff, also for example extracting the Matrix3, transforming it to ENU and then using Quaternion to convert it to HeadingPitchRoll but that also failed to give the same result.

Hi there,

From your description, it looks like the “view” matrix here is either a northUpEast or eastUpNorth system, depending on what cardinal direction direction maps to.

You can use Cesium.Transforms.localFrameToFixedFrameGenerator(firstAxis, secondAxis) to generate a toFixedFrame functoin that will work for that case.

Thanks @Gabby_Getz ! I gave it a couple more tries with your suggestion. I also learned that comparing euler angles (HeadingPitchRoll) is a bad idea because there are always multiple correct solutions for any rotation so it’s not easy to see if two HeadingPitchRoll objects are identical.

I found somewhere that instead you can multiple one rotation matrix with the transpose of the other to get the difference, so that’s what I’m trying now. It seems to work when I construct two local rotation matrices and compare them this way, the result is (close to) the identity matrix.

However I’ve still not found a solution on how to use the Cesium.Transforms to construct this view matrix:

I never get similar heading pitch rolls, nor does this difference matrix ever become the identity matrix.

Oh actually I believe ENU is correct because the HeadingPitchRoll I get from it is correct, it’s just that I seem to not be able to create a similar matrix myself from a HeadingPitchRoll object.

Hi @tinco,

I think I’m still a bit confused on what your code example is trying to accomplish.

To extract heading, pitch, and roll values from your view matrix, I would:

  • convert it to fixed frame at the position in question using one of the toFixedFrame functions
  • get the heading pitch and roll with Transforms.fixedFrameToHeadingPitchRoll

Then to get that view matrix from a heading, pitch, and roll values I would do the inverse:

  • use Transforms.headingPitchRollToFixedFrame to get the fixed frame transform
  • using the inverse of one of that toFixedFrame, convert back to the view matrix

I’m trying to construct a view matrix, in such a way that I can get the direction and up vectors from it so I can pass it to Cesium.Camera.setView. The getHeading, getPitch, and getRoll and the code inside of calculateHeadingPitchRoll are all extracted from Cesium.Camera (https://github.com/CesiumGS/cesium/blob/1.109/packages/engine/Source/Scene/Camera.js#L785).

I would love to use the Cesium.Transforms module to achieve these things, but they simply don’t work the same way as Cesium.Camera does. Where Cesium.Transforms seem to follow the standard way of doing things, Cesium.Camera does everything in a weird way.

For example note in that getHeading function that it boths subtracts PI_OVER_TWO from the heading, and then subtracts the results from TWO_PI after modulating it over TWO_PI. I don’t understand why. In all other implementations I could find in other libraries of turning direction vectors into headings, it’s just the atan2 and the modulo of two pi to get a nicer number. Why isn’t the Cesium.Camera class using Quaternion to produce these values? I’m not asking because I’m critical, I’m just confused by it and desperate to figure out what’s going on.

I don’t understand why Cesium.Camera works this way, and I also don’t understand how RealityCapture and Metashape produce these view matrices that don’t work at all like the sort that are created by Cesium.Quaternion or Cesium.Transforms nor in any libraries in other programming languages I’ve tried but work work so trivially with Cesium.Camera. When you Googling this, I find only other people who are confused by the same thing.

I think the people who are good at this algebra probably have some trivial answer to this, and the people who don’t get it like me are hopelessly stuck :grimacing: