如何避免带缩放的父对象中子对象旋转后被拉伸?
解决父对象缩放导致子对象旋转拉伸的问题
当父对象存在非均匀缩放时,直接给子对象设置反向缩放后旋转仍会出现拉伸,这是因为Three.js的变换顺序为父缩放→父旋转→父平移→子缩放→子旋转→子平移,子对象的旋转会在父缩放扭曲后的空间中执行,最终导致拉伸变形。要让子对象表现得如同父对象缩放为(1,1,1),可以通过手动控制子对象的矩阵变换来实现:
具体实现步骤
- 禁用子对象的自动矩阵更新,避免Three.js自动覆盖我们手动设置的矩阵。
- 计算父对象缩放的逆向量,用于抵消父对象的缩放影响。
- 手动构建子对象的变换矩阵:先应用旋转操作,再应用逆父缩放操作(确保旋转在未被缩放的空间中执行)。
- 将构建好的矩阵赋值给子对象的
matrix属性。
修改后的代码
import * as THREE from 'three'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; // Renderer const renderer = new THREE.WebGLRenderer({ alpha: false, antialias: true, stencil: false, depth: false }); document.body.appendChild(renderer.domElement); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(); camera.position.z = 10; new OrbitControls(camera, renderer.domElement) // Parent const parentGeometry = new THREE.BoxGeometry(1, 1, 1); const parentMaterial = new THREE.MeshBasicMaterial({ color: "tomato" }); const parent = new THREE.Mesh(parentGeometry, parentMaterial); parent.scale.set(3, 0.5, 3) scene.add(parent); // Child const childGeometry = new THREE.BoxGeometry(1, 1, 1); const childMaterial = new THREE.MeshBasicMaterial({ color: "lightblue" }); const child = new THREE.Mesh(childGeometry, childMaterial); parent.add(child); // 关键修改部分 child.matrixAutoUpdate = false; // 禁用自动矩阵更新 const invParentScale = new THREE.Vector3(1/parent.scale.x, 1/parent.scale.y, 1/parent.scale.z); const childMatrix = new THREE.Matrix4(); childMatrix.makeRotationX(Math.PI / 4); // 先应用旋转 childMatrix.scale(invParentScale); // 再应用逆父缩放,抵消父的缩放影响 child.matrix.copy(childMatrix); // 赋值给子对象矩阵 function resize() { const width = document.documentElement.clientWidth; const height = window.innerHeight; camera.aspect = width / height; camera.fov = 55 camera.updateProjectionMatrix(); renderer.setPixelRatio(Math.min(2, devicePixelRatio)); renderer.setSize(width, height); } window.addEventListener("resize", resize); resize(); function tick(t) { requestAnimationFrame(tick); renderer.render(scene,camera); } requestAnimationFrame(tick);
原理说明
通过手动调整变换顺序,让旋转操作先于逆父缩放执行,这样旋转是在未被父对象缩放扭曲的原始空间中完成的,之后再用逆父缩放抵消父对象的缩放影响,最终子对象的视觉效果就如同父对象缩放为(1,1,1)时的状态,且仍保持与父对象的层级关系。
内容的提问来源于stack exchange,提问作者imfbnt
相关产品推荐
相关产品推荐

