three.js中让物体相对于摄像机静止的几种方法

有这么一个需求,屏幕上有两个魔方 A 和 B,然后用THREE.TrackballControls 来控制场景视角和方位,魔方 A 处于屏幕中央,较大,魔方B处于屏幕右上角,较小,当用户旋转视角时,魔方 A 会相对于摄像机旋转,而魔方 B 不会做相对于摄像机的旋转,也就是说,我们可以通过改变视角,看到A的所有六个面,但是始终看不到 B 背对着相机的三个面

B 始终相对于摄像机静止,当我们拉近镜头时,A变大,B的大小不变,当镜头移动时,A 会相对于镜头做反方向移动,而B静止不动。

实现这个需求有好几种方法,一种思路是,把add 到 camera 上 而不是 scene上,
https://stackoverflow.com/questions/31831425/static-object-in-scene-three-js

scene.add( camera ); // required when the camera has a child
camera.add( object );
object.position.set( 0, 0, - 100 ); // or whatever distance you want

另一种思路,在渲染循环中,获取camera的position和rotation,然后调整“静态”物体的位置和旋转方向
https://github.com/mrdoob/three.js/issues/641

object.position.copy( camera.position );
object.rotation.copy( camera.rotation );
object.updateMatrix();
object.translateZ( - 10 );

另一种思路,使用THREE.Vector3.project() 和 THREE.Vector3.unproject() , 原理就是把三维坐标投射的二维屏幕的坐标,然后记录下这个屏幕坐标,每当相机的方位改变时,使用unproject()方法,把屏幕坐标转换成新的相机空间的三维坐标

let geom = new THREE.Geometry();
        let p1 = new THREE.Vector3(-145, 95, 20), p2 = new THREE.Vector3(-115, 95, 20), p3 = new THREE.Vector3(-125, 55, 10);
        geom.vertices = [p1, p2, p3];
        geom.faces = [new THREE.Face3(0, 1, 2)];
        geom.computeFaceNormals();
        var customMesh = new THREE.Mesh(geom, new THREE.MeshLambertMaterial({color: 0x00cc00, side: THREE.DoubleSide}));
        scene.add(customMesh);

var pp1, pp2, pp3;
//below code should run in render loop, but better run only after camera's position or rotation change
if (!pp1) {
                pp1 = p1.clone().project(camera);
                pp2 = p2.clone().project(camera);
                pp3 = p3.clone().project(camera);
            } else {
                let q1 = pp1.clone().unproject(camera);
                let q2 = pp2.clone().unproject(camera);
                let q3 = pp3.clone().unproject(camera);
                updateVertices([q1, q2, q3], customMesh.geometry.vertices);
                customMesh.geometry.verticesNeedUpdate = true;
                customMesh.geometry.computeFaceNormals();
            }

又另一思路,修改TrackballControls.js ,修改其中的rotateCamera 方法

this.qua = new THREE.Quaternion();
	this.rotateCamera = (function(){ }
return function () {
			var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() );
			...
			...
			...
                this.qua = quaternion;

			}
		}

然后监听TrackballControls的事件

var planeGeometry = new THREE.PlaneGeometry(30, 30);
        var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff, side: THREE.DoubleSide});
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.receiveShadow = true;
        
trackballControls.addEventListener("change", function ( ) {
            let ab = projected.clone().unproject(camera);
            plane.position.copy(ab);
            let m = new THREE.Matrix4();
            m.makeRotationFromQuaternion(trackballControls.qua);
            plane.applyMatrix(m);
            // plane.setRotationFromQuaternion(trackballControls.qua);
        } );

完整代码, InvestigateStaticMesh.html

<!DOCTYPE html>

<html>

<head>
    <title>Investigate mesh be static in camera </title>
    <script type="text/javascript" src="../libs/three.js"></script>

    <script type="text/javascript" src="../libs/stats.js"></script>
    <script type="text/javascript" src="../libs/dat.gui.js"></script>
    <script type="text/javascript" src="../libs/TrackballControls.js"></script>

    <style>
        body {
            /* set margin to 0 and overflow to hidden, to go fullscreen */
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output">
</div>
<!-- Div which will hold the Output -->
<div id="WebGL-output">
</div>

<!-- Javascript code that runs our Three.js examples -->
<script type="text/javascript">

    // once everything is loaded, we run our Three.js stuff.
    function init() {


        var clock = new THREE.Clock();

        var stats = initStats();

        // create a scene, that will hold all our elements such as objects, cameras and lights.
        var scene = new THREE.Scene();

        // create a camera, which defines where we're looking at.
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);


        // create a render and set the size
        var webGLRenderer = new THREE.WebGLRenderer();
        webGLRenderer.setClearColor(new THREE.Color(0x000, 1.0));
        webGLRenderer.setSize(window.innerWidth, window.innerHeight);
        webGLRenderer.shadowMapEnabled = true;

        // position and point the camera to the center of the scene
        camera.position.x = 100;
        camera.position.y = 100;
        camera.position.z = 300;
        camera.lookAt(new THREE.Vector3(0, 0, 0));


        var trackballControls = new THREE.TrackballControls(camera);

        trackballControls.rotateSpeed = 1.0;
        trackballControls.zoomSpeed = 1.0;
        trackballControls.panSpeed = 1.0;
//        trackballControls.noZoom=false;
//        trackballControls.noPan=false;
        trackballControls.staticMoving = true;
//        trackballControls.dynamicDampingFactor=0.3;

        // trackballControls.addEventListener( 'end', function ( event ) {
        //     alert( event.type );
        // } );

        // trackballControls.addEventListener("change", function ( ) {
        //     let ab = projected.clone().unproject(camera);
        //     plane.position.copy(ab);
        //     let m = new THREE.Matrix4();
        //     m.makeRotationFromQuaternion(trackballControls.qua);
        //     plane.applyMatrix(m);
        //     // plane.setRotationFromQuaternion(trackballControls.qua);
        // } );

        var ambientLight = new THREE.AmbientLight(0x383838);
        scene.add(ambientLight);

        // add spotlight for the shadows
        var spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(300, 300, 300);
        spotLight.intensity = 1;
        scene.add(spotLight);

        // add the output of the renderer to the html element
        document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);

        // call the render function

        let geom = new THREE.Geometry();
        let p1 = new THREE.Vector3(-145, 95, 20), p2 = new THREE.Vector3(-115, 95, 20), p3 = new THREE.Vector3(-125, 55, 10);
        geom.vertices = [p1, p2, p3];
        geom.faces = [new THREE.Face3(0, 1, 2)];
        geom.computeFaceNormals();
        var customMesh = new THREE.Mesh(geom, new THREE.MeshLambertMaterial({color: 0x00cc00, side: THREE.DoubleSide}));
        scene.add(customMesh);

        var planeGeometry = new THREE.PlaneGeometry(30, 30);
        var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff, side: THREE.DoubleSide});
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.receiveShadow = true;

        // rotate and position the plane
        plane.rotation.x = -0.1 * Math.PI;
        var vector = new THREE.Vector3(-135, 85, 0);
        plane.position.copy(vector);
        // plane.position.x = vector.x;
        // plane.position.y = vector.y;
        // plane.position.z = vector.z;

        // add the plane to the scene
        scene.add(plane);

        render();

        var projected = null, matrix = new THREE.Matrix4();
        matrix.extractRotation(plane.matrixWorld);
        matrix.multiply(camera.projectionMatrix);
        var pp1, pp2, pp3;
        var k = 1;

        function updateVertices(src, dst) {
            for (var i = 0; i < src.length; i++) {
                dst[i].set(src[i].x, src[i].y, src[i].z);
            }
        }
        function render() {
            stats.update();
            var delta = clock.getDelta();

            trackballControls.update(delta);

            //https://github.com/mrdoob/three.js/issues/641
            plane.position.copy( camera.position );
            plane.rotation.copy( camera.rotation );
            plane.updateMatrix();
            plane.translateZ( -270 );

            //webGLRenderer.clear();
            // render using requestAnimationFrame
            requestAnimationFrame(render);
            webGLRenderer.render(scene, camera);
            // if (!pp1) {
            //     pp1 = p1.clone().project(camera);
            //     pp2 = p2.clone().project(camera);
            //     pp3 = p3.clone().project(camera);
            // } else {
            //     let q1 = pp1.clone().unproject(camera);
            //     let q2 = pp2.clone().unproject(camera);
            //     let q3 = pp3.clone().unproject(camera);
            //     updateVertices([q1, q2, q3], customMesh.geometry.vertices);
            //     customMesh.geometry.verticesNeedUpdate = true;
            //     customMesh.geometry.computeFaceNormals();
            // }
            if (!projected) {
                projected = vector.clone().project(camera);
                let k = 1;
            } else {
                // let m = new THREE.Matrix4();
                // let mm = matrix.clone().multiply(m.getInverse( camera.projectionMatrix ));
                // plane.setRotationFromMatrix(mm);
            }
        }

        function initStats() {

            var stats = new Stats();
            stats.setMode(0); // 0: fps, 1: ms

            // Align top-left
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';

            document.getElementById("Stats-output").appendChild(stats.domElement);

            return stats;
        }
    }
    window.onload = init;
</script>
</body>
</html>

https://github.com/mrdoob/three.js/tree/master/examples/js/controls

https://software.intel.com/en-us/articles/simulating-cloth-for-3d-games

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值