threebox "projectToWorld" returns 值超过 canvas,我该如何解决这个问题? (附示例代码)
threebox "projectToWorld" returns values exceeding canvas, how do I fix this? (with sample code)
我最近发现三框有一个非常方便的方法可以在地图上放置three.js个对象,就是
"projectToworld".
在尝试使用该方法放置我的 three.js 对象时,
我意识到 Vector3 方法 returns 真的很大而且不在地图上。
根据threebox的文档,它说
projectToWorld
tb.projectToWorld(lnglat) : THREE.Vector3
Calculate the corresponding THREE.Vector3 for a given lnglat. It's
inverse method is tb.unprojectFromWorld.
所以我决定用这个方法在三个js中定位我的动画对象canvas。
但是什么方法returns真的很庞大
正如我所预料的那样,这些值并未将这三个对象放置在地图上并且所有对象都消失了,因为它们可能被放置在非常远的位置。
我该如何解决这个问题?
我做了一个最小的代码来演示这个问题,如下所示。
- instantiating map
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;
- Set 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()
};
- set variables for three.js render outside of mapboxgl because I need to access it outside of mapboxgl lifecycle hook.
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;
- Instantiate mapboxgl customlayer
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');
});
}
- Draw constantly revolving sphere
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;
}
- Move the spheres to the corresponding location on the map (This is where the issue occurs)
首先,我根据当前地图设置实例化了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而不是单单基于三盒,是因为无法在三盒中说明three.js坐标space上的旋转圆。
运行代码在下面link.
https://codepen.io/jotnajoa/pen/poeKoOW
(由于某些原因,示例在刷新代码后是运行,打开后不会立即启动)
如果有懂三盒的朋友能帮我解决一下,将不胜感激。
我认为如果我可以在只有三框的环境中直接访问 scene() 对象,那么解决这个问题会更容易。如果可以访问,我可以轻松地在三框中制作基于帧的动画。 (也许我错了)
点击按钮时触发放置。
奇怪的是没有人能回答这个问题。
所以我终于想出了自己做的方法。
解决方法如下link.
主要原因是 mapbox 绘制的东西不是基于其矢量配置。它通过其矩阵呈现事物如下。
var m = new THREE.Matrix4().fromArray(矩阵);
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);
我最近发现三框有一个非常方便的方法可以在地图上放置three.js个对象,就是 "projectToworld".
在尝试使用该方法放置我的 three.js 对象时, 我意识到 Vector3 方法 returns 真的很大而且不在地图上。
根据threebox的文档,它说
projectToWorld
tb.projectToWorld(lnglat) : THREE.Vector3
Calculate the corresponding THREE.Vector3 for a given lnglat. It's inverse method is tb.unprojectFromWorld.
所以我决定用这个方法在三个js中定位我的动画对象canvas。 但是什么方法returns真的很庞大
正如我所预料的那样,这些值并未将这三个对象放置在地图上并且所有对象都消失了,因为它们可能被放置在非常远的位置。
我该如何解决这个问题?
我做了一个最小的代码来演示这个问题,如下所示。
- instantiating map
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;
- Set 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()
};
- set variables for three.js render outside of mapboxgl because I need to access it outside of mapboxgl lifecycle hook.
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;
- Instantiate mapboxgl customlayer
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');
});
}
- Draw constantly revolving sphere
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;
}
- Move the spheres to the corresponding location on the map (This is where the issue occurs)
首先,我根据当前地图设置实例化了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而不是单单基于三盒,是因为无法在三盒中说明three.js坐标space上的旋转圆。
运行代码在下面link.
https://codepen.io/jotnajoa/pen/poeKoOW (由于某些原因,示例在刷新代码后是运行,打开后不会立即启动)
如果有懂三盒的朋友能帮我解决一下,将不胜感激。 我认为如果我可以在只有三框的环境中直接访问 scene() 对象,那么解决这个问题会更容易。如果可以访问,我可以轻松地在三框中制作基于帧的动画。 (也许我错了)
点击按钮时触发放置。
奇怪的是没有人能回答这个问题。 所以我终于想出了自己做的方法。
解决方法如下link.
主要原因是 mapbox 绘制的东西不是基于其矢量配置。它通过其矩阵呈现事物如下。
var m = new THREE.Matrix4().fromArray(矩阵); 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);