3D场景围绕选中对象旋转:调整相机矩阵的实现问询
解决方案
要通过调整相机矩阵替代逐个物体的旋转计算,核心思路是让相机绕选中对象的位置做反向旋转,这样视觉上会呈现出所有物体(包括选中对象)绕选中点旋转的效果,完全匹配你需要的Revit式交互,且无需修改任何物体的世界矩阵。
核心原理
原来的实现中,每个物体的世界矩阵被更新为 R * world_from_node_before(R 是绕选中点的旋转矩阵),最终投影坐标为 proj * view * R * world_from_node_before * local_pos。
如果我们将相机的view矩阵更新为 view_new = view_old * R,那么新的投影坐标变为 proj * view_new * world_from_node_before * local_pos = proj * view_old * R * world_from_node_before * local_pos,和原来的计算结果完全一致——这意味着视觉效果完全相同,但无需遍历所有物体修改世界矩阵,仅需更新相机矩阵即可。
同时,选中对象的屏幕位置会保持不变:因为 R 是绕选中对象的位置 P 旋转,所以 R * P = P,新的view矩阵作用于 P 时,view_new * P = view_old * R * P = view_old * P,即选中对象的位置在相机空间中没有变化,投影到屏幕上的位置自然保持不变。
实现伪代码
// 1. 获取关键参数 glm::vec3 selected_local_axis = glm::vec3{0.0f, 1.0f, 0.0f}; // 选中对象的局部旋转轴(比如自身Y轴) float angle_delta = ...; // 鼠标拖动产生的角度增量(而非累计角度) glm::mat4 selected_world_matrix = ...; // 选中对象的当前世界矩阵 glm::vec3 rotation_point = glm::vec3(selected_world_matrix[3]); // 选中对象的世界空间位置 // 2. 将局部旋转轴转换为世界空间轴(如果需要绕对象自身轴旋转) glm::vec3 world_axis = glm::normalize(selected_world_matrix * glm::vec4(selected_local_axis, 0.0f)); // 3. 计算绕选中点的旋转矩阵R glm::quat rotation = glm::angleAxis(angle_delta, world_axis); glm::mat4 translate = glm::translate(glm::mat4{1.0f}, -rotation_point); glm::mat4 untranslate = glm::translate(glm::mat4{1.0f}, rotation_point); glm::mat4 R = untranslate * glm::mat4_cast(rotation) * translate; // 4. 仅更新相机的view矩阵 view_matrix = view_matrix * R;
注意事项
- 旋转轴的选择:如果需要绕世界空间轴旋转(比如全局Y轴),直接使用世界轴即可,无需转换局部轴。
- 增量式更新:必须使用鼠标拖动的角度增量计算旋转矩阵,避免使用累计角度,防止出现跳变或累积误差。
- 矩阵顺序:GLM采用列主序矩阵,
view_matrix * R的顺序是正确的——表示先对场景应用R旋转,再应用原相机的view变换。
内容的提问来源于stack exchange,提问作者Palash
相关产品推荐
相关产品推荐

