Three.js:如何让平面在Y轴约束下尽可能朝向相机?
解决平面绕Y轴旋转朝向相机的角度计算问题
问题背景
初始平面位于X-Y平面,约束仅能绕(0,1,0)轴旋转,需要实现相机移动时平面尽可能朝向相机(类似带轴约束的精灵)。现有代码在相机逆时针移动时正常,顺时针移动时角度计算异常,无法正常跟随。
初始化代码
/////init let planeGeometry; let planeMaterial; let planeMesh; let rotationAngle = 0; planeGeometry = new THREE.PlaneGeometry( 100, 100 ); planeMaterial = new THREE.MeshBasicMaterial( { color: 0xffff00, side: THREE.DoubleSide } ); planeMesh = new THREE.Mesh( planeGeometry, planeMaterial );
逻辑处理代码
/////handle console.log( camera.position ) // 获取平面在世界坐标系中的法向量 const localNormals = planegeometry.attributes.normal.array; const localNormal = new THREE.Vector3(); localNormal.fromArray( localNormals, 0 ); planemesh.updateMatrixWorld( true ); const worldNormal = localNormal.clone().applyMatrix4( planemesh.matrixWorld ).normalize(); console.log( worldNormal ); // 获取相机看向该平面的视线向量 const cameraViewVector = new THREE.Vector3( camera.position.x, 0, camera.position.z ).normalize(); // 实际上不能这样计算上述两个向量的夹角,因为若平面不是特殊的X-Y平面,夹角计算需依赖三个向量(此处通过将视线向量Y值设为0简化) const angle = cameraViewVector.angleTo( worldNormal ); const rotationAxis = new THREE.Vector3( 0, 1, 0 ).normalize(); rotationAngle += angle; rotationAngle = rotationAngle % ( Math.PI * 2 ); console.log( angle ) console.log( rotationAngle ) const quaternion = new THREE.Quaternion().setFromAxisAngle( rotationAxis, rotationAngle ); planeMesh.setRotationFromQuaternion( quaternion );
问题根源与解决方法
1. 问题所在
angleTo()方法返回的是两个向量之间的最小夹角(范围0到π),无法区分顺时针和逆时针方向,导致顺时针移动时累加的角度方向错误,平面旋转异常。另外原代码存在变量名大小写错误(planegeometry应为planeGeometry,planemesh应为planeMesh),需先修正。
2. 正确实现思路
方法一:直接计算目标旋转角度(推荐)
因为约束绕Y轴旋转,将相机位置投影到X-Z平面,计算该点相对于原点的方位角,直接作为平面的Y轴旋转角度,平面正面会始终朝向相机在X-Z平面的投影方向:
/////handle // 将相机位置投影到X-Z平面,计算方位角(atan2(x,z)对应绕Y轴的旋转角度) const targetAngle = Math.atan2(camera.position.x, camera.position.z); // 设置平面绕Y轴的旋转角度 planeMesh.rotation.y = targetAngle;
这种方法完全避免了角度累加的方向问题,代码简洁高效。
方法二:修正角度计算的方向
如果需要保留向量计算逻辑,通过叉乘判断旋转方向,给角度添加正确的正负符号:
/////handle console.log( camera.position ) // 修正变量名大小写 const localNormals = planeGeometry.attributes.normal.array; const localNormal = new THREE.Vector3(); localNormal.fromArray( localNormals, 0 ); planeMesh.updateMatrixWorld( true ); const worldNormal = localNormal.clone().applyMatrix4( planeMesh.matrixWorld ).normalize(); // 获取相机看向平面的视线向量(投影到X-Z平面) const cameraViewVector = new THREE.Vector3( camera.position.x, 0, camera.position.z ).normalize(); // 计算叉乘判断旋转方向:叉乘结果的Y分量正负决定顺时针/逆时针 const cross = new THREE.Vector3(); cross.crossVectors(worldNormal, cameraViewVector); const angleSign = cross.y >= 0 ? 1 : -1; // 计算带方向的角度 const angle = cameraViewVector.angleTo(worldNormal) * angleSign; const rotationAxis = new THREE.Vector3(0, 1, 0).normalize(); rotationAngle += angle; // 保持角度在0~2π范围内 rotationAngle = (rotationAngle + Math.PI * 2) % (Math.PI * 2); const quaternion = new THREE.Quaternion().setFromAxisAngle(rotationAxis, rotationAngle); planeMesh.setRotationFromQuaternion(quaternion);
3. 额外优化
- 平面默认朝向Z轴正方向,若需调整初始朝向,只需在初始化时设置
planeMesh.rotation.y的初始值。 - 若相机Y轴方向移动幅度大,可计算相机到平面中心的向量,再投影到垂直于Y轴的平面上,用该投影向量计算目标角度,确保朝向更精准。
内容的提问来源于stack exchange,提问作者cossoledad
相关产品推荐
相关产品推荐

