首页 > 解决方案 > THREE.js ArrowHelper 不服从固有的欧拉角旋转

问题描述

我想将内在旋转设置为THREE.ArrowHelper. 据我了解,THREE.js 原生使用固有的 Tait-Bryan 欧拉角来表示 3D 旋转。

在下面的代码中,我创建了一个表示 x 轴的单位向量THREE.Vector3(1, 0, 0)

然后我将它绕 Y 轴和 Z 轴旋转任意量。

由于围绕 Y 和 Z 进行了一些旋转,局部坐标系的 X 轴(我假设沿红色矢量的点)也发生了变化。

所以,当我应用关于 的旋转时X,我不希望箭头移动(除了原地旋转......但这不应该是可见的)。

相反,我看到箭头四处扫动,好像它围绕某个任意轴旋转,而不是它的局部 x 轴。

感谢您的帮助!

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );

var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

var origin = new THREE.Vector3(0, 0, 0);
var xDir = new THREE.Vector3(1, 0, 0);
var length = 1;
var arrow = new THREE.ArrowHelper(xDir, origin, length, 0xff0000);
arrow.rotation.order = 'XYZ';
arrow.rotation.y = 0.5;
arrow.rotation.z = 0.5;
scene.add(arrow);

camera.position.z = 5;

var animate = function () {
  requestAnimationFrame( animate );

	arrow.rotation.x += 0.01;
  
  renderer.render( scene, camera );
};

animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>

标签: javascriptthree.js

解决方案


问题是它ArrowHelper正在做自己的特殊旋转数学。它会创建一个向上的单位箭头 (+Y)。它使用自定义数学来设置方向,以使该线点在本地空间中的给定方向上。

如果您只是创建箭头然后打印旋转,您可以看到这一点

  var origin = new THREE.Vector3(0, 0, 0);
  var xDir = new THREE.Vector3(1, 0, 0);
  var length = 1;
  var arrow = new THREE.ArrowHelper(xDir, origin, length, color);
  console.log(arrow.rotation.x, arrow.rotation.y, arrow.rotation.z);

你会看到rotation.z 已经设置为将+Y 箭头旋转到+X,所以你去改变这些旋转,箭头不再基于朝向+X。

这意味着通过操纵箭头arrow.rotation不会按预期工作。

如果您将箭头作为父Object3D对象,然后旋转该对象,它将按预期工作(或按我的预期)

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );

var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

scene.add(new THREE.GridHelper(10, 10));

function addArrow(x, ry, rz, color) {
  var origin = new THREE.Vector3(0, 0, 0);
  var xDir = new THREE.Vector3(1, 0, 0);
  var length = 1;
  var arrow = new THREE.ArrowHelper(xDir, origin, length, color);

  var ob = new THREE.Object3D();
  ob.position.x = x;
  ob.rotation.order = 'XYZ';
  ob.rotation.y = ry;
  ob.rotation.z = rz;
  scene.add(ob);  
  ob.add(arrow);
  return ob;
}

addArrow(-4,   0,   0, 0xFF0000);
addArrow(-2,   0, 0.5, 0x00FF00);
addArrow( 0, 0.5, 0.5, 0xFFFF00);
const arrow = addArrow( 2, 0.5, 0.5, 0x00FFFF);

camera.position.z = 6;

const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0);
controls.update();

var animate = function () {
  requestAnimationFrame( animate );
  	arrow.rotation.x += 0.01;

  renderer.render( scene, camera );
};
animate();
body { margin: 0; }
canvas { display: block; }
<script src="https://cdn.jsdelivr.net/npm/three@0.115.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.115.0/examples/js/controls/OrbitControls.js"></script>

我的期望。

旋转在本地坐标系中。旋转顺序“XYZ”的意思是,假设你只有箭头,完整的矩阵计算将是

matrix = projection * 
         view *
         obTranslation *
         obXAxisRotation * 
         obYAxisRotation * 
         obZAxisRotation * 
         obScale * 
         arrowOrientation;

任何状况之下

var origin = new THREE.Vector3(0, 0, 0);
var xDir = new THREE.Vector3(1, 0, 0);
var length = 1;
var arrow = new THREE.ArrowHelper(xDir, origin, length, 0xff0000);
var ob = new THREE.Object3D();
scene.add(ob);
ob.add(arrow);
ob.rotation.order = 'XYZ';

从 0、0、5 看,这给了我们一个指向右侧的箭头。

我喜欢查看从右到左应用的矩阵,因此将应用第一个比例以上的公式。它是 1,1,1 所以没有变化

接下来应用 zAxisRotation。0.5 弧度大约是 30 度,所以箭头现在要么向上 30 度

接下来应用 yAxisRotation。向上 30 度的箭头现在指向远处 30 度。

下一个 xAxisRotation 被应用,所以这个时髦的箭头将围绕 x 旋转

运行它并拖动上面的示例以从上方查看。您会看到它与描述相符。

所以这取决于你。您可以制作一个面向 ArrowHelper 的 +X 并将其作为 Object3D 的父级,或者您可以只知道 ArrowHelper 实际上制作了一个 +Y 箭头,然后在知道这一点后适当地设置旋转。


推荐阅读