javascript - 三个 JS 旋转组,与 View Cube/Orientation Cube 的相机方向相同

标签 javascript vector three.js quaternions coordinate-transformation

我正在尝试使用三个 JS 制作一个看起来像这样的 blender-esk 方向 View :
enter image description here
我的计划是让相机旋转并旋转 3 个矢量点(代表图片中的 X、Y 和 Z 气泡)。然后,一旦我有了这些点,我就可以使用 X 和 Y 坐标在 Canvas 上绘制圆圈并绘制线条以将它们连接到中心。然后我可以使用 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 中)
  • 我试过只使用相机局部旋转(并尝试否定它)

  • 备注
    我可以在第二个场景中旋转相机以匹配主视图中的相机,但我真的想自己旋转矢量。这样我就可以将这些点投影到基本 Canvas 上以绘制 6 个圆圈和 3 条线。我觉得这比尝试在 3d 中使用 Sprite 要容易得多,而且三个 JS 在不为其创建矩形网格的情况下无法轻松绘制粗线。
    以下是我希望输出看起来像的一些示例:
    enter image description here
    enter image description here
    更新
    Rabbid76 解决了这个问题,如果有人有兴趣在他们的项目中使用 Blender 风格的方位立方体,你可以在这里找到完整的源代码:
    https://github.com/jrj2211/three-orientation-gizmo/
    或者使用 npm 下载:
    https://www.npmjs.com/package/three-orientation-gizmo

    最佳答案

    首先在 View 空间中,X 轴指向右侧,Y 轴指向上方,Z 轴指向 Canvas 外。
    所以你必须为 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);
        }
    
        // [...]
    

    View 矩阵是该矩阵的逆矩阵,它定义了相机的位置和方向。您想要可视化相机的方向。您想要可视化 View 矩阵的方向。
    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>

    关于javascript - 三个 JS 旋转组,与 View Cube/Orientation Cube 的相机方向相同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60245700/

    相关文章:

    css - THREE.js CSS2D 文本标签 - 它们可以被 RayCasting 相交和识别吗?

    php - 在按钮上单击重复的 HTML 代码

    javascript - 状态更改后保持元素处于焦点

    c++ - vector 储备 C++

    c++ - vector 如何找到第一个和最后一个当前值

    javascript - 在 Three.js 中只绘制一行

    javascript - 导入不适用于 Node.js 版本 11.8.0

    带有 Jquery Mobile 的 Javascript 不适用于 Android 2.3 及更低版本

    r - 矢量化我的想法 : Vector Operations in R

    three.js - 在three.js中获取字体