四元数转3×3矩阵需用列主序公式:m[9] = {1-2y²-2z², 2xy-2wz, 2xz+2wy, 2xy+2wz, 1-2x²-2z², 2yz-2wx, 2xz-2wy, 2yz+2wx, 1-2x²-2y²},GLM中quat构造须为(w,x,y,z),且应归一化以防漂移。

四元数转 3x3 矩阵的正确公式(别套错行列顺序)
直接用 quat.x、quat.y、quat.z、quat.w 套标准公式即可,但必须注意:多数图形 API(如 OpenGL、Vulkan)期望列主序(column-major)矩阵,而数学推导常默认行主序。如果你把矩阵填进 glm::mat3 或手写 float[9] 数组时顺序反了,顶点会扭曲或镜像。
标准列主序 3×3 旋转矩阵(假设单位四元数 q = (x,y,z,w))为:
float m[9] = {
1 - 2*y*y - 2*z*z, 2*x*y - 2*w*z, 2*x*z + 2*w*y,
2*x*y + 2*w*z, 1 - 2*x*x - 2*z*z, 2*y*z - 2*w*x,
2*x*z - 2*w*y, 2*y*z + 2*w*x, 1 - 2*x*x - 2*y*y
};
- 这个矩阵满足
R * v(矩阵左乘列向量),符合 OpenGL / GLSL 习惯 - 若你用的是行向量约定(如某些物理引擎),需转置——但绝大多数 C++ 图形代码不这么干
-
glm::mat3_cast(q)内部就是按此公式实现,可直接用,无需手算
用 glm::quat 转换时必须确认 w 是实部
GLM 的 glm::quat 构造顺序是 quat(w,x,y,z),不是 (x,y,z,w)。如果从外部数据(比如 Assimp、OpenXR、ROS)读入四元数,常见格式是 (x,y,z,w),直接传给 glm::quat(x,y,z,w) 会导致旋转完全错误——因为 GLM 把第一个参数当 w,结果相当于把原四元数当成了 (w,x,y,z)。
- 安全做法:
glm::quat q(input.w, input.x, input.y, input.z); - 或显式构造:
glm::quat q = glm::quat(input.w, glm::vec3(input.x, input.y, input.z)); - 验证方法:对
q调用glm::length(q),应 ≈ 1.0;再试glm::rotate(glm::mat4(1.f), 0.1f, glm::vec3(0,1,0))提取的quat,看w是否主导
应用到顶点时别漏掉归一化和坐标系一致性
旋转矩阵只处理方向,不处理平移或缩放。如果你把矩阵直接乘到顶点上却没考虑模型空间/世界空间差异,或者顶点本身没归一化(比如法线向量),结果会偏移或失真。
- 对位置顶点:
rotated_pos = mat3 * original_pos;(假设已居中) - 对法线向量:必须用相同矩阵,但**不能直接用
mat3 * normal**——如果模型矩阵含非均匀缩放,得用逆转置;但纯旋转下,mat3正交,逆等于转置,所以等价,可直接用 - 检查你的顶点数据是否 Z-up 或 Y-up:四元数通常按右手系定义(如 OpenXR),若引擎用左手系(如 DirectX 默认),需翻转一个轴(常见是 z → -z),否则旋转看起来“反向”
性能敏感场景下避免重复计算
每次顶点变换都重新调用 glm::mat3_cast() 或手算矩阵,开销不大但累积起来浪费。尤其在 CPU 蒙皮或批量更新时,应该先算一次矩阵,再批量乘顶点。
- 错误写法:
for (auto& v : vertices) v = glm::mat3_cast(q) * v; - 正确写法:
auto R = glm::mat3_cast(q); for (auto& v : vertices) v = R * v; - 若用 SIMD 或自定义向量类,确保
mat3 * vec3实现是内联且无临时对象——GLM 默认是,但某些封装层可能隐式构造glm::vec3引发额外拷贝
真正容易被忽略的是四元数是否已单位化:哪怕只差 1e-6,转出的矩阵行列式就可能偏离 1.0,长期累加导致“旋转漂移”。建议在关键路径(如动画插值后)做一次 q = glm::normalize(q)。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











