【问题标题】:How to get objects to rotate on a vertical axis and around another object three.js如何让对象在垂直轴上旋转并围绕另一个对象three.js
【发布时间】:2020-02-14 19:18:46
【问题描述】:

您可能注意到我问了很多与我的项目相关的问题,因此提前感谢您的所有支持。

我的项目包括围绕太阳旋转的行星地球和月球。 (它们并不是专门围绕太阳旋转,而是围绕太阳恰好所在的 0,0 轴旋转)。

我最初创建了我的几何图形,然后将它们添加到场景中,然后按如下方式添加到轨道组中

var orbitGroup = new THREE.Object3D();
                scene.add(orbitGroup); 

            scene.add(planetEarth);
            orbitGroup.add(planetEarth);
            scene.add(planetMoon);
            orbitGroup.add(planetMoon);

然后我在渲染函数中声明旋转如下

                   planetEarth.add( planetMoon );
                planetEarth.add(rocketGroup);

            // call the render function
            var angle = 0;
            render();

            function render() {
                stats.update();

                 // rotate the orbit group
                angle += 0.002;                    
                angle += 0.002 * controls.EarthRotationSpeed;
                planetEarth.rotation.y += controls.EarthRotationSpeed;                    
                planetMoon.rotation.y += controls.MoonRotationSpeed;                    
                angle+= 0.01 * controls.SunRotationSpeed;
                planetSun.rotation.y += controls.SunRotationSpeed;        

                 // rotate the orbit group
                angle += 0.02;
                orbitGroup.rotation.y = -angle / 10;


                littleOrbitGroup.rotation.x = -angle;


                // render using requestAnimationFrame
                requestAnimationFrame(render);
                renderer.render(scene, camera);
            }

如您所知,月球和地球都围绕太阳运行,而不是月球在旋转地球的同时旋转太阳。有没有办法我可以声明它应该绕行的特定点或物体,并让它在我想要的任何轴上而不是在 y 轴上绕行?

----------------编辑-----------------

function createMesh(geom) {
                    var loader = new THREE.TextureLoader();
                    var planetTexture = loader.load("../assets/textures/planets/Earth.png");
                    var normalTexture = loader.load("../assets/textures/planets/EarthNormal.png");
                    var planetMaterial = new THREE.MeshPhongMaterial({map: planetTexture, bumpMap: normalTexture});
                    // create a multimaterial
                    var mesh = THREE.SceneUtils.createMultiMaterialObject(geom, [planetMaterial]);
                    return mesh;
                }

【问题讨论】:

    标签: javascript three.js


    【解决方案1】:

    您可以通过将THREE.Object3D 嵌套在另一个中轻松地做到这一点,通过旋转半径移动它们并围绕中心旋转。

    看这个简单的例子,这里使用了轨道控制 - 使用鼠标滚轮缩小并拖动旋转:

    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
    <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
    
    <script>
        THREE.TOUCH = {}
        let base64prefix = 'data:image/png;base64,'
        let blue = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkuPn/PwAFkgLZdUNuXwAAAABJRU5ErkJggg=='
        let blue2= 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkqPz/HwAEcgJ5UUczUQAAAABJRU5ErkJggg=='
        let orange = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8H8PwHwAGGQJcoBRfLQAAAABJRU5ErkJggg=='
        
    let solarsystem = [{
            texture: blue,
            name: 'earth', radius: 2, orbit: 30, speed: 2,
            satellites: [{
                texture: blue2, rotation: [1, 1, 1],
                name: 'rock', radius: 0.5, orbit: 4, speed: 5,
            },{
                texture: blue2,
                name: 'moon', radius: 1, orbit: 6, speed: 1,
            }]
        }, {
            texture: orange,
            name: 'mars', radius: 2, orbit: 50, speed: 1,
            satellites: [{
                texture: blue2,
                name: 'phobos', radius: 0.5, orbit: 3, speed: 1,
            }, {
                texture: blue2,
                name: 'deimos', radius: 0.5, orbit: 4, speed: 3,
            }]
        }];
       
    var scene = new THREE.Scene();
    var aspect = window.innerWidth / window.innerHeight;
    var camera = new THREE.PerspectiveCamera(75, aspect, 0.1, 1000);
    var renderer = new THREE.WebGLRenderer();
    renderer.setSize( window.innerWidth, window.innerHeight );
    document.body.appendChild( renderer.domElement );
    
    
    // planets
    solarsystem.forEach(d => create(d, scene));
    
    // sun
    let sun = sphere({radius:3, orbit:0, texture: orange});
    scene.add(sun)
    
    function create(d, target) {
       var o = new THREE.Object3D(d.name);
       d.rotation && o.rotateX(d.rotation[0]);
       d.rotation && o.rotateY(d.rotation[1]);
       d.rotation && o.rotateZ(d.rotation[2]);
       o.add(orbit(d));
       let p = sphere(d)
       o.add(p);
       d.satellites && d.satellites.forEach(d1 => create(d1, p))
       target.add(o);
       d.o=o; 
    }
    
    function orbit(d) {
        var o = new THREE.Object3D('orbit '+d.name);
        o.rotateX(Math.PI/2);
        o.add( new THREE.Line( 
            new THREE.CircleGeometry( d.orbit, 64 ), 
            new THREE.LineBasicMaterial( { color: 0xffffff } ) ));
        return o;
    }
    
    function sphere(d){
        var o = new THREE.Object3D('sphere '+d.name);
        o.translateX(d.orbit);
        var loader = new THREE.TextureLoader();
        loader.load( base64prefix + d.texture, function ( texture ) {
            var geometry = new THREE.SphereGeometry( d.radius, 20, 20 );
            var material = new THREE.MeshBasicMaterial( { 
                map: texture, overdraw: 0.5 
            } );
            o.add( new THREE.Mesh( geometry, material ) );
        } );    
        return o;
    }
    
    var grid = new THREE.GridHelper(500, 100, 0x666666, 0x444444)
    grid.rotateY(Math.PI/2);
    scene.add(grid);
    camera.position.set(25,25,25);
    
    new THREE.OrbitControls( camera, renderer.domElement );
    
    let t = 0
    function render(dt) {
        
        let t2 = dt - t;
        requestAnimationFrame( render );
        renderer.render( scene, camera );
        solarsystem.forEach(upd);
        sun.rotateY(t2/1000);
        t = dt;
        
        function upd(d) {
            d.o.rotateY(t2/10000*d.speed);
            d.satellites && d.satellites.forEach(upd)
        }
    }
    
    requestAnimationFrame( render );
    
    </script>
    
    <style>
    
      body, canvas { 
        margin: 0;  
        width: 100%;
        height: 100%;
        overflow: hidden;
        background-color: black;
      }
      
    </style>

    PS:对不起我的英语

    【讨论】:

    • 我编辑了问题以添加我如何制作行星模型的代码,我可以将它应用到您的方法中吗?
    • @zadders 如果您有关于添加纹理球体而不是蓝色线框的问题 - 所以是的
    • 我的当前功能如下:imgur.com/a/LEgcIIi 我将如何将其应用于我的?你的英语也很完美别担心?
    • @StrangerintheQ 这个代码示例很棒,真的帮助我更好地理解three.js以及嵌套旋转的工作原理。请注意,new THREE.Object3d( xyz.name) 是失败的。 Object3d 构造函数不带参数。您必须在代码中多次添加o.name = d.name。这种命名使事情更容易理解。
    • 不用担心。详细阅读该代码确实帮助我了解了threejs。我会说我不完全理解当儿童卫星使用d.o.rotateY() 进行递归调用时会发生什么@ 987654329@。在这个例子中,我正在努力理解是什么生成了欧拉和四元数矩阵。再次感谢您。