在 Three.js 中基于局部向量定位子网格

Posted

技术标签:

【中文标题】在 Three.js 中基于局部向量定位子网格【英文标题】:Position child mesh based on local vectors in Three.js 【发布时间】:2017-12-10 13:16:09 【问题描述】:

我需要帮助将网格定位到根据本地坐标给出的特定点。我创建了高度为 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】:

因此,Matrix 在解决旋转问题方面确实非常强大。我分两个步骤来正确设置cube2 旋转和位置。

旋转

cube1,cube2,v1,v2之间的关系是: 我们想要的是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 的世界位置相同。这是一个显示坐标空间的图表: 因为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);

我还添加了一个祖父立方体以包含 cube1cube2。 jsfiddle

【讨论】:

嗯,这有点帮助,v1 和 v2 只是为了演示而创建的,所以它们不应该用来解决这个问题。我也不能在计算之间使用渲染。我正在尝试解决这个问题,所以我可以在我的应用程序中使用您的解决方案,但似乎不可能 我渲染是因为我需要更新v2和cube2的位置,我觉得很方便,如果你不想这样做,你可以让它们的位置应用变换矩阵。除了使用,你想用哪种方式解决它?有什么约束吗?我可以继续帮忙。 我在我的实际应用程序中实现了您的解决方案,当它是孙子(第一个立方体的孩子的孩子)时,一切都有奇怪的偏移。它让我发疯,我不知道如何正确实施。 @Patryk 我的解决方案基于 v1、v2。我想知道我是否可以用立方体的 conner 顶点来实现它。我想知道你的最终目标, 只是在角落里碰它们?它们之间有固定的旋转吗? 比较复杂,不能分享源码。基本上我正在构建一个引导版本的 3d 编辑器。 v1 和 v2 展示了两个网格之间的连接。在那个编辑器中,您将每个元素定位为其他一些元素的子元素。你可以有无数代的孩子。此外,当我应用您的解决方案时,除了仅定位在一代子代上之外,每次运行该代码时,它都会使网格移动到全局 0,0,0 瞬间。

以上是关于在 Three.js 中基于局部向量定位子网格的主要内容,如果未能解决你的问题,请参考以下文章

在three.js中将网格添加到场景之前如何设置网格的位置

three.js css3d 如何更新数据

在three.js中播放特定部分的网格动画

在 Three.js 中绘制网格的边缘

我可以在 three.js 中隐藏网格的面吗?

Three.js Raycaster 未检测到场景网格