bitshifter / glam-rs

A simple and fast linear algebra library for games and graphics
Apache License 2.0
1.45k stars 144 forks source link

`slerp` for vectors (feature request) #377

Open kovaxis opened 1 year ago

kovaxis commented 1 year ago

I'm in a situation where I would like to slerp two Vec3s, something like the following pseudocode:

fn slerp(from: Vec3, to: Vec3, alpha: f32):
    angle = angle_between(from, to) * alpha
    return from.rotate_by(amount: angle, plane: plane_between(from, to))

I think this can be implemented in terms of quaternions, but it is not directly obvious to me and I'm not sure if it is the most performant way either.

Does this fit in the scope of the library?

bitshifter commented 1 year ago

You can create a Quat rotation between two vectors with https://docs.rs/glam/latest/glam/f32/struct.Quat.html#method.from_rotation_arc.

For slerp you need a start and end orientation to slerp between though, you can probably get that via:

let q0 = Quat::from_rotation_arc(FORWARD_DIR, from);
let q1 = Quat::from_rotation_arc(FORWARD_DIR, to);
let qa = q0.slerp(q1, alpha);

Where FORWARD_DIR is the bind pose of your object (i.e. what direction it faces with no rotation applied).

Whether that achieves what you want I am not sure. I would need to see some wider code context of how you are using this to determine if it makes sense to add a helper.

The Bevy 2D rotation example might help, it's in 2D but they're using quaternions for all rotations so the same thing should work in 3D - https://github.com/bevyengine/bevy/blob/main/examples/2d/rotation.rs.

kovaxis commented 1 year ago

I was iterating on a dual contouring implementation, and I was interpolating normals between two points, so it really was just taking two unit Vec3 and interpolating them across the unit sphere. I was doing a simple hack of lerp-ing them and then re-normalizing the result (nlerp), but I was getting odd results, so I wanted to quickly experiment by interpolating the normals "correctly".

I could kind of guess how to implement this in terms of quaternions, although it wasn't an instantly obvious solution as I'm not really familiar with quaternions. I did something along these lines:

Quat::IDENTITY.slerp(Quat::from_rotation_vec(from, to), alpha).mul_vec3(from)

I don't have the exact code I used because it turned out that normal interpolation was not the cause of my issues, and I quickly scrapped the code. I just thought that a proper Vec3 slerp would be a nice quality-of-life addition to this library and it would have saved me some time. Also, at least according to this stackoverflow answer, a proper Vec3::slerp function can be more efficient than a quaternion-based implementation.

Of course, it's up to you whether it's worth it to include this functionality. As a precedent, Unity provides a Vec3.slerp function.