Three.js 立方体面相对于相机的旋转矢量

Three.js cube face rotation vector in relation to camera

我有一个旋转球体,上面附有 div 示例可以在此处查看:https://jsfiddle.net/ao5wdm04/ 我计算 x 和 y 值并使用 translate3d 变换放置 div,效果很好。

我的问题是如何获取 rotateXrotateYrotateZrotate3d 转换的值,以便 div "tangents"球面。我知道立方体网格面向球体中心,所以我假设相对于相机的外向法向量的旋转向量将包含我需要的值。但是我不太确定如何获得这些。

更新

通过使用欧拉角,我几乎达到了预期的效果,如下所示:https://jsfiddle.net/ao5wdm04/1/ 但旋转不够大。

免责声明:我对three.js一无所知。我刚刚完成了一些 OpenGL。

您的欧拉角来自模型视图投影原点(第 74-80 行)。我看不出这背后的逻辑。

如果您的 div 在球体表面上,那么它应该在 div 的位置由球体的法线定向。幸运的是,你已经有了这些角度。他们被命名为 rotation.

如果您将第 82-84 行中的欧拉角替换为用于定位 div 的旋转角度,那么在我的浏览器中,div 在位于圆圈,当它位于中心时面向上。它看起来像是在绕圈移动,边缘靠近屏幕。这是你想要的效果吗?

我对链接代码的修改:

82 var rotX = (rotation.x * (180/ Math.PI));
83 var rotY = (rotation.y * (180/ Math.PI));
84 var rotZ = 0;

编辑

啊,好的。 rotation 变量就是相机的变量。它控制赤道的切线。您还需要修改方向以考虑纬度。

使 rotY 等于负纬度。然后确保这种旋转发生在赤道旋转之前。旋转不可交换。

综上所述,https://jsfiddle.net/ao5wdm04/1/处的代码变化如下:

27 var lat = 45 * Math.PI / 180;
...
82 var rotX = (rotation.x * (180/ Math.PI));
83 var rotY = - 45;
...
88 document.getElementById('face').style.webkitTransform = 'translate3d(' + x + 'px,' + y + 'px,0px) rotateY('+rotX+'deg) rotateX('+rotY+'deg)';

我不知道纬度应该如何在 initrender 函数之间传播。正如我所说,我不熟悉这种语言。

有关 openGL 或任何其他图形中的变换和旋转的详细信息,请查看 here


基本 -

3D世界基本上有2种变换-

  1. Translation
  2. Rotation

给出了一个关于这件事的小例子here


如果你全部看完,我想你对 3D 转换系统有一个清晰的概念。

如果你能理解这些,你就可以很容易地模拟 :) 因为你需要同时为每个动作做这两件事。

试试这个代码-

var camera, scene, renderer, raycaster, geometry, material, mesh, box;
var rotation = {
  x: 0,
  y: 0
};
var distance = 500;

init();
animate();

function init() {
  raycaster = new THREE.Raycaster(); ;
  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
  camera.position.z = distance;
  camera.position.y = 100;
  scene.add(camera);

  geometry = new THREE.SphereGeometry(100, 50, 50, 50);
  material = new THREE.MeshNormalMaterial();

  mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);

  var transform = new THREE.Matrix4().getInverse(scene.matrix);

  var lat = 0 * Math.PI / 180;
  var lon = 90 * Math.PI / 180;
  var r = 100;
  var p = new THREE.Vector3(-r * Math.cos(lat) * Math.cos(lon),
    r * Math.sin(lat),
    r * Math.cos(lat) * Math.sin(lon)
  );
  p.applyMatrix4(transform);

  var geometry = new THREE.CubeGeometry(10, 10, 10);
  box = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({
    color: 0xff0000,
    
  }));
  box.position.set(p.x, p.y, p.z);
  box.lookAt(mesh.position);
  //scene.add(box);
  box.updateMatrix();

  renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);

  document.body.appendChild(renderer.domElement);

}

function animate() {

  requestAnimationFrame(animate);
  render();
}

function render() {

  rotation.x += 0.01;
  camera.position.x = distance * Math.sin(rotation.x) * Math.cos(rotation.y);
  camera.position.y = distance * Math.sin(rotation.y);
  camera.position.z = distance * Math.cos(rotation.x) * Math.cos(rotation.y);

  camera.lookAt(mesh.position);

  var w = window.innerWidth;
  var h = window.innerHeight;

  var mat = new THREE.Matrix4();
  var v = new THREE.Vector3();

  mat.copy(scene.matrix);
  mat.multiply(box.matrix);
  v.set(0, 0, 0);
  v.applyMatrix4(mat);
  v.project(camera);
  
  var euler = new THREE.Euler().setFromVector3(v);
  
  var rotX = (rotation.x * (180/ Math.PI));
  var rotY = (rotation.y * (180/ Math.PI));
  var rotZ = 0;
  
  var x = (w * (v.x + 1) / 2) - 12.5; //compensate the box size
  var y = (h - h * (v.y + 1) / 2) - 12.5;
  document.getElementById('face').style.webkitTransform = 'translate3d(' + x + 'px,' + y + 'px,0px) rotateX('+rotY+'deg) rotateY('+rotX+'deg) rotateZ('+rotZ+'deg)';

  renderer.render(scene, camera);

}
#face {
  position: absolute;
  width: 25px;
  height: 25px;
  border-radius: 50%;
  background-color: red;
}
<script src="https://rawgit.com/mrdoob/three.js/master/build/three.min.js"></script>

<div id="face"></div>