Three.js 立方体面相对于相机的旋转矢量
Three.js cube face rotation vector in relation to camera
我有一个旋转球体,上面附有 div 示例可以在此处查看:https://jsfiddle.net/ao5wdm04/
我计算 x 和 y 值并使用 translate3d
变换放置 div,效果很好。
我的问题是如何获取 rotateX
、rotateY
和 rotateZ
或 rotate3d
转换的值,以便 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)';
我不知道纬度应该如何在 init
和 render
函数之间传播。正如我所说,我不熟悉这种语言。
有关 openGL 或任何其他图形中的变换和旋转的详细信息,请查看 here。
基本 -
3D世界基本上有2种变换-
给出了一个关于这件事的小例子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>
我有一个旋转球体,上面附有 div 示例可以在此处查看:https://jsfiddle.net/ao5wdm04/
我计算 x 和 y 值并使用 translate3d
变换放置 div,效果很好。
我的问题是如何获取 rotateX
、rotateY
和 rotateZ
或 rotate3d
转换的值,以便 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)';
我不知道纬度应该如何在 init
和 render
函数之间传播。正如我所说,我不熟悉这种语言。
有关 openGL 或任何其他图形中的变换和旋转的详细信息,请查看 here。
基本 -
3D世界基本上有2种变换-
给出了一个关于这件事的小例子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>