一、实验内容

通过上次实验,了解了Three.js创建场景的基本步骤。这一节,我们将通过Three.js实现全景漫游功能。如下图:

全景图是获取一个3D场景中的不同角度的图片,然后通过拼接、融合实现3D的虚拟场景。例如百度地图上全景漫游。

二、探讨

方法1:使用软件(例如:Pano2vr )

前提准备:下载Pano2vr软件;全景图。

全景图:对于现实世界,可以通过鱼眼摄像头拍摄得到全景图;对于虚拟世界,使用3D模型软件,例如3dMaxs,导入场景模型,生成全景图。

有点:可以导出flash、html5等格式的全景漫游。你只需要准备一张全景图就可以。

这里我们使用的是另外一种方法,使用Three.js实现全景漫游的功能,这样有利于我们以后的功能扩展。

三、全景漫游基本知识

全景图通过广角的表现手段以及绘画、相片、视频、三维模型等形式,尽可能多表现出周围的环境。360全景,即通过对专业相机捕捉整个场景的图像信息或者使用建模软件渲染过后的图片,使用软件进行图片拼合,并用专门的播放器进行播放,即将平面照片或者计算机建模图片变为360 度全观,用于虚拟现实浏览,把二维的平面图模拟成真实的三维空间,呈现给观赏者。[1]

实现360度全景漫游的有3种方法,1)球形图、2)圆柱图、3)正方体。

对基本原理比较感兴趣的朋友可以去看看这篇博客,写的很详细。

https://aotu.io/notes/2016/01/02/3D-panorama/?utm_source=tuicool&utm_medium=referral

注:圆柱图实现方法的确定比较明显,所以使用的比较少。上面提到的软件实现全景漫游,使用的是球形的方法。这里我们实现的方法是正方体。

四、实验步骤

1、创建场景:

/**
* 创建场景
* @type {THREE.Scene}
*/
scene = new THREE.Scene();

2、添加相机

这里我们使用的是透视相机。

/**
* 添加相机
* @type {THREE.PerspectiveCamera}
*/
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

3、添加渲染器

这里使用的是CSS3DRenderer渲染器,要在html文件中添加:

/**
* 添加一个渲染器
* @type {THREE.CSS3DRenderer}
*/
renderer = new THREE.CSS3DRenderer();
renderer.setClearColor(0x000000, 1.0);
renderer.setSize(window.innerWidth, window.innerHeight);

4、创建正方体

创建正方体,分别对正方体的六个面进行贴图

/**
*正方体的6个面的资源及相关(坐标、旋转等)设置
*/
var sides = [{url: './images/posx.jpg', position: [-512, 0, 0], rotation: [0, Math.PI / 2, 0]},
{url: './images/negx.jpg', position: [512, 0, 0], rotation: [0, -Math.PI / 2, 0]},
{url: './images/posy.jpg', position: [0, 512, 0], rotation: [Math.PI / 2, 0, Math.PI]},
{url: './images/negy.jpg', position: [0, -512, 0], rotation: [-Math.PI / 2, 0, Math.PI]},
{url: './images/posz.jpg', position: [0, 0, 512], rotation: [0, Math.PI, 0]},
{url: './images/negz.jpg',position: [0, 0, -512], rotation: [0, 0, 0]}]; for (var i = 0; i < sides.length; i++) {
var side = sides[i];
var element = document.createElement('section');
document.body.appendChild(element);
element.id = 'section_'+ i;
var imgElement = document.createElement('img');
imgElement.width = 1028; // 2 pixels extra to close the gap.
imgElement.height = 1028;
imgElement.src = side.url;
element.appendChild(imgElement);
var object = new THREE.CSS3DObject(element);
object.position.set(side.position[0], side.position[1], side.position[2]);
object.rotation.set(side.rotation[0], side.rotation[1], side.rotation[2]);
scene.add(object);
}

5、实时渲染函数

/**
* 实时渲染函数
*/
function render() {
requestAnimationFrame(render);
lon = Math.max(-180, Math.min(180, lon));//限制固定角度内旋转
lat = Math.max(-85, Math.min(85, lat));
phi = THREE.Math.degToRad(90 - lat);
theta = THREE.Math.degToRad(lon);
target.x = Math.sin(phi) * Math.cos(theta);
target.y = Math.cos(phi);
target.z = Math.sin(phi) * Math.sin(theta);
camera.lookAt(target);
renderer.render(scene, camera);
}

6、窗口改变时对camera焦点的处理

/**
* 窗体大小改变
*/
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}

7、相机焦点跟着鼠标的操作移动

function onDocumentMouseDown(event) {
event.preventDefault();
document.addEventListener('mousemove', onDocumentMouseMove, false);
document.addEventListener('mouseup', onDocumentMouseUp, false);
} function onDocumentMouseMove(event) {
var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
lon -= movementX * 0.1;
lat += movementY * 0.1;
} function onDocumentMouseUp(event) {
document.removeEventListener('mousemove', onDocumentMouseMove);
document.removeEventListener('mouseup', onDocumentMouseUp);
} /**
* 鼠标滚轮改变相机焦距
*/
function onDocumentMouseWheel(event) {
camera.fov -= event.wheelDeltaY * 0.05;
camera.updateProjectionMatrix();
} function onDocumentTouchStart(event) {
event.preventDefault();
var touch = event.touches[0];
touchX = touch.screenX;
touchY = touch.screenY;
} function onDocumentTouchMove(event) {
event.preventDefault();
var touch = event.touches[0];
lon -= (touch.screenX - touchX) * 0.1;
lat += (touch.screenY - touchY) * 0.1;
touchX = touch.screenX;
touchY = touch.screenY;
}

五、总结

开始导师要实现全景图的功能,开始对这方面的知识不了解,无从下手。后来通过查找资料,看论文等掌握了全景漫游的基本知识点。通过这次式样,掌握了全景漫游功能的基本原理已经实现方法。

六、完整代码

main.js

var scene, renderer, camera;
var target = new THREE.Vector3(); //相机焦点
var lon = 90, lat = 0;
var phi = 0, theta = 0;
var touchX, touchY; function init() {
/**
* 创建场景
* @type {THREE.Scene}
*/
scene = new THREE.Scene(); /**
* 添加相机
* @type {THREE.PerspectiveCamera}
*/
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000); /**
* 添加一个渲染器
* @type {THREE.CSS3DRenderer}
*/
renderer = new THREE.CSS3DRenderer();
renderer.setClearColor(0x000000, 1.0);
renderer.setSize(window.innerWidth, window.innerHeight); /**
*正方体的6个面的资源及相关(坐标、旋转等)设置
*/
var sides = [{url: './images/posx.jpg', position: [-512, 0, 0], rotation: [0, Math.PI / 2, 0]},
{url: './images/negx.jpg', position: [512, 0, 0], rotation: [0, -Math.PI / 2, 0]},
{url: './images/posy.jpg', position: [0, 512, 0], rotation: [Math.PI / 2, 0, Math.PI]},
{url: './images/negy.jpg', position: [0, -512, 0], rotation: [-Math.PI / 2, 0, Math.PI]},
{url: './images/posz.jpg', position: [0, 0, 512], rotation: [0, Math.PI, 0]},
{url: './images/negz.jpg',position: [0, 0, -512], rotation: [0, 0, 0]}]; for (var i = 0; i < sides.length; i++) {
var side = sides[i];
var element = document.createElement('section');
document.body.appendChild(element);
element.id = 'section_'+ i;
var imgElement = document.createElement('img');
imgElement.width = 1028; // 2 pixels extra to close the gap.
imgElement.height = 1028;
imgElement.src = side.url;
element.appendChild(imgElement);
var object = new THREE.CSS3DObject(element);
object.position.set(side.position[0], side.position[1], side.position[2]);
object.rotation.set(side.rotation[0], side.rotation[1], side.rotation[2]);
scene.add(object);
}
document.body.appendChild(renderer.domElement); /**
* 注册监听
*/
document.addEventListener('mousedown', onDocumentMouseDown, false);
document.addEventListener('mousewheel', onDocumentMouseWheel, false);
document.addEventListener('touchstart', onDocumentTouchStart, false);
document.addEventListener('touchmove', onDocumentTouchMove, false);
window.addEventListener('resize', onWindowResize, false); render();
} /**
* 实时渲染函数
*/
function render() {
requestAnimationFrame(render);
lon = Math.max(-180, Math.min(180, lon));//限制固定角度内旋转
lon += 0.1;//自动旋转
lat = Math.max(-85, Math.min(85, lat));
phi = THREE.Math.degToRad(90 - lat);
theta = THREE.Math.degToRad(lon);
target.x = Math.sin(phi) * Math.cos(theta);
target.y = Math.cos(phi);
target.z = Math.sin(phi) * Math.sin(theta);
camera.lookAt(target);
renderer.render(scene, camera);
} /**
* 窗体大小改变
*/
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
} function onDocumentMouseDown(event) {
event.preventDefault();
document.addEventListener('mousemove', onDocumentMouseMove, false);
document.addEventListener('mouseup', onDocumentMouseUp, false);
} function onDocumentMouseMove(event) {
var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
lon -= movementX * 0.1;
lat += movementY * 0.1;
} function onDocumentMouseUp(event) {
document.removeEventListener('mousemove', onDocumentMouseMove);
document.removeEventListener('mouseup', onDocumentMouseUp);
} /**
* 鼠标滚轮改变相机焦距
*/
function onDocumentMouseWheel(event) {
camera.fov -= event.wheelDeltaY * 0.05;
camera.updateProjectionMatrix();
} function onDocumentTouchStart(event) {
event.preventDefault();
var touch = event.touches[0];
touchX = touch.screenX;
touchY = touch.screenY;
} function onDocumentTouchMove(event) {
event.preventDefault();
var touch = event.touches[0];
lon -= (touch.screenX - touchX) * 0.1;
lat += (touch.screenY - touchY) * 0.1;
touchX = touch.screenX;
touchY = touch.screenY;
} /**
* 脚本入口
* @type {init}
*/
window.onload = init;

引用

1:百度百科 http://baike.baidu.com/view/2424250.htm

<Three.js>(第三节)全景漫游的更多相关文章

  1. three.js全景漫游实践

    Hello 小伙伴们,如果觉得本文还不错,记得给个 star , 小伙伴们的 star 是我持续更新的动力!GitHub 地址 简介 全景图分两种 由六张正方形图片组成的SkyBox 一整张的宽高比为 ...

  2. 打造H5里的“3D全景漫游”秘籍

    近来风生水起的VR虚拟现实技术,抽空想起年初完成的“星球计划”项目,总结篇文章与各位分享一下制作基于Html5的3D全景漫游秘籍. QQ物联与深圳市天文台合作,在手Q“发现新设备”-“公共设备”里,连 ...

  3. 打造自己的3D全景漫游

    three.js 示例: ​ 打造H5里的"3D全景漫游"秘籍 - 腾讯ISUX ​ QQ物联星球计划 通过pano2vr直接将鱼眼全景图生成立体空间的六个面:也可通过Photos ...

  4. 轩辕展览-VR虚拟展厅设计如何实现全景漫游功能

    什么是在线3d漫游?如何在VR虚拟展厅设计之中实现3d漫游功能?让我们来分享3dVR虚拟展厅的在线漫游. 实际上,在线3d漫游就是通过3d仿真场景,使用鼠标和键盘在虚拟空间之中自由漫游,它可以从高空俯 ...

  5. 3D全景漫游

    全景图共分为三种: ①球面全景图 利用一张全景图围成一个球,自身位置位于球体内.由于图片是矩形,所以最上和最下的缝合处很明显就能够看得出来. 球面全景图是最接近人眼的构建模式,若利用多个立面构建,拼接 ...

  6. Three.js 实现3D全景侦探小游戏🕵️

    背景 你是嘿嘿嘿侦探社实习侦探️,接到上级指派任务,到甄开心小镇调查市民甄不戳宝石失窃案,根据线人流浪汉老石‍提供的线索,小偷就躲在小镇,快把他找出来,帮甄不戳寻回失窃的宝石吧! 本文使用 Three ...

  7. Unity VR全景漫游

    一.前言: 最近VR如火如茶,再不学习就落伍啦.有空闲时间,跟Rodolfo一起研究下相关知识. 本文介绍了两种方法来制作VR场景: 方法一:通过6张小图搭建的VR场景 方法二:通过一张全景图来搭建V ...

  8. 摸索js的3d全景

    先我在网上找到了一个例子,http://silali.vicp.net/three/emaple.html 完美实现3d全景,在详细查看这个例子后,发现他在手机上运行并不流畅,而且显不全并会卡顿. 我 ...

  9. 全景视频外包团队:技术分享Unity3D全景漫游

    作者:未知 1.建模中使用的图片.文件.文件夹等以及模型中物体.材质等的名称都不能使用中文或者特殊符号,可以使用英文字母.数字.下划线等 2.调整Max的单位为米 3.烘培光影的设置 4.模型的中的植 ...

随机推荐

  1. Smalltalk

    Smalltalk is an object-oriented, dynamically typed, reflective programming language. Smalltalk was o ...

  2. Unity 向量点乘、叉乘

    向量点乘计算角度,向量叉乘计算方位 a,b为向量 点乘计算公式:a x b = |a| x |b| x cosθ 叉乘计算公式:a x b = |a| x |b| x sinθ

  3. JS取出特定字符前后的字符串,针对一串字符里面的单个字符前后的字符串

    //针对一串自负里面的单个字符前后的字符串<!doctype html> <html> <head> <meta charset="utf-8&qu ...

  4. org.xml.sax.SAXParseException: Failed to read schema document 的原因分析与解决方法

    现象: org.xml.sax.SAXParseException: schema_reference.4: Failed to read schema documen t 'http://www.s ...

  5. Codeforces 679A Bear and Prime 100

    链接:传送门 题意:给你一个隐藏数,这个隐藏数在[2,100]之间,现在最多可以询问20次,每次询问的是这个数是不是隐藏数的底数,是为yes,不是为no,每次询问后都需要flush一下输出缓冲区,最后 ...

  6. hdu5791 TWO

    hdu5791 TWO 题意 给你两个数串 问你两个数串有多少子串一致 子串不一定是连续的 解法 我们设 \(dp[i][j]\) 表示A串匹配到 i 位,B串匹配到 j 位,一致的子串数.那么我们有 ...

  7. UDP Linux编程(客户端&服务器端)

    服务器端 服务器不用绑定地址,他只需要进行绑定相应的监听端口即可. #include <sys/types.h> #include <sys/socket.h> #includ ...

  8. 常用js方法封装

    常用js方法封装 var myJs = { /* * 格式化日期 * @param dt 日期对象 * @returns {string} 返回值是格式化的字符串日期 */ getDates: fun ...

  9. sql删除注意的问题

    老大骂你都是有原因的,基础要打好!!!! SQL关于删除的三个语句:DROP.TRUNCATE. DELETE 的区别. DROP test; 删除表test,并释放空间,将test表删除的一干二净 ...

  10. 【HDOJ 5407】 CRB and Candies (大犇推导

    pid=5407">[HDOJ 5407] CRB and Candies 赛后看这题题解仅仅有满眼的迷茫------ g(N) = LCM(C(N,0),C(N,1),...,C(N ...