7

用到一个基础的 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 旋转之后会移动到什么位置, 还是要基于该平面进行计算:

clipboard.png

四元数可以表示成 (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...
再复杂一些的空间的转换, 应该就需要手动基于四元数或者空间向量计算, 太难, 再看了...


题叶
17.3k 声望2.6k 粉丝

Calcit 语言作者


引用和评论

0 条评论