假设我们的面在 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 )
//Get the normal vector of the face in world coordinates.
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 );
//Get the view vector of the camera looking at this face.
const cameraViewVector = new THREE.Vector3( camera.position.x, 0, camera.position.z ).normalize();
//The angle between the two vectors above cannot actually be calculated this way, because if the surface is not so special (on the x-y plane), the calculation of this angle depends on three vectors instead of two (here we take the shortcut of setting the y value of the view vector to 0, so it appears simpler).
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 );
当我逆时针移动相机时,此代码工作正常,并且脸部将尽可能紧密地跟随,但当我顺时针移动相机时,此代码不起作用,因为角度计算变得不稳定。我该如何解决这个问题,或者有其他方法来实现我想要的吗?谢谢大家。
THREE.Object3D 已经有了可以做你想要的事情的方法
plane.lookAt(camera.position)
Object3D.lookAt 文档
完整代码示例:
这是旋转到相机和静态 AxesHelper 的平面(X 轴是红色。Y 轴是绿色。Z 轴是蓝色。AxesHelper 文档)
// just wanted to put all js in js section
Promise.all([
//import * as THREE from 'three'
import('three').then(m=>{window.THREE = m}),
//import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import('three/addons/controls/OrbitControls.js').then(m=>{window.OrbitControls = m.OrbitControls}),
]).then(()=>{
const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.01, 10_000 );
camera.position.set(0,0,2)
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
const canvas = document.body.appendChild( renderer.domElement );
const controls = new OrbitControls( camera, renderer.domElement );
const scene = new THREE.Scene();
const plane = new THREE.Mesh(
new THREE.PlaneGeometry( 1, 1)
,new THREE.MeshBasicMaterial( {color: 0xffff00} )
)
const arrowHelper = new THREE.ArrowHelper(new THREE.Vector3(0,0,1),new THREE.Vector3(),1,0xff0000,0.5,0.25)
plane.add(arrowHelper)
scene.add(
plane // will always rotate to camera
,new THREE.AxesHelper( 5 ) // static Axes object
)
function animate() {
requestAnimationFrame( animate );
plane.lookAt(camera.position) // plane rotates to camers
controls.update();
renderer.render( scene, camera );
};
animate();
})
body {
margin: 0;
padding: 0;
}
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/"
}
}
</script>