TWGL WebGL2相机多轴组合旋转效果不符合预期
问题根因
你遇到的多轴旋转异常是三个逻辑错误叠加导致的,和twgl库本身无关:
- 旋转轴选择错误:当前代码调用的
m4.rotateX/rotateY/rotateZ都是绕世界坐标系的固定轴旋转,不是绕相机自身的局部轴。多轴旋转叠加时,前一次旋转会改变后续旋转轴的朝向,自然会出现类似陀螺仪的翻滚效果。 - 相机矩阵构建顺序错误:你当前是先平移再旋转,不符合FPS类相机的变换逻辑。正确逻辑是先应用旋转确定朝向,再应用平移确定位置,且平移量需要取反适配视图矩阵的坐标空间变换规则。
- 欧拉角万向锁隐患:固定顺序的三轴欧拉角旋转本身就存在万向锁问题,普通FPS相机不需要绕Z轴的滚转,去掉Z轴旋转就能规避绝大多数日常使用场景下的万向锁问题。
另外你的代码还存在几个不影响旋转但会导致渲染异常的问题:
- 透视投影近裁剪面设为
0.0是非法值,会导致深度测试失效 - 同一个canvas不能同时获取webgl2和2d上下文,2d UI需要单独用叠加的canvas或者DOM实现
- render循环里每帧新建Block对象但不调用渲染方法、不缓存对象,会造成内存泄漏且画面无内容
修正实现
相机矩阵修正
替换原有Camera类的setPosRot方法,每帧从单位矩阵重新构建相机矩阵,避免误差累积,旋转顺序调整为「先绕世界Y轴做左右偏航、再绕相机局部X轴做上下俯仰」,去掉不必要的Z轴旋转:
class Camera { static setPosRot(x, y, z, pitch, yaw, camera) { // pitch: 俯仰角(抬头/低头,绕X轴),yaw: 偏航角(左右转头,绕Y轴) m4.identity(camera); // 先应用旋转 m4.rotateY(camera, (yaw * Math.PI) / 180, camera); m4.rotateX(camera, (pitch * Math.PI) / 180, camera); // 再应用平移,位置取反适配视图矩阵变换 m4.translate(camera, [-x, -y, -z], camera); return camera; } }
调用时把原有cx - 90的偏移直接整合到初始pitch值中,初始pitch设为0即可让相机默认看向正前方,不需要每次计算时做角度偏移。
键鼠控制参考
给俯仰角加角度限制,避免视角转到正上/正下时出现翻转:
// 初始化时绑定鼠标事件 let isMouseDown = false; let lastMouseX = 0, lastMouseY = 0; Render.canvas.addEventListener('mousedown', e => { isMouseDown = true; lastMouseX = e.clientX; lastMouseY = e.clientY; }); window.addEventListener('mouseup', () => isMouseDown = false); window.addEventListener('mousemove', e => { if (!isMouseDown) return; const deltaX = e.clientX - lastMouseX; const deltaY = e.clientY - lastMouseY; lastMouseX = e.clientX; lastMouseY = e.clientY; // 灵敏度可自行调整,俯仰角限制在-89°~89°避免翻转 Render.#cy -= deltaX * 0.2; Render.#cx = Math.max(-89, Math.min(89, Render.#cx - deltaY * 0.2)); });
其他渲染逻辑修正
- 把透视投影近裁剪面从0.0改为0.1:
m4.perspective(fov, aspect, 0.1, 100.0, this.projection); - 把三层循环创建Block的逻辑移到
load阶段,创建后存入数组,render循环遍历数组调用每个block的render()方法,不要每帧新建对象 - 删除webgl2上下文同canvas获取2d上下文的代码,UI单独用叠加的透明canvas或者DOM实现
内容的提问来源于stack exchange,提问作者Bomber87t
相关产品推荐
相关产品推荐

