Stable normals for a path

So I’ve been playing with camera paths and I took @carolhmj 's lovely camera track demo (thanks Carol!) and converted it to use Catmull-Rom splines in an attempt to make it smoother: https://playground.babylonjs.com/#SGVUBC#32. (BTW, I can sent a PR to add this PG to the docs along the original one, useful?).

New problem though, the camera tends to rotate as normals are computed along the way. I was wondering if there’s a stable way to compute normals so they “point up”, emphasis on stable.

My first try was to project the binormal into the XZ plane, and recompute the normal with a cross product, making sure that the normal always point “up” and never down (and fixing the binormal sign too). Code speaks more than words here:

for (let p = 0; p < curvePath.length; p++) {
      // project into XZ
      binormals[p].y = 0;
      binormals[p].normalize();
      // calculate normal
      BABYLON.Vector3.CrossToRef(tangents[p], binormals[p], normals[p]);
      // always up
      normals[p].y = Math.abs(normals[p].y);
      // now fix the binormal
      BABYLON.Vector3.CrossToRef(normals[p], tangents[p], binormals[p]);
}

The problem is that when the path goes vertical or close to it, the projection doesn’t make sense anymore and wild rotations come into play. Since we have a path I guess what I want is “the normal should rotate around the path as little as possible”, which I couldn’t translate into a decent algorithm. This is pretty much a gimbal lock, but I couldn’t find anything about handling it stably along paths. I’m sure this was solved 30 years, though. I wish Knuth had a volume on 3D algorithms.

If I understand the path generation algorithm from Path3D properly, it uses the binormal of the previous point as a guess, using it to compute the normal and then recomputing the actual binormal for the current point. I don’t know if other people have a reason to expect something from the normals since there’s no preferred direction (actually, I think you could reasonably define the normal as a direction orthogonal to the path pointing to the instantaneous center of rotation at that point in the path, but that won’t help my case). On the other hand, since you can pick the first normal, it might make sense to keep other normals as close to the first one as possible. That’s what I expected it to do.

So, has anybody ran into this issue before or knows a decent stable algorithm? I can send a PR with the results of this thread to Path3D if it’s deemed useful.

1 Like

An alternative to set normals and binormals.

Example of camera following a track path.

1 Like

@carolhmj worked a bit on smoothing and I wonder if this might be relevant here :slight_smile:

Hey there! Sorry I took so long to answer this one, got sidetracked with other stuff :sweat_smile:

I used the same approach that is usually done when building a camera’s lookAt’s matrix. For that, you set a “preferred” normal direction (in this case, (0, 1, 0) ), then you calculate the binormal from Cross(tangent, (0, 1, 0)), and calculate the final normal as Cross(new binormal, tangent). After that, we get normals that point in the general (0, 1, 0) direction. The camera animation itself was looking a bit jerky after that, but that was solved by interpolating Euler Angles instead of Quaternions (since the quaternion interpolation algorithm on the animation isn’t truly a SLERP).

Here is the finished playground: Camera explore catmull | Babylon.js Playground (babylonjs.com)

1 Like