如何在Three.js的3D地球上使用经纬度放置标记?

6
我这里有一个基本的Three.js 3D地球。我使用Navigator获取用户位置,并在地球上标记用户当前位置。但是我无论如何都不能显示正确的位置。此外,3D地球会旋转。我该如何获得正确的位置并使标记简单地随着地球旋转?
示例:http://corexsystems.net/Projects/ThreeJs/EarthAndMoon/ 代码:
    var renderer    = new THREE.WebGLRenderer({
        antialias   : true
    });
    renderer.setSize( window.innerWidth, window.innerHeight );
    document.body.appendChild( renderer.domElement );
    renderer.shadowMapEnabled   = true

    var onRenderFcts= [];
    var scene   = new THREE.Scene();
    var camera  = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 100 );
    camera.position.z = 1;

    var light   = new THREE.AmbientLight( 0x222222 )
    scene.add( light )

    var light   = new THREE.DirectionalLight( 0xffffff, 1 )
    light.position.set(5,5,5)
    scene.add( light )
    light.castShadow    = true
    light.shadowCameraNear  = 0.01
    light.shadowCameraFar   = 15
    light.shadowCameraFov   = 45

    light.shadowCameraLeft  = -1
    light.shadowCameraRight =  1
    light.shadowCameraTop   =  1
    light.shadowCameraBottom= -1
    // light.shadowCameraVisible    = true

    light.shadowBias    = 0.001
    light.shadowDarkness    = 0.2

    light.shadowMapWidth    = 1024
    light.shadowMapHeight   = 1024

    //////////////////////////////////////////////////////////////////////////////////
    //      added starfield                         //
    //////////////////////////////////////////////////////////////////////////////////

    var starSphere  = THREEx.Planets.createStarfield()
    scene.add(starSphere)

    //////////////////////////////////////////////////////////////////////////////////
    //      add an object and make it move                  //
    //////////////////////////////////////////////////////////////////////////////////

    // var datGUI   = new dat.GUI()

    var containerEarth  = new THREE.Object3D()
    containerEarth.rotateZ(-23.4 * Math.PI/180)
    containerEarth.position.z   = 0
    scene.add(containerEarth)
    var moonMesh    = THREEx.Planets.createMoon()
    moonMesh.position.set(0.5,0.5,0.5)
    moonMesh.scale.multiplyScalar(1/5)
    moonMesh.receiveShadow  = true
    moonMesh.castShadow = true
    containerEarth.add(moonMesh)

    var earthMesh   = THREEx.Planets.createEarth()
    earthMesh.receiveShadow = true
    earthMesh.castShadow    = true
    containerEarth.add(earthMesh)
    onRenderFcts.push(function(delta, now){
        earthMesh.rotation.y += 1/32 * delta;       
    })

    var geometry    = new THREE.SphereGeometry(50, 32, 32)
    var material    = THREEx.createAtmosphereMaterial()
    material.uniforms.glowColor.value.set(0x00b3ff)
    material.uniforms.coeficient.value  = 0.8
    material.uniforms.power.value       = 2.0
    var mesh    = new THREE.Mesh(geometry, material );
    mesh.scale.multiplyScalar(1.01);
    containerEarth.add( mesh );
    // new THREEx.addAtmosphereMaterial2DatGui(material, datGUI)

    var geometry    = new THREE.SphereGeometry(0.5, 32, 32)
    var material    = THREEx.createAtmosphereMaterial()
    material.side   = THREE.BackSide
    material.uniforms.glowColor.value.set(0x00b3ff)
    material.uniforms.coeficient.value  = 0.5
    material.uniforms.power.value       = 4.0
    var mesh    = new THREE.Mesh(geometry, material );
    mesh.scale.multiplyScalar(1.15);
    containerEarth.add( mesh );
    // new THREEx.addAtmosphereMaterial2DatGui(material, datGUI)

    var earthCloud  = THREEx.Planets.createEarthCloud()
    earthCloud.receiveShadow    = true
    earthCloud.castShadow   = true
    containerEarth.add(earthCloud)
    onRenderFcts.push(function(delta, now){
        earthCloud.rotation.y += 1/8 * delta;       
    })

    //////////////////////////////////////////////////////////////////////////////////
    //      Camera Controls                         //
    //////////////////////////////////////////////////////////////////////////////////
    var mouse   = {x : 0, y : 0}
    document.addEventListener('mousemove', function(event){
        mouse.x = (event.clientX / window.innerWidth ) - 0.5
        mouse.y = (event.clientY / window.innerHeight) - 0.5
    }, false)
    onRenderFcts.push(function(delta, now){
        camera.position.x += (mouse.x*5 - camera.position.x) * (delta*3)
        camera.position.y += (mouse.y*5 - camera.position.y) * (delta*3)
        camera.lookAt( scene.position )
    })





       var marker2 = new THREE.Object3D();
                var loader = new THREE.OBJLoader( );
                loader.load( 'Pin.obj', function ( object2 ) {                 
                    object2.traverse( function ( child ) {
                        if ( child instanceof THREE.Mesh ) {
                            child.material = new THREE.MeshPhongMaterial( { 
                                color: 0x790811, 
                                specular: 0x050505,
                                shininess: 100
                            } );
                        }
                    } );

                    object2.position.set(0.57,-0.15 ,-0.07); // rotating obj should set (X > 0, 0, 0)
                        object2.receiveShadow   = true
    object2.castShadow  = true
    object2.quaternion.setFromUnitVectors(
                    new THREE.Vector3(8, 1, 3.5), new THREE.Vector3(1, 0, 0));
                    object2.scale.set(0.0003,0.0003,0.0003);


                containerEarth.add( object2 );

                });



    scene.add(marker2);


    var rad = Math.PI / 180;

    marker2.quaternion.setFromEuler(
        new THREE.Euler(0, 105 * rad, 45 * rad, "YZX")); 

  navigator.geolocation.watchPosition(function (pos) {


     var lat2 = 42.3125, lon2 = -86.1131;
        marker2.quaternion.setFromEuler(
            new THREE.Euler(45, lon2 * rad, lat2 * rad, "YZX")); 
        });

    //////////////////////////////////////////////////////////////////////////////////
    //      render the scene                        //
    //////////////////////////////////////////////////////////////////////////////////
    onRenderFcts.push(function(){
        renderer.render( scene, camera );       
    })

    //////////////////////////////////////////////////////////////////////////////////
    //      loop runner                         //
    //////////////////////////////////////////////////////////////////////////////////
    var lastTimeMsec= null
    requestAnimationFrame(function animate(nowMsec){
        // keep looping
        requestAnimationFrame( animate );
        // measure time
        lastTimeMsec    = lastTimeMsec || nowMsec-1000/60
        var deltaMsec   = Math.min(200, nowMsec - lastTimeMsec)
        lastTimeMsec    = nowMsec
        // call each update function
        onRenderFcts.forEach(function(onRenderFct){
            onRenderFct(deltaMsec/1000, nowMsec/1000)
        })
    })

这里有一个可工作的GIST:https://gist.github.com/bellbind/47f36f39f632d9ab77a3。下载zip文件并在测试服务器上运行,以查看其工作情况。 - Radio
这是我正在使用的示例。我不知道为什么它无法工作,标记很接近但位置不对:( - user8320550
你根据你在这里留下的脚本所使用的代码位于http://corexsystems.net/Projects/ThreeJs/EarthAndMoon/。你确定这个要点不是有帮助的吗? - Radio
这非常有帮助,CoreXSystems是我的网站。我基于那个GIT做了一个例子。但是在这个例子中,我似乎无法正确地放置标记。一些变量是不同的,我不确定哪些是不同的。 - user8320550
这里肯定有错误:object2.quaternion.setFromUnitVectors(new THREE.Vector3(8, 1, 3.5), new THREE.Vector3(1, 0, 0));:该函数期望两个参数都是归一化向量(长度 === 1),因此您需要对它们进行归一化(例如 new THREE.Vector3(8, 1, 3.5).normalize())。不确定它与您的问题有多大关联。 - Martin Schuhfuß
1个回答

9

您需要将球坐标转换为笛卡尔坐标

如果您没有修改球的纹理偏移,下面的代码应该可以解决问题:

/**
 * Position an object on a planet.
 * @param {THREE.Object3D} object - the object to place
 * @param {number} lat - latitude of the location
 * @param {number} lon - longitude of the location
 * @param {number} radius - radius of the planet
 */
function placeObjectOnPlanet(object, lat, lon, radius) {
    var latRad = lat * (Math.PI / 180);
    var lonRad = -lon * (Math.PI / 180);
    object.position.set(
        Math.cos(latRad) * Math.cos(lonRad) * radius,
        Math.sin(latRad) * radius,
        Math.cos(latRad) * Math.sin(lonRad) * radius
    );
    object.rotation.set(0.0, -lonRad, latRad - Math.PI * 0.5);
}

请注意,您可以使用THREE.Spherical实现相同的操作。

请查看此演示代码

如果您希望您的图钉随地球旋转而旋转,则需要将它们作为地球的子级添加。或者将地球和图钉同时添加到同一个 THREE.Object3D中:

var object = THREE.Object3D();
object.add(planet);
object.add(pins);
object.rotation.y = 1.0;

绝对是天才,你百分之百正确,我把物体放在了三维空间而不是球体本身! - user8320550
2
我认为您的JSFiddle函数中纬度-经度数值顺序有些混淆了,这里是已经修改过的JSFiddle链接:https://jsfiddle.net/k0Lz9hek/2/ - Haris
我的错!我交换了 latlon。示例和代码已经更新,感谢您指出。 - neeh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接