【threejs】实现绕指定点/物体旋转

您所在的位置:网站首页 ai中如何围绕一个点旋转 【threejs】实现绕指定点/物体旋转

【threejs】实现绕指定点/物体旋转

2024-07-12 00:09| 来源: 网络整理| 查看: 265

背景

实现方块A绕方块B旋转(方块B在原点,且不动)

 

思路

将方块A平移到方块B所在位置,旋转方块A,将方块A平移回去

方向上没错,但缺少细节,下面我们分两种情况实践一下。(区别就在于:平移时,使用的坐标系)

方法一:对方块A,基于世界坐标系平移,再基于自身/局部坐标系旋转,最后基于世界坐标系平移回去。

方法二:对方块A,基于自身坐标系平移,再基于自身/局部坐标系旋转,最后基于自身坐标系平移回去。

 

过程

方法一:

基于世界坐标系平移(按照红轴方向,平移-N个单位)

再基于自身/局部坐标系旋转(绕蓝轴旋转K度)

最后基于世界坐标系平移回去(按照红轴方向,平移N个单位)

表现:对比最初的状态,方块A看起来是在原地绕自身旋转。

 

方法二:

基于自身坐标系平移(按照红轴方向,平移-N个单位)

再基于自身/局部坐标系旋转(绕蓝轴旋转K度)

最后基于自身坐标系平移回去(按照红轴方向,平移N个单位)

表现:对比最初的状态,方块A表现出绕物体B旋转。

 

分析

将两方法结合对比可得:

第一步:效果一致。

按世界坐标系的红轴 和 自身坐标系的红轴方向完全一致,所以平移效果一致。

第二步:效果一致。

实现一致,都是按照自身坐标系旋转

第三步:效果不一致。

方法一世界坐标系的红轴不受物体A旋转影响,世界的红轴方向不变。所以平移回去,还是最初的位置。

方法二自身坐标系受到的旋转的影响,此时自身坐标系的红轴方向改变,因此根据变化方向后的红轴平移回去的结果,和最初的位置不同。

 

疑问

Q:为什么第一步和第三步的平移,要么都是按世界坐标系,要么都是按自身坐标系?不可以第一步按世界坐标系,第三步按自身坐标系吗?

A:为了方便,因为计算第一步的平移矩阵M后,第三步的平移矩阵只需要用api求出M的逆矩阵。

第二个问题是可以按照这个思路,你只要确保算出的平移矩阵是对的就行。

 

 

总结

绕定点/物体旋转,最简单的方式是:将动点基于自身坐标系平移到定点的位置,(动点)再基于自身坐标系旋转,最后(动点)基于自身坐标系平移回去。

 

部分核心ts代码

感受threejs 里multiply和premultiply的应用,

还涉及到“同一向量在不同基底上表示为不同坐标”的计算(应用场景:某物体在世界坐标系下平移到目标点的向量是T1,那转换到某物体的局部坐标系下,T1在局部坐标系下如何计算)。

(链接为gitchat专栏《机器学习中的数据:线性代数》 的第二篇 空间:从向量和基底谈起  ,专栏29元,16篇,个人觉得还挺划算的)

// 世界平移-自身旋转-世界平移的逆(表现为原地旋转) public worldTranslation_SelfRotate() { let worldPosZero = this.getWorldPosition(this.cubeArr[0]);//本质上就是Object3D里的getWorldPosition() 获取世界坐标 let worldPosOne = this.getWorldPosition(this.cubeArr[1]); let worldOffset = worldPosZero.sub(worldPosOne); let goZeroMatrix = new Matrix4().makeTranslation(worldOffset.x, worldOffset.y, worldOffset.z); this.cubeArr[1].matrix.premultiply(goZeroMatrix); let angle = 30 * Math.PI / 180; let absAngle = Math.abs(angle); this.cubeArr[1].matrix.multiply(new THREE.Matrix4().makeRotationZ(absAngle)); let mat4I = new THREE.Matrix4(); mat4I.copy(goZeroMatrix).invert();// mat4I.getInverse(goZeroMatrix);getInverse函数已弃用 this.cubeArr[1].matrix.premultiply(mat4I); } // 自身平移-自身旋转-自身平移的逆(表现为绕轴旋转) public SelfTranslation_SelfRotate() { let worldPosZero = this.getWorldPosition(this.cubeArr[0]); let worldPosOne = this.getWorldPosition(this.cubeArr[1]); let worldOffset = worldPosZero.sub(worldPosOne); // 世界空间转局部空间 let itemBaseVec = this.getBasisVec(this.cubeArr[1].matrix);//本质上就是extractBasis() 获取基坐标系 let localOffset = this.vectorChangBasic(worldOffset,itemBaseVec); let goZeroMatrix = new Matrix4().makeTranslation(localOffset.x, localOffset.y, localOffset.z); this.cubeArr[1].matrix.multiply(goZeroMatrix); let angle = 30 * Math.PI / 180; let absAngle = Math.abs(angle); this.cubeArr[1].matrix.multiply(new THREE.Matrix4().makeRotationZ(absAngle)); let mat4I = new THREE.Matrix4(); mat4I.copy(goZeroMatrix).invert();// mat4I.getInverse(goZeroMatrix);getInverse函数已弃用 this.cubeArr[1].matrix.multiply(mat4I); } private vectorChangBasic(needChangeVec: Vector3, baseVec: baseVectorObj) { // 世界空间的相对位置,转成基于baseVec坐标系下的位置 let newVec: Vector3 = new Vector3(); newVec.x = needChangeVec.dot(baseVec.x); newVec.y = needChangeVec.dot(baseVec.y); newVec.z = needChangeVec.dot(baseVec.z); return newVec; } 项目

https://github.com/LJLCarrien/threejsCube 分支 test_2Rotate

初衷:为了学下矩阵和threejs



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3