使用 mapbox 更新 3d 模型位置

Updating 3d Model location location with mapbox

我将一个 3D 模型放置在地图中,使用 mapbox 和 javascript(使用 this tutorial),我试图在单击按钮时更改对象的位置。但是,物体没有移动,我真的不知道如何修复它。

这是我编写的用于更改位置的函数(当我点击按钮时我可以看到所有这些控制台日志,所以我知道不起作用的部分是实际位置更新本身):

function updateObjectLocation(id,newTransform) {
    let objectLayer = map.getLayer(id);
    if (typeof objectLayer === 'undefined') {
        console.log('layer ' + id + ' does not exist');
    } else {
        console.log(objectLayer);
        console.log(newTransform);
        objectLayer.render = function(gl, matrix) {
            var rotationX = new THREE.Matrix4().makeRotationAxis(
                new THREE.Vector3(1,0,0),
                newTransform.rotateX
            );
            var rotationY = new THREE.Matrix4().makeRotationAxis(
                new THREE.Vector3(0,1,0),
                newTransform.rotateY
            );
            var rotationZ = new THREE.Matrix4().makeRotationAxis(
                new THREE.Vector3(0,0,1),
                newTransform.rotateZ
            );

            var m = new THREE.Matrix4().fromArray(matrix);
            var l = new THREE.Matrix4()
                .makeTranslation(
                    newTransform.translateX,
                    newTransform.translateY,
                    newTransform.translateZ
                )
                .scale(
                    new THREE.Vector3(
                        newTransform.scale,
                        - newTransform.scale,
                        newTransform.scale
                    )
                )
                .multiply(rotationX)
                .multiply(rotationY)
                .multiply(rotationZ);

            this.camera.projectionMatrix = m.multiply(l);
            this.renderer.state.reset();
            this.renderer.render(this.scene, this.camera);
            this.map.triggerRepaint();
        };
    }
}

欢迎使用 Whosebug。解决方法就简单多了,只需要修改transformation,不需要重写render方法。 这里有一个 fiddle 和解决方案 how to move a 3D model

这里是完整代码

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Add a 3D model</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://api.mapbox.com/mapbox-gl-js/v1.11.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.11.1/mapbox-gl.css" rel="stylesheet" />
<style>
    body {
        margin: 0;
        padding: 0;
    }

    #map {
        position: absolute;
        top: 0;
        bottom: 0;
        width: 100%;
    }
    #move {
        display: block;
        position: relative;
        margin: 0px auto;
        width: 50%;
        height: 40px;
        padding: 10px;
        border: none;
        border-radius: 3px;
        font-size: 12px;
        text-align: center;
        color: #fff;
        background: #ee8a65;
    }
</style>
</head>
<body>
<script src="https://unpkg.com/three@0.106.2/build/three.min.js"></script>
<script src="https://unpkg.com/three@0.106.2/examples/js/loaders/GLTFLoader.js"> 
</script>
<div id="map"></div>

<button id="move">Move</button>
<script>
    mapboxgl.accessToken = 'pk.eyJ1IjoianNjYXN0cm8iLCJhIjoiY2s2YzB6Z25kMDVhejNrbXNpcmtjNGtpbiJ9.28ynPf1Y5Q8EyB_moOHylw';
    var map = (window.map = new mapboxgl.Map({
        container: 'map',
        style: 'mapbox://styles/mapbox/light-v10',
        zoom: 18,
        center: [148.9819, -35.3981],
        pitch: 60,
        antialias: true // create the gl context with MSAA antialiasing, so custom layers are antialiased
    }));

    // parameters to ensure the model is georeferenced correctly on the map
    var modelOrigin = [148.9819, -35.39847];
    var modelOrigin2 = [148.9816, -35.39851];
    var modelAltitude = 0;
    var modelRotate = [Math.PI / 2, 0, 0];

    var modelAsMercatorCoordinate = mapboxgl.MercatorCoordinate.fromLngLat(
        modelOrigin,
        modelAltitude
    );

    // transformation parameters to position, rotate and scale the 3D model onto the map
    var modelTransform = {
        translateX: modelAsMercatorCoordinate.x,
        translateY: modelAsMercatorCoordinate.y,
        translateZ: modelAsMercatorCoordinate.z,
        rotateX: modelRotate[0],
        rotateY: modelRotate[1],
        rotateZ: modelRotate[2],
        /* Since our 3D model is in real world meters, a scale transform needs to be
        * applied since the CustomLayerInterface expects units in MercatorCoordinates.
        */
        scale: modelAsMercatorCoordinate.meterInMercatorCoordinateUnits()
    };

    var THREE = window.THREE;

    // configuration of the custom layer for a 3D model per the CustomLayerInterface
    var customLayer = {
        id: '3d-model',
        type: 'custom',
        renderingMode: '3d',
        onAdd: function (map, gl) {
            this.camera = new THREE.Camera();
            this.scene = new THREE.Scene();

            // create two three.js lights to illuminate the model
            var directionalLight = new THREE.DirectionalLight(0xffffff);
            directionalLight.position.set(0, -70, 100).normalize();
            this.scene.add(directionalLight);

            var directionalLight2 = new THREE.DirectionalLight(0xffffff);
            directionalLight2.position.set(0, 70, 100).normalize();
            this.scene.add(directionalLight2);

            // use the three.js GLTF loader to add the 3D model to the three.js scene
            var loader = new THREE.GLTFLoader();
            loader.load(
                'https://docs.mapbox.com/mapbox-gl-js/assets/34M_17/34M_17.gltf',
                function (gltf) {
                    this.scene.add(gltf.scene);
                }.bind(this)
            );
            this.map = map;

            // use the Mapbox GL JS map canvas for three.js
            this.renderer = new THREE.WebGLRenderer({
                canvas: map.getCanvas(),
                context: gl,
                antialias: true
            });

            this.renderer.autoClear = false;
        },
        render: function (gl, matrix) {
            var rotationX = new THREE.Matrix4().makeRotationAxis(
                new THREE.Vector3(1, 0, 0),
                modelTransform.rotateX
            );
            var rotationY = new THREE.Matrix4().makeRotationAxis(
                new THREE.Vector3(0, 1, 0),
                modelTransform.rotateY
            );
            var rotationZ = new THREE.Matrix4().makeRotationAxis(
                new THREE.Vector3(0, 0, 1),
                modelTransform.rotateZ
            );

            var m = new THREE.Matrix4().fromArray(matrix);
            var l = new THREE.Matrix4()
                .makeTranslation(
                    modelTransform.translateX,
                    modelTransform.translateY,
                    modelTransform.translateZ
                )
                .scale(
                    new THREE.Vector3(
                        modelTransform.scale,
                        -modelTransform.scale,
                        modelTransform.scale
                    )
                )
                .multiply(rotationX)
                .multiply(rotationY)
                .multiply(rotationZ);

            this.camera.projectionMatrix = m.multiply(l);
            this.renderer.state.reset();
            this.renderer.render(this.scene, this.camera);
            this.map.triggerRepaint();
        }
    };

    map.on('style.load', function () {
        map.addLayer(customLayer, 'waterway-label');
    });

    document.getElementById('move').addEventListener('click', function () {
        // creating an event listener to modify the position to modelOrigin2
        modelAsMercatorCoordinate = mapboxgl.MercatorCoordinate.fromLngLat(
            modelOrigin2,
            modelAltitude
        );

        // transformation parameters to position, rotate and scale the 3D model onto the map
        modelTransform = {
            translateX: modelAsMercatorCoordinate.x,
            translateY: modelAsMercatorCoordinate.y,
            translateZ: modelAsMercatorCoordinate.z,
            rotateX: modelRotate[0],
            rotateY: modelRotate[1],
            rotateZ: modelRotate[2],
            /* Since our 3D model is in real world meters, a scale transform needs to be
            * applied since the CustomLayerInterface expects units in MercatorCoordinates.
            */
            scale: modelAsMercatorCoordinate.meterInMercatorCoordinateUnits()
        };

    });
</script>

</body>
</html>

顺便说一句,如果你想在 Mapbox 和 Three.js 之间创建更具交互性的体验,但我建议你查看最新版本的 threebox,因为它可以让你添加任意多的模型和 3D 图层,而且只需几行代码就可以完成下面这些事情

        map.on('style.load', function () {
        map.addLayer({
            id: 'custom_layer',
            type: 'custom',
            renderingMode: '3d',
            onAdd: function (map, mbxContext) {

                window.tb = new Threebox(
                    map,
                    mbxContext,
                    { defaultLights: true }
                );

                var options = {
                    obj: '/3D/soldier/soldier.glb',
                    type: 'gltf',
                    scale: 1,
                    units: 'meters',
                    rotation: { x: 90, y: 0, z: 0 } //default rotation
                }

                tb.loadObj(options, function (model) {
                    soldier = model.setCoords(origin);
                    tb.add(soldier);
                })

            },
            render: function (gl, matrix) {
                tb.update();
            }
        });
    })

- 3D 模型内置和自定义动画

- 完整的光线投射支持 MouseOver/Mouseout、选定、拖放、拖放和旋转、线框

- 考虑高度的 CSS2D 工具提示和标签

**- Three.js 和 Mapbox 相机与深度调整同步 **

- 包括纪念碑的地理定位模型