我们如何在不修改场景树结构或几何图形的情况下更改 Three.js 对象的旋转原点(轴心点)?
How can we change the rotation origin (pivot point) of a Three.js object without modifying scene tree structure or geometry?
我知道我们可以让对象有一个新的父对象作为轴心,或者我们可以调整网格内的几何位置。
但是,我们如何在不重新设置对象父级或修改对象父级,并且不修改对象的几何形状(如果它是网格)的情况下从数学上实现这一点?
换句话说,我们必须对其变换矩阵(或零件、旋转、位置、四元数等)做些什么才能达到同样的效果,同时满足上述不接触父级或几何体的要求?
取主元矩阵并求逆。应用逆矩阵时,会将枢轴放置在世界原点,并将您的对象放置在其他地方。现在你的对象是相对于枢轴点 [0,0,0].
应用您想要相对于枢轴点进行的变换。
- 重新应用初始主元矩阵(嘿,不是反转!)以将对象放置在之前的位置。
我的例子把所有步骤都分开了,主要是为了说明逻辑。当然,您不应该转换枢轴对象(也许您甚至没有)。所有步骤都可以压缩在一行公式中:
object.matrix = inverse(pivot.matrix)*someTranformationMatrix*pivot.matrix
您在此处找到的工作演示:https://jsfiddle.net/mmalex/hd8ex0ok/
// example for
let renderer;
let camera;
let controls;
let scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(54, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(new THREE.Color(0xfefefe));
document.body.appendChild(renderer.domElement);
camera.position.x = 4;
camera.position.y = 10;
camera.position.z = 4;
camera.lookAt(0, 0, 0);
controls = new THREE.OrbitControls(camera);
// white spotlight shining from the side, casting a shadow
let spotLight = new THREE.SpotLight(0xffffff, 2.5, 25, Math.PI / 6);
spotLight.position.set(9, 10, 1);
scene.add(spotLight);
var light = new THREE.AmbientLight(0x202020); // soft white light
scene.add(light);
// example starts here
let gridHelper = new THREE.GridHelper(4, 4);
scene.add(gridHelper);
var axesHelper = new THREE.AxesHelper(1);
axesHelper.applyMatrix(new THREE.Matrix4().makeTranslation(1.5, 0, -1.5));
axesHelper.updateMatrixWorld(true);
scene.add(axesHelper);
document.changePivot = function() {
axesHelper.position.set(-2 + 4*Math.random(), -2 + 4*Math.random(), -2 + 4*Math.random());
axesHelper.updateMatrixWorld(true);
}
const geometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
const material = new THREE.MeshStandardMaterial({
color: 0xff0000
});
const topBox = new THREE.Mesh(geometry, material);
topBox.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / 8));
topBox.applyMatrix(new THREE.Matrix4().makeTranslation(0.5, 1, -0.5));
scene.add(topBox);
let animate = function() {
requestAnimationFrame(animate);
// get world transforms from desired pivot
var pivot_matrix = axesHelper.matrixWorld.clone();
// inverse it to know how to move pivot to [0,0,0]
let pivot_inv = new THREE.Matrix4().getInverse(pivot_matrix, false);
// place pivot to [0,0,0]
// apply same transforms to object
axesHelper.applyMatrix(pivot_inv);
topBox.applyMatrix(pivot_inv);
// say, we want to rotate 0.1deg around Y axis of pivot
var desiredTransform = new THREE.Matrix4().makeRotationY(Math.PI / 180);
axesHelper.applyMatrix(desiredTransform);
topBox.applyMatrix(desiredTransform);
// and put things back, i.e. apply pivot initial transformation
axesHelper.applyMatrix(pivot_matrix);
topBox.applyMatrix(pivot_matrix);
controls.update();
renderer.render(scene, camera);
};
animate();
body {
margin: 0;
}
<button onclick="changePivot()">set random pivot</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/91/three.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
let renderer;
let camera;
let controls;
let scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(54, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(new THREE.Color(0xfefefe));
document.body.appendChild(renderer.domElement);
camera.position.x = 5;
camera.position.y = 15.5;
camera.position.z = 5.5;
camera.lookAt(0, 0, 0);
controls = new THREE.OrbitControls(camera);
// white spotlight shining from the side, casting a shadow
let spotLight = new THREE.SpotLight(0xffffff, 2.5, 25, Math.PI / 6);
spotLight.position.set(9, 10, 1);
scene.add(spotLight);
var light = new THREE.AmbientLight(0x202020); // soft white light
scene.add(light);
// example starts here
let gridHelper = new THREE.GridHelper(4, 4);
scene.add(gridHelper);
var axesHelper = new THREE.AxesHelper(1);
axesHelper.applyMatrix(new THREE.Matrix4().makeTranslation(1.5, 0, -1.5));
scene.add(axesHelper);
const geometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
const material = new THREE.MeshStandardMaterial({
color: 0xff0000
});
const topBox = new THREE.Mesh(geometry, material);
topBox.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / 8));
topBox.applyMatrix(new THREE.Matrix4().makeTranslation(0.5, 1, -0.5));
scene.add(topBox);
let animate = function() {
requestAnimationFrame(animate);
// get world transforms from desired pivot
axesHelper.updateMatrixWorld(true);
var pivot_matrix = axesHelper.matrixWorld.clone();
// inverse it to know how to move pivot to [0,0,0]
let pivot_inv = new THREE.Matrix4().getInverse(pivot_matrix, false);
// place pivot to [0,0,0]
// apply same transforms to object
axesHelper.applyMatrix(pivot_inv);
topBox.applyMatrix(pivot_inv);
// say, we want to rotate 0.1deg around Y axis of pivot
var desiredTransform = new THREE.Matrix4().makeRotationY(Math.PI / 180);
axesHelper.applyMatrix(desiredTransform);
topBox.applyMatrix(desiredTransform);
// and put things back, i.e. apply pivot initial transformation
axesHelper.applyMatrix(pivot_matrix);
topBox.applyMatrix(pivot_matrix);
controls.update();
renderer.render(scene, camera);
};
animate();
我知道我们可以让对象有一个新的父对象作为轴心,或者我们可以调整网格内的几何位置。
但是,我们如何在不重新设置对象父级或修改对象父级,并且不修改对象的几何形状(如果它是网格)的情况下从数学上实现这一点?
换句话说,我们必须对其变换矩阵(或零件、旋转、位置、四元数等)做些什么才能达到同样的效果,同时满足上述不接触父级或几何体的要求?
取主元矩阵并求逆。应用逆矩阵时,会将枢轴放置在世界原点,并将您的对象放置在其他地方。现在你的对象是相对于枢轴点 [0,0,0].
应用您想要相对于枢轴点进行的变换。
- 重新应用初始主元矩阵(嘿,不是反转!)以将对象放置在之前的位置。
我的例子把所有步骤都分开了,主要是为了说明逻辑。当然,您不应该转换枢轴对象(也许您甚至没有)。所有步骤都可以压缩在一行公式中:
object.matrix = inverse(pivot.matrix)*someTranformationMatrix*pivot.matrix
您在此处找到的工作演示:https://jsfiddle.net/mmalex/hd8ex0ok/
// example for
let renderer;
let camera;
let controls;
let scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(54, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(new THREE.Color(0xfefefe));
document.body.appendChild(renderer.domElement);
camera.position.x = 4;
camera.position.y = 10;
camera.position.z = 4;
camera.lookAt(0, 0, 0);
controls = new THREE.OrbitControls(camera);
// white spotlight shining from the side, casting a shadow
let spotLight = new THREE.SpotLight(0xffffff, 2.5, 25, Math.PI / 6);
spotLight.position.set(9, 10, 1);
scene.add(spotLight);
var light = new THREE.AmbientLight(0x202020); // soft white light
scene.add(light);
// example starts here
let gridHelper = new THREE.GridHelper(4, 4);
scene.add(gridHelper);
var axesHelper = new THREE.AxesHelper(1);
axesHelper.applyMatrix(new THREE.Matrix4().makeTranslation(1.5, 0, -1.5));
axesHelper.updateMatrixWorld(true);
scene.add(axesHelper);
document.changePivot = function() {
axesHelper.position.set(-2 + 4*Math.random(), -2 + 4*Math.random(), -2 + 4*Math.random());
axesHelper.updateMatrixWorld(true);
}
const geometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
const material = new THREE.MeshStandardMaterial({
color: 0xff0000
});
const topBox = new THREE.Mesh(geometry, material);
topBox.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / 8));
topBox.applyMatrix(new THREE.Matrix4().makeTranslation(0.5, 1, -0.5));
scene.add(topBox);
let animate = function() {
requestAnimationFrame(animate);
// get world transforms from desired pivot
var pivot_matrix = axesHelper.matrixWorld.clone();
// inverse it to know how to move pivot to [0,0,0]
let pivot_inv = new THREE.Matrix4().getInverse(pivot_matrix, false);
// place pivot to [0,0,0]
// apply same transforms to object
axesHelper.applyMatrix(pivot_inv);
topBox.applyMatrix(pivot_inv);
// say, we want to rotate 0.1deg around Y axis of pivot
var desiredTransform = new THREE.Matrix4().makeRotationY(Math.PI / 180);
axesHelper.applyMatrix(desiredTransform);
topBox.applyMatrix(desiredTransform);
// and put things back, i.e. apply pivot initial transformation
axesHelper.applyMatrix(pivot_matrix);
topBox.applyMatrix(pivot_matrix);
controls.update();
renderer.render(scene, camera);
};
animate();
body {
margin: 0;
}
<button onclick="changePivot()">set random pivot</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/91/three.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
let renderer;
let camera;
let controls;
let scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(54, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(new THREE.Color(0xfefefe));
document.body.appendChild(renderer.domElement);
camera.position.x = 5;
camera.position.y = 15.5;
camera.position.z = 5.5;
camera.lookAt(0, 0, 0);
controls = new THREE.OrbitControls(camera);
// white spotlight shining from the side, casting a shadow
let spotLight = new THREE.SpotLight(0xffffff, 2.5, 25, Math.PI / 6);
spotLight.position.set(9, 10, 1);
scene.add(spotLight);
var light = new THREE.AmbientLight(0x202020); // soft white light
scene.add(light);
// example starts here
let gridHelper = new THREE.GridHelper(4, 4);
scene.add(gridHelper);
var axesHelper = new THREE.AxesHelper(1);
axesHelper.applyMatrix(new THREE.Matrix4().makeTranslation(1.5, 0, -1.5));
scene.add(axesHelper);
const geometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
const material = new THREE.MeshStandardMaterial({
color: 0xff0000
});
const topBox = new THREE.Mesh(geometry, material);
topBox.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / 8));
topBox.applyMatrix(new THREE.Matrix4().makeTranslation(0.5, 1, -0.5));
scene.add(topBox);
let animate = function() {
requestAnimationFrame(animate);
// get world transforms from desired pivot
axesHelper.updateMatrixWorld(true);
var pivot_matrix = axesHelper.matrixWorld.clone();
// inverse it to know how to move pivot to [0,0,0]
let pivot_inv = new THREE.Matrix4().getInverse(pivot_matrix, false);
// place pivot to [0,0,0]
// apply same transforms to object
axesHelper.applyMatrix(pivot_inv);
topBox.applyMatrix(pivot_inv);
// say, we want to rotate 0.1deg around Y axis of pivot
var desiredTransform = new THREE.Matrix4().makeRotationY(Math.PI / 180);
axesHelper.applyMatrix(desiredTransform);
topBox.applyMatrix(desiredTransform);
// and put things back, i.e. apply pivot initial transformation
axesHelper.applyMatrix(pivot_matrix);
topBox.applyMatrix(pivot_matrix);
controls.update();
renderer.render(scene, camera);
};
animate();