Frustum Geometry Orientation

I am trying to display frustums based on different origin and HPR. When I tried using the Frustum Geometry, its orientation doesn’t seem right. Here’s a code snippet:

const origin = Cesium.Cartesian3.fromDegrees(lon, lat, height);
const rotation = new Cesium.HeadingPitchRoll(heading, pitch, roll);
const orientation = Cesium.Quaternion.fromHeadingPitchRoll(rotation);

let frustum = new Cesium.PerspectiveFrustum();
frustum.fov = Cesium.Math.toRadians(fov);
frustum.aspectRatio = aspectWidth / aspectHeight;
frustum.near = 1.0;
frustum.far = 30.0;

const frustumGeometry = new Cesium.FrustumOutlineGeometry({

const frustumOutlineGeometryInstance = new Cesium.GeometryInstance({
  geometry: frustumGeometry,
    attributes: {
        color: Cesium.ColorGeometryInstanceAttribute.fromColor(new Cesium.Color(1.0, 1.0, 1.0, 1.0))

But when I checked using the Camera, its debug frustum is correct.

const orientation = {
  destination: origin,

Here’s a screenshot. Black one is the Frustum Geometry. Blue one is from the debug frustum planes.

Why is it different even if I am using the same HPR values? I need the Frustum to be the same orientation as the debug frustum plane.

Thank you for the help!

I also tried using Cesium.Transforms.headingPitchRollQuaternion but still getting different orientation.

let orientation = Cesium.Transforms.headingPitchRollQuaternion(origin, rotation);
1 Like

Bump! I read in another post that I need to transform relative heading-pitch-roll to absolute rotation Quaternion. Here’s my code:

let localQ = Cesium.Quaternion.fromHeadingPitchRoll(rotation);

let enu = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
let transform3 = new Cesium.Matrix3();
Cesium.Matrix4.getMatrix3(enu, transform3);
let transformQ = Cesium.Quaternion.fromRotationMatrix(transform3);

let orientation = new Cesium.Quaternion();
Cesium.Quaternion.multiply(localQ, transformQ, orientation);

But still, I am getting a different orientation compared to the camera. I need it to be the same as the camera since based on my image, that is the correct orientation.

Would really appreciate it if someone can help. I already tried 3 different solutions but still getting incorrect orientation.

@omar or anyone else. Would really appreciate any help. 1 week since my first post. Thanks

1 Like

@john1 if you haven’t already seen it, I would look at how the DebugCameraPrimitive creates the frustum geometry and make sure you’re doing the same computation:

Let me know if that works. It would be great to have a code example that makes this easier perhaps. Can you tell us about your use case for this?

1 Like

@omar I already checked that one before, but DebugCameraPrimitive is based from its directionWC, upWC and rightWC. Is there a way to get those from HPR?

Can you tell us about your use case for this?

I am trying to visualize the FOV of images taken from certain origins and orientations.

This should work
Transforms.headingPitchRollToFixedFrame(origin, heading, pitch, roll, ellipsoid, result)
“Computes a 4x4 transformation matrix from a reference frame with axes computed from the heading-pitch-roll angles centered at the provided origin to the provided ellipsoid’s fixed reference frame.”
Assumes heading pitch roll are in terms of the local ENU frame.

1 Like

@Hyper_Sonic But that one will result to a Matrix4. If you can see from my comment, I actually tried using the Cesium.Transforms.headingPitchRollQuaternion. The same as that one but will result to a Quaternion which is the one needed for the Frustum. But still, I got a different orientation.

You’ve mentioned that you needed directionWC, upWC and rightWC from HPR (presumably HPR relative to the local ENU frame.) You should be able to get those directly from the Matrix4 (which is Matrix3 rotation coupled with a Cartesian3 position.)

right = new Cesium.Cartesian3(mat[0],mat[1],mat[2]);
direction = new Cesium.Cartesian3(mat[4],mat[5],mat[6]);
up = new Cesium.Cartesian3(mat[8],mat[9],mat[10]);

Can convert rotation portion of Matrix4 to Quaternion

var mat3 = new Cesium.Matrix3();
var quat = new Cesium.Quaternion();

Just tried it out, the Mat4 it produces is in terms of Earth’s fixed frame (WC), I was thinking that it might be in terms of the local ENU frame and would have to be transformed, but it’s not so it doesn’t. Can use just the 1st 2 parameters pos and HPR, and just have it use defaults for the rest.

1 Like

I see. Sure I’ll try that one. Thanks! I’ll update once tested

@omar @Hyper_Sonic

I tried different solutions but still got different orientation from the camera frustum. Here’s a sample implementation of the different options I tried so far: Cesium Sandcastle

Blue: Camera Frustum (target orientation)
White Outline: Straight HPR
Black Outline: Using Cesium.Transforms.headingPitchRollQuaternion
Red Outline: Using Cesium.Transforms.headingPitchRollToFixedFrame (Same results with Black Outline)
Green Outline: Using same method in camera debug frustum

Still not getting the right orientation.

Just messing with it a little: with option 1 HPR for the white frustum outline appears to be in relation to the global coordinate system, not the local ENU coordinate system as the camera is. I discovered this because when putting it on the north pole then on the south pole it faces opposite directions for the same HPR setting.

OK so the white frustum is based on global HPR. Tinkering some more I’ve discovered:
heading: rotation is around global Z axis (0 heading facing? not sure due to frustum symmetry)
pitch: rotation is around global Y axis (0 pitch facing +Z)
roll: rotation is around global X axis (0 roll is facing +Z)

HPR (0,0,0) in local ENU means facing toward +Y with right facing +X. If this were true with global HPR, that would mean that the model would face east at lon/lat (0,0) which is where global +X intersects the globe, and it’s right would face away from the globe. Perhaps I mistakenly assumed that the model’s forward vector extents from the near to far plane.

I haven’t examined the other frustum options yet. One possible solution might be global HPR<->local ENU HPR conversion.

related links

1 Like

Observations of the other 3 options

headings would match (so they shared the same z axis)
but pitch axis differed (pitch axis twisted 90deg around z)
but roll axis differed (roll axis twisted 90deg around z)

heading appeared to have rolled
pitch setting had no affect
roll appeared to have simultaneously pitched and yawed

In option4 I noticed that you just wanted the rotation matrix, and not the 3 vectors it is composed of. In that case you can simply use

EDIT: tinkering a bit with option 4
Works great if you only change heading (matrices match, the model doesn’t because forward on the model is defined differently)

      function() {
      }, 5000);

Still not sure why it goes haywire on pitch/roll.

Tinkering further I’ve discovered that Transforms.headingPitchRollToFixedFrame is bugged. Using my own function it works fine. I tried to alter the modelMatrix of Cesium.GeometryInstance, but it’s not quite working right.

//Cesium.GeometryInstance attribute
modelMatrix :[1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1], //identity no problem
modelMatrix :[1,0,0,0, 0,0,-1,0, 0,1,0,0, 0,0,0,1], //twist 90deg around x no problem
//most anything else: problem
modelMatrix :[0,-1,0,0, 1,0,0,0, 0,0,1,0, 0,0,0,1], //twist 90deg around z WON'T SHOW
modelMatrix :[0,0,-1,0, 0,1,0,0, 1,0,0,0, 0,0,0,1], //twist 90deg around y WON'T SHOW

I think this is where it works on this attribute
line 853 if =identity it returns, otherwise it executes the rest of the function. It does a bunch of stuff then sets instance.modelMatrix to identity anyways, I suppose because its a one time thing.

1 Like

@Hyper_Sonic So among the options, we can say option #1 with the conversion of global HPR to local ENU HPR is the closest solution?

Performing more tests on global HPR:
| = parallel to

000,000,000 long side | x axis, short side | y axis, face +z
090,000,000 long side | y axis, short size | x axis, face +z //rot around z

000,000,000 long side | x axis, short side | y axis, face +z
000,090,000 long side | z axis, short side | y axis, face -x //rot around y

//pitch after yaw
090,000,000 long side | y axis, short side | x axis, face +z
090,090,000 long side | z axis, short side | x axis, face +y //rot around x

000,000,000 long side | x axis, short side | y axis, face +z
000,000,090 long side | x axis, short side | z axis, face -y //rot around x

//roll after yaw
090,000,000 long side | y axis, short side | x axis, face +z
090,000,090 long side | y axis, short side | z axis, face -x //rot around y


-yaw around z, pitch relative to xy plane (where z is normal to), roll around longitudinal
-face is not model_forward, model_forward is one of the short sides
-model_forward should be re-defined in the geometry to match face

So just like in Option#4, model_forward of the geometry should be altered, but as mentioned in my previous post, it doesn’t seem to work properly, I could only twist around the model’s x axis, but what’s needed is twisting around either the model’s y or z axis.

Alternatively you could alter how the frustum outline is created
However even if you do manage to do this, you’d have to always modify whenever a new Cesium version came out, so not a very good solution. Or create your own frustum outline function, not an easy task.

EDIT: Well I tried, but couldn’t control the frustum model via modelMatrix like I coulcd ellipsoids
How to draw an upright circle

Why I’m have trouble getting the orientation working properly
Frustum geometry around Earth center, not Model center

EDIT: Wow alot of rigmarole just to end up with such a simple fix! What the issue was is this:
‘new Cesium.FrustumOutlineGeometry’ was given a huge origin, causing the ‘mesh’ to be far from it’s own origin. So in fact you can re-orient it, however rotating the mesh that is so far from its own origin is going to move the mesh really far away, and I didn’t see where it went until I made the outline huge. Why did it not move far when rotating around x? Because at 0lon,0lat x still goes through the mesh.

So the fix is just make origin fed to ‘new Cesium.FrustumOutlineGeometry’ (0,0,0). In ‘new Cesium.GeometryInstance’ just use identity matrix (default) for modelMatrix. Then you can control orientation and position via the primitive’s modelMatrix.

Later on I’ll post a full solution based on this discovery. EDIT: I put solution in another thread.

1 Like

@Hyper_Sonic I tried your solution and it worked perfectly! Thank you very much! I am trying really hard these past days but can’t really understand. Your explanation makes so much sense now.

I think the issues/bug mentioned should be submitted. Again, Thank you!

Good to hear that it’s working perfectly now! I see that you already found the other thread, I suppose I should post a link for anyone else whom might come across this
A way to orient and place Frustum outline

Ya I was confused for awhile as well. I wrote in the other thread about making the geometry creation as generic as possible, then place & orient using the primitive. I suspect Cesium.Transforms.headingPitchRollToFixedFrame isn’t working properly, but I want to first make sure I’m using it correctly before I submit an issue.