自制3D引擎相机旋转异常问题:鼠标操作导致物体绕中心旋转而非相机视角转动
嘿,我懂你现在卡在哪了——从零搭3D引擎时,相机控制的bug简直是家常便饭!咱们一步步拆解问题,把它彻底修好。
核心问题分析与修复
1. 矩阵乘法顺序完全搞反了
你在ApplyView里计算顶点变换的代码,把矩阵的行和列用反了。当前的逻辑是拿顶点分量乘以矩阵的列,但正确的做法应该是乘以矩阵的行。这直接导致变换方向颠倒,看起来像是场景绕中心旋转,而非相机自身转向。
2. 齐次坐标W分量设为0,相机移动完全失效
你把pos数组的第四个元素设为0,但齐次坐标里顶点的W分量必须是1。W=0会直接忽略视图矩阵里的平移部分(也就是相机位置的偏移),所以不管你怎么移动相机,渲染结果都不会有变化。
3. 鼠标角度控制的细节缺失
当前的鼠标偏移转角度的逻辑还有两个小问题:
- 没有灵敏度控制:
x_diff和y_diff是0到1的范围,直接累加的话角度变化会慢到离谱(毕竟cos/sin用的是弧度)。 - 没有俯仰角限制:如果
pitch超过±π/2(90度),相机就会翻转,出现完全错乱的视角。
修正后的代码
先修复鼠标角度控制逻辑
// 定义灵敏度和角度限制(可根据自己的屏幕调整) const float MOUSE_SENSITIVITY = 0.0015f; const float PITCH_LIMIT = 1.5708f; // 也就是π/2,防止相机翻转 float x_diff = (WIDTH / 2) - mousePos.x; float y_diff = (HEIGHT / 2) - mousePos.y; // 转换成合理的弧度增量,加上灵敏度 yaw += x_diff * MOUSE_SENSITIVITY; pitch += y_diff * MOUSE_SENSITIVITY; // 限制俯仰角范围,避免翻转 pitch = std::clamp(pitch, -PITCH_LIMIT, PITCH_LIMIT);
再修正ApplyView函数
Vector3 ApplyView(Vector3 vertex, Vector3 cameraPosition, float yaw, float pitch) { float cosPitch = cos(pitch); float sinPitch = sin(pitch); float cosYaw = cos(yaw); float sinYaw = sin(yaw); // 相机的三个轴(右手坐标系,z轴代表前向) Vector3 xaxis(cosYaw, 0, -sinYaw); Vector3 yaxis(sinYaw * sinPitch, cosPitch, cosYaw * sinPitch); Vector3 zaxis(sinYaw * cosPitch, -sinPitch, cosPitch * cosYaw); // 构造视图矩阵:行对应相机轴,第四行是平移分量 float lookAt[4][4] = { { xaxis.x, xaxis.y, xaxis.z, 0 }, // 第一行:相机右轴 { yaxis.x, yaxis.y, yaxis.z, 0 }, // 第二行:相机上轴 { zaxis.x, zaxis.y, zaxis.z, 0 }, // 第三行:相机前轴 { -DotProduct(xaxis, cameraPosition), -DotProduct(yaxis, cameraPosition), -DotProduct(zaxis, cameraPosition), 1 } // 第四行:相机位置的负平移 }; // 正确的矩阵乘法:矩阵行 × 顶点分量,W分量设为1确保平移生效 float pos[4] = { vertex.x * lookAt[0][0] + vertex.y * lookAt[0][1] + vertex.z * lookAt[0][2] + 1 * lookAt[0][3], vertex.x * lookAt[1][0] + vertex.y * lookAt[1][1] + vertex.z * lookAt[1][2] + 1 * lookAt[1][3], vertex.x * lookAt[2][0] + vertex.y * lookAt[2][1] + vertex.z * lookAt[2][2] + 1 * lookAt[2][3], 1 // 保持W分量为1,后续透视投影会用到 }; return Vector3(pos[0], pos[1], pos[2]); }
额外补充:相机移动的实现
如果要加WASD移动,需要沿着相机自身的轴移动,你可以把相机轴的计算单独抽出来复用:
// 假设移动速度为speed if (W_KEY_DOWN) { cameraPosition += zaxis * speed; // 向前移动(沿相机前轴) } if (S_KEY_DOWN) { cameraPosition -= zaxis * speed; // 向后移动 } if (A_KEY_DOWN) { cameraPosition -= xaxis * speed; // 向左移动(沿相机右轴的反方向) } if (D_KEY_DOWN) { cameraPosition += xaxis * speed; // 向右移动 }
内容的提问来源于stack exchange,提问作者Noah Burchell
相关产品推荐
相关产品推荐

