在 Three.js 中,如何才能让人脸在一定的约束条件下尽可能的面向相机?

问题描述 投票:0回答:1

假设我们的面在 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.js
1个回答
0
投票

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>

© www.soinside.com 2019 - 2024. All rights reserved.