全景图其实是一种广角图,它的原理是等距圆柱投影。说白了就是将一个球体上的所有的点,全部投影到一个圆柱体的侧面上去,圆柱侧面展开图上包含了球体上所有的像素点。
哪里能看到全景图? 这种新颖的展现方式如今已经被各行各业所应用。不仅能够实现身临其境的效果,更能嵌入各种应用功能。
例如我们熟知的百度地图、高德地图、谷歌地图的街景模式,旅游网站的虚拟旅行模块,看房软件的全景图浏览等。遇到找不到的建筑或者地方,打开街景模式+导航指引,可以完美解决路痴的痛苦。
全景图像素材的获取有两种方式:一是采用专门全景设备,如全景相机或者带有鱼眼镜头或者广角镜头的相机;二是利用普通相机拍摄局部图像,然后经过拼接形成全景图。
现在明白了全景图素材的获取方式,那么我们要做的就是创建全景图,怎么办?可以利用ThingJS创建全景图,还可以实现全景图与数字孪生可视化园区模型联动、巡游功能,实现第一人称在数字孪生可视化场景中行走的效果。
点击地面上的图标,全景图播放器中的图片跟随数字孪生可视化场景中小人的运动路径改变。点击全景图播放器中的热点,数字孪生可视化场景中小人跟随热点切换运动。
1.加载全景图插件,设置全景图数据。
2.创建全景图播放器,设置初始视角。
function createPlayer() {
THING.PanoManager.createPlayer(panoData, {
fullscreen: false, // 是否全屏
location: "right-center", // 悬浮框所在位置
offsetX: 0, // 相对于location的X轴偏移
offsetY: 0 // 相对于location的Y轴偏移
}, {
panoID: panoData.config.panos[0].panoID, // 初始打开的全景图
h: 180, // 打开全景图的初始视角h
v: 0 // 打开全景图的初始视角v
}, null, function() {
createTourButton();
createPoints();
});
// 全景图播放器中全景图切换事件
app.on(THING.PanoEvent.PanoChange, function(event) {
var panoID = event.panoID; // 当前打开的全景图ID
var panoObj = app.query("[panoID=" + panoID + "]")[0]; // 当前打开的全景图ID
if (isPoint && panoObj) {
var panos = app.query("[panoObj=1]");
moveToNext(panoObj.position);
}
curPanoConfig = event.pano;
if (!carModel) return;
reComputePosition(curPanoConfig.position, carModel.position, carModel.angles);
});
}
3.数字孪生可视化园区模型与全景图联动,数字孪生可视化园区创建模型后全景图也会加载模型,移动模型全景图内的模型位置也对应改变。
function createModel() {
// 创建Thing
carModel = app.create({
type: 'Thing',
name: 'car',
url: modelUrl, // 模型地址
position: [-25, 0, 30],
angles: [0, 90, 0], // 旋转
complete: function(ev) {
ev.object.style.renderOrder = 1;
// 添加到全景图中
var pano3DObjProperties =
THING.PanoManager.compute3DObjectProperties(panoData.config.panos[0].position.position, ev.object.position, ev.object.angles);
THING.PanoManager.create3DObject("pano3DObj", modelUrl, false, pano3DObjProperties);
}
});
carModel.draggable = true; // 设置模型可拖拽
carModel.on('drag', function(ev) {
if (!ev.picked) return;
var pickPos = ev.pickedPosition; // 获取当前拾取坐标
ev.object.position = pickPos; // 设置模型位置
reComputePosition(curPanoConfig.position.position, ev.object.position, ev.object.angles);
});
}
4.根据全景图数据在场景中创建点和路径线,控制数字孪生可视化人物模型移动,开启、暂停巡游。
function createRoute() {
var line = app.query("panoLine");
if (line[0]) line[0].destroy();
var line2 = app.create({
type: 'RouteLine',
name: "panoLine",
points: points,
image: 'https://www.thingjs.com/static/images/line01.png' // 线路中的纹理资源
});
// 启用 UV 动画
line2.scrollUV = true;
line2.style.renderOrder = 9;
}
/**
* 创建人物模型
*/
function createPersonModel() {
peopleModel = app.create({
type: 'Thing',
url: 'https://model.3dmomoda.cn/models/0bcba8ca78734b64a3dae3eb699a913c/0/gltf/',
position: [-10.4, 0, 27.9],
complete: function() {
peopleModel.playAnimation({ name: '_defaultAnim_', loop: true });
}
});
peopleModel.rotateY(-90.0);
}
/**
* 控制人物移动
*/
function moveToNext(p) {
peopleModel.playAnimation({ name: '跑步', loop: true });
peopleModel.style.opacity = 1.0;
peopleModel.style.outlineColor = "#FF0000";
peopleModel.moveTo({
"position": p,
"time": 1500,
"orientToPath": true,
"lerp": false,
"complete": function() {
peopleModel.playAnimation({ name: '_defaultAnim_', loop: true });
}
});
}
/**
* 计算全景图中模型位置
*/
function reComputePosition(panoPosition, modelPosition, modelAngles) {
console.log(panoPosition);
console.log(modelPosition);
var pano3DObjProperties = THING.PanoManager.compute3DObjectProperties(panoPosition, modelPosition, modelAngles);
console.log(pano3DObjProperties);
THING.PanoManager.set3DObjectProperties("pano3DObj", pano3DObjProperties);
}
})