Acquiring camera world matrix in an OrbitControls change event

While manipulating my three.js view screen, I’m hoping to get the instantaneous world matrix of the camera and do stuff with them. At this point, the stuff isn’t that important because I’m encountering a problem.

The code that does this can be found in [this branch}(GitHub - SeanCurtis-TRI/meshcat at PR_camera_callback) (and I’ll call out some particular sites).

  1. I register a callback on the “change” event of the controls.
  2. In a particular callback instance, I grab the camera.matrixWorld.elements (where camera is the camera associated with the controls.
  3. In the example html, I use that matrix to look at the distance between target and camera and the direction as well. The assumption is that, for a stable rotation, the Z-axis of the camera’s basis should point directly towards the target point.

Here’s the problem, there’s an observable deviation between how the camera is actually oriented and what the reported matrix says. Rapid mouse movement exacerbates this deviation. Fast mouse movements are more likely to guarantee that the matrix available when the event is processed is lying.

Given the stability of the view, good things are happening (the view doesn’t suffer from the “jiggling” implied by the deviation). Therefore, I’m left to assume that asking the camera for its world matrix at that point is a bad thing.

Is there some accepted alternative to querying the camera position in the change event that isn’t subject to the noise that I’m observing?

Although I am not able to run (and debug) your code, here is a Schrödinger hint (it may work, or not work – state is unknown until you try it).

Usually matrixWorld is updated at the rendering phase, thus often inside the animation loop its value is still not updated. Actually it contains the old value, from the previous loop. If you need the matrix before renderer’s render, you may try to force the matrix update with .updateMatrix, .updateMatrixWorld or .updateWorldMatrix (see the docs for their parameters, they are defined for Object3D). Most likely one of them is enough, but you can try all of them and then eliminate them one by one.

Is you camera .add() ed to the scene? scene.add(camera)

If not… it’s matrices won’t get auto-updated.

You can force/manually update the world matrix with camera.updateMatrixWorld() after you modify it…

controls.update() might also force a matrix update of the camera.

Basically in threejs whenever you see a matrix not reflecting the state properly you can object.updateMatrix() ( composes the .position, .rotation, .scale of the object into its .matrix )

and updateMatrixWorld( true/false ) which updates matrices for the parent, then updates .matrixWorld for the object, and if true is passed, does it for children as well iirc.

Actually it contains the old value, from the previous loop.

This isn’t strictly true. It frequently contains values that have never been “used”. (For a given value of “never”.) I suspect that between calling update on the matrix and manipulating the camera with the control, there’s a window where the matrix array is used as scratch space. So, the values may represent intermediate calculations.

However, your comments were all helpful and I realized I was probably doing something foolish. The “change” event on the camera control is probably less important than the rendering event. After all, if the camera moves twice between rendered frames, the pose that matters most is where the camera is when images are actually drawn. So, I simply pulled my callback out of the control event listener and put it in my rendering function. The rendering function, as you would suspect, already handles calling updateMatrixWorld() on the camera. So, at that moment, I know the camera pose is well defined and I’ll be operating on the same value as the actual rendering.

Thank you all for your feedback and help.

1 Like

One last thought. If you want to acquire the camera world matrix inside the camera controls change event, it is possible. As mentioned above, you need to call update on the controls and updateWorldMatrix on the camera. But doing so dispatches yet another change event. So, if you go this route you need to make sure that your own event listener has a guard against stack overflow as you keep instantiating another stack for the callback.

I was finally able to get a version of the callback working that would make the camera world matrix available based on the control’s change event, but it required all of the above.