基于Three.js中的本地向量定位子网格位置

4

我需要帮助将网格定位到给定的局部坐标点。我创建了高度为0.1的粉色圆柱,以展示我想要让立方体互相接触的局部点。

同时,我希望cube2成为cube1的子对象,但这并不是必须的。

期望结果:立方体的特定角落相互接触,并以正确的旋转方式排列 - 粉色圆柱应该完美地覆盖彼此。

我尝试用以下方法解决:

cube2.position.copy(cube_position.clone().add(cube2_position))
cube2.rotation.setFromVector3(cube_rotation.clone().add(cube2_rotation))

但是这并没有像预期的那样起作用。我应该使用某种矩阵变换吗?

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(25, 25, 12);
// setup cubes

var geometry = new THREE.CubeGeometry(6, 6, 6);
var material = new THREE.MeshLambertMaterial({
  color: 0x00fff0
});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);

cube.position.set(0.3, 1, -2);
cube.rotation.set(Math.PI / 3, Math.PI / 2, 1)

var geometry2 = new THREE.CubeGeometry(4, 4, 4);
var material2 = new THREE.MeshLambertMaterial({
  color: 0x0fff00
});
var cube2 = new THREE.Mesh(geometry2, material2);
cube.add(cube2);

cube_position = new THREE.Vector3(3, 3, 3)
cube_rotation = new THREE.Vector3(Math.PI / 3, Math.PI / 4, 0)
cube2_position = new THREE.Vector3(2, -2, 2)
cube2_rotation = new THREE.Vector3(Math.PI / 2, Math.PI / 2, 0)

// visualize points
vmat = new THREE.MeshLambertMaterial({
  color: 0xFF00FF,
  opacity: 0.5,
  transparent: true
});
v1 = new THREE.Mesh(new THREE.CylinderGeometry(1, 1, 0.1, 10), vmat);
cube.add(v1)
v1.position.copy(cube_position)
v1.rotation.setFromVector3(cube_rotation)

v2 = new THREE.Mesh(new THREE.CylinderGeometry(1, 1, 0.1, 10), vmat);
cube2.add(v2)
v2.position.copy(cube2_position)
v2.rotation.setFromVector3(cube2_rotation)

// BUG: connect cube with cube2 on specified points

cube2.position.copy(cube_position.clone().add(cube2_position))
cube2.rotation.setFromVector3(cube_rotation.clone().add(cube2_rotation))

// setup rest
var pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.x = 10;
pointLight.position.y = 50;
pointLight.position.z = 130;
scene.add(pointLight)

const renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x20252f);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);

const controls = new THREE.OrbitControls(camera, renderer.domElement);

animate();

function animate() {
  requestAnimationFrame(animate);
  render();
}

function render() {
  renderer.render(scene, camera);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r82/three.js"></script>
<script src="https://yume.human-interactive.org/examples/buffer-geometry/OrbitControls.js"></script>

1个回答

1

所以,矩阵在解决旋转问题方面非常强大。我采取了两个步骤来正确设置cube2的旋转和位置。

  • 旋转

    cube1cube2v1v2 之间的关系如下: enter image description here 我们想要的是 v2 的世界旋转等于 v1 的相反世界旋转。当两个对象具有相同的旋转时,它们具有相同的旋转矩阵(世界矩阵)。我们有以下公式:

    V1opposite.w = V2.w
    V1opposite.l * C1.w = V2.l * C2.l * C1.l
    V1opposite.l = V2.l * C2.l
    C2.l = V1opposite.l * V2.l(-1)
    

    其中,V1opposite.w 表示 v1 的相反世界矩阵,V2.w 是 v2 的世界矩阵,C 表示 cube,V2.l(-1) 表示 v2 的本地矩阵的逆矩阵。现在,我们可以计算出 cube2 的旋转矩阵。

  • 位置

    我们需要让 v1v2 的世界位置相同。下面是一个图表,展示了坐标空间:enter image description here 因为 v2cube2 的子对象,我们需要移动 cube2。在世界空间中,我们应该将 cube2v2 移动到 v1 的距离,现在我们得到了 cube2 的世界位置,cube2cube1 的子对象,只需应用 cube1 的逆矩阵即可获得 cube2 的本地位置。

这里有一个 jsfiddle example

希望它能帮到你。

更新

我写了一个函数,你可以在不使用 v1v2 的情况下使用它。

//connect obj1 and obj2 on obj1's point1 and obj2's point2.
function connect(obj1,obj2,point1,point2)
{
    var point1World = new THREE.Vector3().copy(point1).applyMatrix4(obj1.matrixWorld);
    var point2World = new THREE.Vector3().copy(point2).applyMatrix4(obj2.matrixWorld);
    var translation = new THREE.Vector3().subVectors(point1World,point2World);
    var obj2World = new THREE.Vector3().copy(obj2.getWorldPosition());
    obj2World.add(translation);
    if(obj2.parent !== null)
    {
        var inverseMatrix = new THREE.Matrix4().getInverse(obj2.parent.matrixWorld);
        obj2World.applyMatrix4(inverseMatrix);
    }
    obj2.position.copy(obj2World);
}

我还添加了一个祖先立方体来包含cube1cube2jsfiddle


嗯,这有点帮助。v1和v2只是为了示范而创建的,所以不应该用来解决这个问题。而且我不能在计算过程中使用渲染。我正在尝试解决这个问题,以便在我的应用程序中可以使用您的解决方案,但似乎是不可能的。 - Patryk
我进行了渲染,因为我需要更新v2和cube2的位置,我认为这很方便。如果您不想这样做,可以让它们的位置应用变换矩阵。除了使用这种方法之外,您还希望如何解决?有任何限制条件吗?我可以继续帮助您。 - Craig.Li
我在我的实际应用程序中实现了您的解决方案,但是当它是第一个立方体的孙子(子代的子代)时,一切都有奇怪的偏移量。这让我疯狂,我不知道如何正确实现它。 - Patryk
这更加复杂,我不能分享源代码。基本上,我正在构建一个有指导的3D编辑器版本。v1和v2演示了两个网格之间的连接。在那个编辑器中,您将每个元素的位置作为其他元素的子元素。您可以拥有无限数量的子代。此外,当我应用您的解决方案时,除了仅在一代子元素上定位之外,每次运行代码时,它使网格移动到全局0,0,0位置的瞬间。 - Patryk
当所有元素都是场景的子元素时,我会尝试使其正常工作。 - Patryk
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接