首页 > 解决方案 > 三框“projectToWorld”返回的值超过了画布,我该如何解决这个问题?(附示例代码)

问题描述

我最近发现在三框中有一个非常方便的方法可以在地图上放置三个.js 对象,即“projectToworld”。

在尝试使用该方法放置我的 three.js 对象时,我意识到该方法返回的 Vector3 非常大,而且不在地图上。

根据threebox的文档,它说

项目到世界

tb.projectToWorld(lnglat) : THREE.Vector3

计算给定 lnglat 对应的 THREE.Vector3。它的逆方法是 tb.unprojectFromWorld。

所以我决定用这个方法在三个js画布中定位我的动画对象。但是这些方法返回的东西真的很大。

在此处输入图像描述

因此,正如我所料,这些值不会将三个对象放在地图上,并且所有对象都消失了,因为它们可能被放置在非常遥远的位置。

我该如何解决这个问题?

我做了一个最小的代码来演示这个问题,如下所示。

  1. 实例化地图
var viewOrigin = [-73.8, 40.7];

var map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/jotnajoa/ckpj6g4ho3nvf18pg0r98pjes/draft',
    zoom: 12,
    center: viewOrigin,
    pitch: 60,
    antialias: true // create the gl context with MSAA antialiasing, so custom layers are antialiased
});

var modelAltitude = 0;
  1. 设置 mapboxgl
function translateCoords(lon, lat) {

    let destination = [lon, lat];
    let finalCoord = mapboxgl.MercatorCoordinate.fromLngLat(destination, 0);

    return finalCoord
}
var modelAsMercatorCoordinate = translateCoords(viewOrigin[0], viewOrigin[1]);

var modelTransform = {
    translateX: modelAsMercatorCoordinate.x,
    translateY: modelAsMercatorCoordinate.y,
    translateZ: modelAsMercatorCoordinate.z,
    scale: modelAsMercatorCoordinate.meterInMercatorCoordinateUnits()
};
  1. 在 mapboxgl 之外为 three.js 渲染设置变量,因为我需要在 mapboxgl 生命周期挂钩之外访问它。
const camera = new THREE.Camera();
const scene = new THREE.Scene();

// Setting map, canvas, renderer variable from the outside to access that from outside later

var map;
var canvas;
var renderer;
let particleGroup;
var frameCount = 0;  
 
  1. 实例化 mapboxgl 自定义层
function generateMap() {

    var customLayer = {
        id: '3d-model',
        type: 'custom',
        renderingMode: '3d',
        onAdd: function(map, gl) {
            map = map;
            canvas = map.getCanvas()
            renderer = new THREE.WebGLRenderer({
                canvas: map.getCanvas(),
                context: gl,
                antialias: true,
                preserveDrawingBuffer: true
            });
            renderer.autoClear = false;
        },
        render: function(gl, matrix) {
            frameCount += 0.1;
            rotate(frameCount)
            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
                    )
                )

            camera.projectionMatrix = m.multiply(l);
            renderer.resetState();
            renderer.render(scene, camera);
            map.triggerRepaint();

        }
    };

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

}
  1. 绘制不断旋转的球体
function fillingSpheres(group) {
    const meshGroup = new THREE.Group();

    const { count } = group.geometry.attributes.position
    const { array } = group.geometry.attributes.position

    for (let i = 0; i < count; i++) {

        let i3 = i * 3;
        const x = array[i3]
        const y = array[i3 + 1]
        const z = array[i3 + 2];
        const posVec = new THREE.Vector3(x, y, z);

        const circleGeo = new THREE.SphereGeometry(10, 10, 10);
        const circleMtl = new THREE.MeshBasicMaterial({ color: 'green' });
        const circleMesh = new THREE.Mesh(circleGeo, circleMtl);
        circleMesh.userData.destination = posExample[i % 10];
        circleMesh.position.set(posVec.x, posVec.y, posVec.z)
        meshGroup.add(circleMesh)
    };
    console.log(meshGroup)
    return meshGroup;

}
  1. 将球体移动到地图上的相应位置(这是出现问题的地方)

首先,我根据当前的地图设置实例化了 tb 对象

function setThreeBox() {

    window.tb = new Threebox(
        map,
        map.getCanvas().getContext('webgl'), {
            realSunlight: true,
            enableSelectingObjects: true, //change this to false to disable 3D objects selection
            enableTooltips: true, // change this to false to disable default tooltips on fill-extrusion and 3D models
        }
    );
    tb.altitudeStep = 1;
}

二、通过projectToWorld获取坐标并定位

function moveSphere() {

    // instantiate threebox object based on map

    setThreeBox()

    const { children } = particleGroup
    children.forEach((d) => {
        const { destination } = d.userData;
        const coordVec = tb.projectToWorld([destination.lng, destination.lat])
        console.log(coordVec)
        d.position.set(coordVec)
    })
}

然后,所有的坐标都像 x:200000 并且不再显示场景中的三个对象。

我做错什么了?

我之所以基于three.js而不是仅仅基于三框,是因为我无法说明三框坐标空间上的旋转圆。

运行代码在以下链接中。

https://codepen.io/jotnajoa/pen/poeKoOW (由于某种原因,示例在刷新代码后正在运行,打开后不会立即启动)

如果有熟悉三盒的人可以帮助我,将不胜感激。我认为如果我可以在仅三盒环境中直接访问 scene() 对象,则解决此问题可能会更容易。如果可以访问,我可以轻松地在三框内制作基于帧的动画。(也许我错了)

单击按钮时触发放置。

标签: three.jsmapbox-gl-jsthreebox

解决方案


奇怪的是没有人能回答这个问题。所以我终于想出了如何自己制作它。

解决方案在以下链接中。

主要原因是 mapbox 绘制的东西不是基于其矢量配置。它通过其矩阵呈现事物,如下所示。

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))

            sphere.position.y = 0.2;

            camera.projectionMatrix.elements = matrix;
            camera.projectionMatrix = m.multiply(l);
            renderer.state.reset();
            renderer.render(scene, camera);

https://codepen.io/jotnajoa/pen/BaWGBoL?editors=1010


推荐阅读