关于 Threejs 模型旋转问题(笔记)
用到一个基础的 three.js 的拖拽旋转, 梳理了一下资料,
欧拉角旋转的问题
定义的话看 Wiki, 数学描述太晦涩, 没细看 https://zh.wikipedia.org/wiki...
简单的描述就是定义了沿着 X Y Z 方向的依次旋转, 来模拟空间当中的任何一个旋转,
但是用欧拉角描述旋转有问题, 就是一个空间旋转这里是拆成三个旋转来描述的,
存在问题, 就是不同的多个旋转合并是可能得到一个旋转的, 所以这种描述并不精确,
实际当中就导致一些问题了, 数学上有一些问题, 然后就是 three.js 使用当中有问题,
比如这个例子, 鼠标在 X Y 方向的拖拽, 分别对应到地面的水平渲染, 和视角在纵向的旋转,
https://threejs.org/examples/...
在拖到俯瞰地面的情况下, 再 X 方向拖动, 地面旋转的效果就很邪门了.. 就不是直观的旋转.
而且, 即便看一点点叠加的旋转的效果, 也是比较奇怪的,
比如这个例子, 尝试修改 X Y 不同的数值, 结果旋转的效果非常怪异:
https://codepen.io/jonnybonif...
function animate() { requestAnimationFrame(animate); mesh.rotation.x += 0.04; mesh.rotation.y += 0.04; renderer.render(scene, camera); }
三维空间的当中旋转的叠加和顺序的不同存在着关系, 所以处理起来不简单,
用欧拉角描述旋转应该是本来就有问题的, 具体的描述看知乎, 数学上的关系太复杂了,
知乎: 通俗的解释欧拉角,之后为何要引入四元数?
四元数
关于四元数, 有同学写了一个比较详细的文档(可惜 HTTPS 有点问题, 等他修吧):
Understanding Quaternions 中文翻译《理解四元数》
或者参考知乎: 如何形象地理解四元数?
数学归数学. 我看懂个大概, 按照空间向量去理解, 这个算式是很复杂了,
不过旋转本身还是发生在一个与旋转轴垂直的二维平面上的,
比如点 P 旋转之后会移动到什么位置, 还是要基于该平面进行计算:
四元数可以表示成 (x, y, z, w)
, 其中 X Y Z 是空间的向量, 而 W 是标量,
对比平面复数的 (x, y)
, 其中 X 是标量, Y 是向量.
而向量的正交和缩放的一些特性, 正好用来计算得到于旋转轴正交的那种平面,
最后可以得到一个通用的旋转的坐标计算公式...
理解是这样的理解, 具体计算过程还有得到的公式还是挺玄乎的, 看具体的推导吧.
(别来问我具体的推导是怎么回事, 我解释不清楚的, 随便看看吧...)
https://gist.github.com/jiyin...
https://gist.github.com/cheny...
相关代码
three.js 提供了四元数(Quaternion) API 用于完成四元数计算:
https://threejs.org/docs/#api...
效果还是不错的, 找到不少例子, 麻烦的地方是使用的 API 感觉挺难懂了.
比如这个完全看不懂矩阵怎么算的: https://codepen.io/OpherV/pen...
也找到一些基于角度进行计算的例子, 大致意思比如:
http://projects.defmech.com/T...
function rotateMatrix(rotateStart, rotateEnd) { var axis = new THREE.Vector3(), quaternion = new THREE.Quaternion(); var angle = Math.acos( rotateStart.dot(rotateEnd) / rotateStart.length() / rotateEnd.length() ); if (angle) { axis.crossVectors(rotateStart, rotateEnd).normalize(); angle *= rotationSpeed; quaternion.setFromAxisAngle(axis, angle); } return quaternion; }
还找到一个基于欧拉角换算(setFromEuler
)的 API 的例子, 这个就是最容易理解的了:
https://jsfiddle.net/MadLittl...
var deltaRotationQuaternion = new three.Quaternion() .setFromEuler(new three.Euler( toRadians(deltaMove.y * 1), toRadians(deltaMove.x * 1), 0, 'XYZ' )); cube.quaternion.multiplyQuaternions(deltaRotationQuaternion, cube.quaternion);
用这个例子, deltaMove
的坐标就可以很容易转化到 cube
的三维空间旋转了,
绕过了复杂的计算, 而且显得比较直观, 斜方向的鼠标拖动也比较自然的.
我直接用了这个代码.
Webpack 打包 Loader
另外关于 threejs 加载用于旋转的模型, 有个坑是 Webpack 打包 Loader, 直接看链接了:
https://gist.github.com/cecil...
plugins:[ new webpack.ProvidePlugin({ 'THREE': 'three' }), //... ]
import "three/examples/js/loaders/GLTFLoader.js";
其他
上面找到的代码可以解决拖拽和旋转, 可能以后还会涉及到旋转之后拖放的问题,
找到个例子还可以, 但是已经很复杂了 https://codepen.io/kaolay/pen...
再复杂一些的空间的转换, 应该就需要手动基于四元数或者空间向量计算, 太难, 再看了...