【问题标题】:Three JS rotate group with same orientation as camera for View Cube/Orientation Cube三个 JS 旋转组,与 View Cube/Orientation Cube 的相机方向相同
【发布时间】:2020-02-16 05:34:36
【问题描述】:

我正在尝试使用三个 JS 制作一个看起来像这样的 blender-esk 方向视图:

我的计划是让相机旋转并旋转 3 个矢量点(代表图片中的 X、Y 和 Z 气泡)。然后,一旦我有了这些点,我就可以使用 X 和 Y 坐标在画布上绘制圆圈并绘制线以将它们连接到中心。然后我可以使用 Z 方向来确定应该在什么之上绘制什么。

目前我正在使用第二个 three.js 场景来可视化这些 X、Y 和 Z 点的位置,因为我尝试了不同的方法来旋转顶点组以使其与相机的方向匹配。我在这部分苦苦挣扎,我尝试了很多没用的方法。

这是我目前所在位置的 JS Fiddle:

https://jsfiddle.net/j9mcL0x4/4/ (在 Brave 中不加载,但在 Chrome 中有效)

当您平移相机时,它会移动第二个场景中的 3 个立方体,但我无法让它正确旋转。

X、Y 和 Z 点由一组 Vector3 表示:

this.ref = [
  [new THREE.Vector3(1,0,0), 0xFF0000],
  [new THREE.Vector3(0,1,0), 0x00FF00],
  [new THREE.Vector3(0,0,1), 0x0000FF],
]

然后我创建每个立方体并将其添加到一个组中:

this.group = new THREE.Group();
for(var pos of this.ref) {
  var geometry = new THREE.BoxGeometry(.25,.25,.25);
  var material = new THREE.MeshBasicMaterial({
    color: pos[1]
  });
  var cube = new THREE.Mesh(geometry, material);
  cube.position.copy(pos[0]);
  this.group.add(cube);
}

this.scene.add(this.group);

然后在我的动画函数中,我试图计算组的旋转,使其与主视图的方向相同:

var quaternion = new THREE.Quaternion();
camera.getWorldQuaternion( quaternion );

let rotation = new THREE.Euler();
rotation.setFromQuaternion(quaternion);

var dir = new THREE.Vector3();
dir.subVectors( camera.position, controls.target ).normalize();

orientationHelper.group.rotation.set(dir.x, dir.y, dir.z);
orientationHelper.animate();

我所拥有的是完全错误的,但这是我尝试过的一些事情:

  • 计算相机前方的一个点,然后使用 lookAt 函数使小组朝那个方向看
  • 我正在使用轨道控制,它有一个相机聚焦的目标。我试过减去相机位置向量和目标向量来获得观察方向,但这没有用(目前在 jsfiddle 中)
  • 我试过只使用相机局部旋转(并尝试否定它)

注意 我可以在第二个场景中旋转相机以匹配主视图中的相机,但我真的想自己旋转矢量。这样我就可以将这些点投影到基本画布上以绘制 6 个圆圈和 3 条线。我觉得这会比尝试在 3d 中使用 sprite 容易得多,而且三个 JS 在不为其创建矩形网格的情况下无法轻松绘制粗线。

以下是我希望输出的一些示例:

更新

Rabbid76 解决了这个问题,如果有人有兴趣在他们的项目中使用 Blender 风格的方位立方体,您可以在这里找到完整的源代码:

https://github.com/jrj2211/three-orientation-gizmo/

或者用 npm 下载:

https://www.npmjs.com/package/three-orientation-gizmo

【问题讨论】:

  • 太棒了。感谢分享
  • 很棒的作品,感谢分享。
  • 谢谢!我还添加了一个 npm 链接,以防你使用 nodejs 并想使用它

标签: javascript vector three.js quaternions coordinate-transformation


【解决方案1】:

首先在视图空间中,X 轴指向右侧,Y 轴指向上方,Z 轴指向画布外。
所以你必须为OrientationHelper相机在正Z轴上选择一个初始位置:

class OrientationHelper {
    constructor(canvas) {
        // [...]

        this.camera = new THREE.PerspectiveCamera(75, size / size, 0.1, 1000);
        this.camera.position.set(0, 0, 2);

        // [...]

下面我建议在OrientationHelper场景中通过矩阵设置组的方向:

class OrientationHelper {
    // [...]

    setCameraRotation(rotation) {
        this.group.setRotationFromMatrix(rotation);
    }

    // [...]

视图矩阵是该矩阵的逆矩阵,它定义了相机的位置和方向。您想要可视化相机的方向。您想要可视化视图矩阵的方向。
camera.rotation 创建一个旋转矩阵。计算Inverse matrix 并通过计算矩阵设置OrientationHelper 的方向:

let rotMat = new THREE.Matrix4().makeRotationFromEuler(camera.rotation);
let invRotMat = new THREE.Matrix4().getInverse(rotMat); 

orientationHelper.setCameraRotation(invRotMat);
orientationHelper.update();

看例子:

class OrientationHelper {
  constructor(canvas) {
    this.canvas = canvas;
    
    this.ref = [
      [new THREE.Vector3(1,0,0), 0xFF0000],
      [new THREE.Vector3(0,1,0), 0x00FF00],
      [new THREE.Vector3(0,0,1), 0x0000FF],
    ]

    var size = 200;
    this.scene = new THREE.Scene();
    this.camera = new THREE.PerspectiveCamera(75, size / size, 0.1, 1000);
    this.camera.position.set(0, 0, 2);

    this.renderer = new THREE.WebGLRenderer({alpha: true});
    this.renderer.setSize(size, size);
    canvas.appendChild(this.renderer.domElement);
    
    var controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
    
    var geometry = new THREE.SphereGeometry( .1, 32, 32 );
    var material = new THREE.MeshBasicMaterial( {color: 0xffffff} );
    var sphere = new THREE.Mesh( geometry, material );
    this.scene.add( sphere );

    this.group = new THREE.Group();
    for(var pos of this.ref) {
      var geometry = new THREE.BoxGeometry(.25,.25,.25);
      var material = new THREE.MeshBasicMaterial({
        color: pos[1]
      });
      var cube = new THREE.Mesh(geometry, material);
      cube.position.copy(pos[0]);
      this.group.add(cube);
    }

    this.scene.add(this.group);
  }

  setCameraRotation(rotation) {
    this.group.setRotationFromMatrix(rotation);
  }
  
  update() {
  	this.renderer.render(this.scene, this.camera);
  }
}

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 controls = new THREE.OrbitControls(camera, renderer.domElement);
camera.position.set(0, 3, 3);
controls.update();

//controls.autoRotate = true;

var size = 10;
var divisions = 10;

var gridHelper = new THREE.GridHelper(size, divisions);
scene.add(gridHelper);

var axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);

 // material
  var material = new THREE.MeshBasicMaterial({
    color: 0xffffff,
    vertexColors: THREE.FaceColors
  });

var geometry = new THREE.BoxGeometry();

 // colors
  red = new THREE.Color(1, 0, 0);
  green = new THREE.Color(0, 1, 0);
  blue = new THREE.Color(0, 0, 1);
  var colors = [red, green, blue];
  
  for (var i = 0; i < 3; i++) {
    geometry.faces[4 * i].color = colors[i];
    geometry.faces[4 * i + 1].color = colors[i];
    geometry.faces[4 * i + 2].color = colors[i];
    geometry.faces[4 * i + 3].color = colors[i];
  }
  
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);

// Orientation
var orientationHelper = new OrientationHelper(document.getElementById("orientation-helper"));

function animate() {
  requestAnimationFrame(animate);
  controls.update();

  let rotMat = new THREE.Matrix4().makeRotationFromEuler(camera.rotation);
  let invRotMat = new THREE.Matrix4().getInverse(rotMat); 
  orientationHelper.setCameraRotation(invRotMat);
	orientationHelper.update();
  
  renderer.render(scene, camera);
}

animate();
body { margin: 0; }
canvas { display: block; }
#orientation-helper { position: absolute; top: 10px; left: 10px; background: #505050; }
<script src="https://rawcdn.githack.com/mrdoob/three.js/r113/build/three.js"></script>
<script src="https://rawcdn.githack.com/mrdoob/three.js/r113/examples/js/controls/OrbitControls.js"></script>
<div id='orientation-helper'></div>

【讨论】:

  • 完美!也感谢您的精彩解释
猜你喜欢
  • 2016-07-20
  • 2013-01-22
  • 2015-01-30
  • 2013-09-10
  • 2020-08-29
  • 2020-06-13
  • 2021-04-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多