You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何基于指定原点实现平移、旋转、缩放变换?(GLM框架)

嘿,我太懂你这种绕自定义原点做变换卡壳的感觉了——GLM的矩阵顺序确实容易绕晕,尤其是列主序的乘法逻辑,稍不注意就翻车。先给你把核心逻辑理清楚,再给你对应代码示例~

核心:自定义原点变换的正确顺序

要基于指定原点(比如精灵的锚点)完成缩放、旋转,再平移到目标位置,变换顺序绝对不能乱。因为GLM里的矩阵是列主序,变换函数(translate/scale/rotate)是把新变换左乘到当前矩阵上,所以代码里的执行顺序就是变换实际生效的顺序:

  1. 先把物体平移,让你的自定义原点和世界坐标系原点重合(用原点坐标的负值做平移)
  2. 应用缩放变换(此时缩放是围绕自定义原点进行的)
  3. 应用旋转变换(同理,旋转中心就是你的自定义原点)
  4. 把物体平移回自定义原点的原始位置
  5. 最后平移到物体的目标世界位置
GLM代码实现(适配你的精灵场景)

假设你已经有精灵的原点、尺寸、缩放系数、旋转角度和目标位置,代码应该是这样的:

// 先定义好所需变量(根据你的场景调整)
glm::vec2 sprite_origin = sprite->origin;
glm::vec3 scale_factor = glm::vec3(scale[0], scale[1], 1.0f); // 2D场景z轴保持1
float rotation_rad = glm::radians(rotation_deg); // 记得把角度转成弧度!
glm::vec3 target_pos = glm::vec3(sprite->position.x, sprite->position.y, 0.0f);

// 初始化模型矩阵为单位矩阵
glm::mat4 model = glm::mat4(1.0f);

// 步骤1:将锚点移到世界原点
model = glm::translate(model, glm::vec3(-sprite_origin.x, -sprite_origin.y, 0.0f));

// 步骤2:基于锚点缩放
model = glm::scale(model, scale_factor);

// 步骤3:基于锚点旋转(2D场景绕z轴旋转)
model = glm::rotate(model, rotation_rad, glm::vec3(0.0f, 0.0f, 1.0f));

// 步骤4:移回锚点原始位置,再放到目标位置
model = glm::translate(model, glm::vec3(sprite_origin.x, sprite_origin.y, 0.0f));
model = glm::translate(model, target_pos);
你之前代码可能踩的坑
  1. 变换顺序搞反:如果先做缩放/旋转再平移到原点,那变换是围绕世界原点进行的,完全不是你想要的自定义原点效果。
  2. 错误归一化原点坐标:你代码里的rx = sprite->origin.x / width这种归一化值,只在处理UV纹理坐标时有用,做空间变换时直接用原始的像素/世界坐标就行,不需要归一化。
  3. 忘记转弧度:GLM的rotate函数要求传入弧度值,如果直接传角度会导致旋转幅度异常(比如90度会变成90弧度,转了十几圈)。
快速线性代数小科普(帮你记牢逻辑)

缩放和旋转都是基于原点的线性变换,如果你的物体锚点不在原点,直接做变换会让物体绕世界原点转/缩放。所以必须先把锚点“临时挪到”世界原点,做完变换再挪回去,这样所有变换都是围绕你的自定义原点生效的。

内容的提问来源于stack exchange,提问作者Ruks

火山引擎 最新活动