1 glm::mat4矩阵插值

在glm中我们可以使用glm::slerp方法对四元数glm::quat进行插值,比如

glm::quat result_quat = glm::slerp(start_quat, end_quat, t);

其中start_quat为源四元数,end_quat为目标四元数,t为插值系数。

但是glm::mat4是一个变换矩阵,那么既包括旋转,同时也包含位移和缩放信息,在glm中目前没有提供一个现成的函数进行glm::mat4的插值。

我封装了两个函数,在插值过的过程中只对旋转和位移进行插值。

方法一:

static glm::mat4 slerp_matrix2(const glm::mat4& start, const glm::mat4& end, float t) {
    // 方法2 
    glm::quat start_quat = glm::quat_cast(start);
    glm::quat end_quat = glm::quat_cast(end);
    glm::quat result_quat = glm::slerp(start_quat, end_quat, t);
    glm::mat4 result_mat = glm::mat4_cast(result_quat);
    result_mat[3] = start[3] * (1 - t) + end[3] * t;

    return result_mat;
}

方法二:

static glm::mat4 slerp_matrix1(const glm::mat4& start, const glm::mat4& end, float t) {
    // 方法1
    // 提取起始矩阵的旋转部分
    glm::mat3 start_matrix(
        start[0][0], start[0][1], start[0][2],
        start[1][0], start[1][1], start[1][2],
        start[2][0], start[2][1], start[2][2]
    );
    glm::quat start_quat = glm::quat_cast(start_matrix);

    // 提取结束矩阵的旋转部分
    glm::mat3 end_matrix(
        end[0][0], end[0][1], end[0][2],
        end[1][0], end[1][1], end[1][2],
        end[2][0], end[2][1], end[2][2]
    );
    glm::quat end_quat = glm::quat_cast(end_matrix);

    // 插值四元数
    glm::quat result_quat = glm::slerp(start_quat, end_quat, t);

    // 将插值后的四元数转换回旋转矩阵
    glm::mat3 result_rotation = glm::mat3_cast(result_quat);

    // 构造最终的插值矩阵
    glm::mat4 result_mat = glm::mat4(1.0f);
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 3; ++j) {
            result_mat[i][j] = result_rotation[i][j];
        }
    }

    // 应用平移插值
    glm::vec3 start_translation = glm::vec3(start[3]);
    glm::vec3 end_translation = glm::vec3(end[3]);
    glm::vec3 result_translation = glm::mix(start_translation, end_translation, t);
    result_mat[3] = glm::vec4(result_translation, 1.0f);

    return result_mat;
}

上述两个方法的实现思路差不多,旋转部分插值使用四元数,位移部分插值按插值系数进行插值就行,然后再将旋转部分和位移部分进行整合,形成最后的变换矩阵。

上述例子的测试代码如下:

#include <iostream>

#include "glm/glm.hpp"
#include "glm/gtc/type_ptr.hpp"
#include "glm/gtc/matrix_transform.hpp"

static glm::mat4 slerp_matrix1(const glm::mat4& start, const glm::mat4& end, float t) {
    // 方法1
    // 提取起始矩阵的旋转部分
    glm::mat3 start_matrix(
        start[0][0], start[0][1], start[0][2],
        start[1][0], start[1][1], start[1][2],
        start[2][0], start[2][1], start[2][2]
    );
    glm::quat start_quat = glm::quat_cast(start_matrix);

    // 提取结束矩阵的旋转部分
    glm::mat3 end_matrix(
        end[0][0], end[0][1], end[0][2],
        end[1][0], end[1][1], end[1][2],
        end[2][0], end[2][1], end[2][2]
    );
    glm::quat end_quat = glm::quat_cast(end_matrix);

    // 插值四元数
    glm::quat result_quat = glm::slerp(start_quat, end_quat, t);

    // 将插值后的四元数转换回旋转矩阵
    glm::mat3 result_rotation = glm::mat3_cast(result_quat);

    // 构造最终的插值矩阵
    glm::mat4 result_mat = glm::mat4(1.0f);
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 3; ++j) {
            result_mat[i][j] = result_rotation[i][j];
        }
    }

    // 应用平移插值
    glm::vec3 start_translation = glm::vec3(start[3]);
    glm::vec3 end_translation = glm::vec3(end[3]);
    glm::vec3 result_translation = glm::mix(start_translation, end_translation, t);
    result_mat[3] = glm::vec4(result_translation, 1.0f);

    return result_mat;
}

static glm::mat4 slerp_matrix2(const glm::mat4& start, const glm::mat4& end, float t) {
    // 方法2 
    glm::quat start_quat = glm::quat_cast(start);
    glm::quat end_quat = glm::quat_cast(end);
    glm::quat result_quat = glm::slerp(start_quat, end_quat, t);
    glm::mat4 result_mat = glm::mat4_cast(result_quat);
    result_mat[3] = start[3] * (1 - t) + end[3] * t;

    return result_mat;
}

int main() {
    glm::mat4 start_matrix(1.0);
    glm::mat4 end_matrix = glm::mat4(
        0.0, -1.0, 0.0, 0.0,
        1.0, 0.0, 0.0, 0.0,
        0.0, 0.0, 1.0, 0.0,
        5.0, 6.0, 7.0, 1.0);

    glm::mat4 interpolation_matrix_1 = slerp_matrix1(start_matrix, end_matrix, 0.7);
    glm::mat4 interpolation_matrix_2 = slerp_matrix2(start_matrix, end_matrix, 0.7);

    bool compare = (interpolation_matrix_1 == interpolation_matrix_2);

    return 0;
}

在示例代码中,使用方法一和方法二对同一个输入矩阵按相同系数进行插值,然后比较两个方法的插值结果是否一致。

参考链接