3D全景之ThreeJs

一、前言

随着H5越来越多的被应用到各个领域,3D也越来越频繁的出现在各个H5案例中,今天我们就来讨论一下3D全景的实现。

据百度百科上介绍:720全景是视角超过人的正常视角的图像。顾名思义就是给人以三维立体感觉的实景360度全方位图像。全景实际上只是一种对周围景象以某种几何关系进行映射生成的平面图片,只有通过全景播放器的矫正处理才能成为三维全景(全景特指水平360度,上下360度全能观看的,能看到“天、地”的全景)。

二、全景图分类

首先我们来看一下全景图的分类:

全景图共分为三种:

1、球面全景图

利用一张全景图围成一个球,自身位置位于球体内。由于图片是矩形,所以最上和最下的缝合处很明显就能够看得出来。

虽然球面全景图具有和人眼最接近的构建模式,但需要很多个立面才可以构建成一个球体,球面的经纬度坐标无法展开成一个平面贴图,相对于其他方案,性能消耗过高,拼接方法过于繁琐,性能消耗高。

2、柱状全景图

这个则是前两种构建模式的结合版啦。其实, 粗略一想就可以想到这种实现方法的缺点, 如果我们将拍摄到的全景图贴在一个圆柱的侧面上, 我们站在圆柱中心朝四周看的话, 应该就有全景观察的效果. 不过这样做也有坏处, 也就是我们的头顶跟脚底都是无法看到的区域.

3、立方体全景图

一个立方体,有六个面组成,所以就需要六张图片啦。自身的位置位于立方体中间。这也是最常见的全景图构建模式。

通过比较,显而易见,采用立方体全景图,无论体验还是性能都比较好。那具体到使用ThreeJS实现全景图这个场景, 我们需要做什么呢?

三、用ThreeJs实现立方体全景图

html结构:

 
<div id="controlBtn" class="controlBtn controlIconae"></div> <!-- 陀螺仪开启按钮 -->
<div id="pano">
<!-- 正方体的六个面 -->
<div id="bg_section_0" class="bg_section bg_section_4 scale_test">
<img class="bg" src="data:images/panorama.right.jpg" alt="">
<div class="btn1 bounceant1"></div> <!-- 可点击按钮1 -->
</div>
<div id="bg_section_1" class="bg_section bg_section_5 scale_test">
<img class="bg" src="data:images/panorama.left.jpg" alt="">
</div>
<div id="bg_section_2" class="bg_section bg_section_2 scale_test">
<img class="bg" src="data:images/panorama.top.jpg" alt="">
</div>
<div id="bg_section_3" class="bg_section bg_section_3 scale_test">
<img class="bg" src="data:images/panorama.bottom.jpg" alt="">
</div>
<div id="bg_section_4" class="bg_section bg_section_1 scale_test">
<img class="bg" src="data:images/panorama.front.jpg" alt="">
<div class="btn2 bounceant1"></div> <!-- 可点击按钮2 -->
</div>
<div id="bg_section_5" class="bg_section bg_section_0 scale_test">
<img class="bg" src="data:images/panorama.back.jpg" alt="">
</div>
</div>
 

JS:

引用的JS库:

<script src="js/zepto.min.js"></script>
<script src="js/three.min.js"></script>
<script src="js/CSS3DRenderer.min.js"></script>
<script src="js/DeviceOrientationControls.js"></script>

核心js代码实现步骤:

1、创建场景,即是画布,是所有物体object的容器。在最开始的时候对场景实例化,将之后构建的物体都添加到场景中即可。

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

2、添加相机

在ThreeJS中, 相机还分为CubeCamera(立方体相机),PerspectiveCamera(透视相机)以及OrthographicCamera(正交相机)。其中, CubeCamera是创建动态贴图用的,OrthographicCamera创建的照相机不具有透视效果。 在这里, 我们用到的是PerspectiveCamera(透视相机)。 如下图所示, fov是相机视角的夹角,aspect等于相机画幅比例,near和far分别是照相机到视景体最近、最远的距离, 均为正值,且far应大于near。

/**
* 添加相机
* @type {THREE.PerspectiveCamera}
*/
camera = new THREE.PerspectiveCamera(
75, // 相机视角的夹角
window.innerWidth / window.innerHeight, // 相机画幅比
1, // 最近焦距
1000 // 最远焦距
);

3、添加渲染器

渲染器是用来设定渲染的结果会在页面的什么元素上面呈现,以及按什么规则来渲染。这里使用的是CSS3DRenderer渲染器。

/*
* 添加渲染器
*/
renderer = new THREE.CSS3DRenderer(); // 定义渲染器
renderer.setSize( window.innerWidth, window.innerHeight ); // 定义尺寸
document.body.appendChild( renderer.domElement ); // 将场景到加入页面中

4、创建正方体

立方体全景图有6个面,我们需要定义每个面贴图的背景图片,3D位置,旋转角度(默认的6个面都是朝着我们的,我们需要定义朝坐标轴的各个方向做90度的旋转,才可以搭建成一个立方体)。

/**
*正方体的6个面的资源及相关(坐标、旋转等)设置
*/
var flipAngle = Math.PI, // 180度
rightAngle = flipAngle / 2, // 90度
tileWidth = 512;
var sides = [{
url: "images/panorama.right.jpg", //right
position: [-tileWidth, 0, 0],
rotation: [0, rightAngle, 0]
}, {
url: "images/panorama.left.jpg", //left
position: [tileWidth, 0, 0],
rotation: [0, -rightAngle, 0]
}, {
url: "images/panorama.top.jpg", //top
position: [0, tileWidth, 0],
rotation: [rightAngle, 0, Math.PI]
}, {
url: "images/panorama.bottom.jpg", //bottom
position: [0, -tileWidth, 0],
rotation: [-rightAngle, 0, Math.PI]
}, {
url: "images/panorama.front.jpg", //front
position: [0, 0, tileWidth],
rotation: [0, Math.PI, 0]
}, {
url: "images/panorama.back.jpg", //back
position: [0, 0, -tileWidth],
rotation: [0, 0, 0]
}]; for ( var i = 0; i < sides.length; i ++ ) {
var side = sides[ i ];
var element = document.getElementById("bg_section_"+i);
element.width = 1026;
element.height = 1026; // 2 pixels extra to close the gap.
// 添加一个渲染器
var object = new THREE.CSS3DObject( element );
object.position.fromArray( side.position );
object.rotation.fromArray( side.rotation );
scene.add( object ); }

5、实时渲染函数

这里我们用的是Threejs的 实时渲染 :就是需要不停的对画面进行渲染,即使画面中什么也没有改变,也需要重新渲染。其中一个重要的函数是requestAnimationFrame,这个函数就是让浏览器去执行一次参数中的函数,这样通过上面animate中调用requestAnimationFrame()函数,requestAnimationFrame()函数又让animate()再执行一次,就形成了我们通常所说的渲染循环了。

/**
* 实时渲染函数
*/
function animate() {
requestAnimationFrame(animate);
// lon = Math.max(-180, Math.min(180, lon));//限制固定角度内旋转
// lon += 0.1;//自动旋转
lat = Math.max(-85, Math.min(85, lat)); //限制固定角度内旋转
phi = THREE.Math.degToRad(85 - lat);
theta = THREE.Math.degToRad(lon+180);
target.x = Math.sin(phi) * Math.cos(theta);
target.y = Math.cos(phi);
target.z = Math.sin(phi) * Math.sin(theta);
camera.lookAt( target );
camera.updateProjectionMatrix();
isDeviceing == false ? initMouseControl() : deviceControl.update();
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.deltaY * 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; }

8、绑定陀螺仪

var controlsBtn= document.getElementById("controlBtn"); // 控制陀螺仪开关的按钮
var isDeviceing = false; // 陀螺仪状态
controlsBtn.addEventListener("touchend", controlDevice, true);
isDeviceing == true ? $("#controlBtn").addClass("controlIconae") : $("#controlBtn").addClass("controlIcon");
// 初始化陀螺仪
function initDevices() {
deviceControl = new THREE.DeviceOrientationControls(camera);
}
/* 控制陀螺仪 */
function controlDevice(event) {
if (isDeviceing == true) {
isDeviceing = false;
//关闭陀螺仪
$("#controlBtn").removeClass("controlIcon").addClass("controlIconae");
} else {
isDeviceing = true;
//开启陀螺仪
$("#controlBtn").removeClass("controlIconae").addClass("controlIcon");
}
}

9、给全景里的物体加点击事件

/**
*点击按钮
*/
$('.btn1').on('touchstart',function(){
alert('第一个按钮被点击了');
});
$('.btn2').on('touchstart',function(){
alert('第二个按钮被点击了');
});

DEMO演示

五、注意事项

重力感应在安卓下有时候体验非常不好,所以我做的这个DEMO在iOS和Android下都支持手势操作,想体验重力感应的小伙伴可以点击左上角的按钮,选择开启重力感应或关闭重力感应。

转载地址:https://www.h5-share.com/articles/201612/threejsdemo.html

3D全景之ThreeJs的更多相关文章

  1. ThreeJs 3D 全景项目开发总结

    本文来自网易云社区 作者:唐钊 项目背景 那是在一个毫无征兆的下午,我还沉浸在 vue 的世界中,突然编辑跑过来说N的新官网想做一些3D全景的东西,一开始其实我的内心是拒绝的,一是没怎么实质性做过 W ...

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

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

  3. 摸索js的3d全景

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

  4. 3D全景!这么牛!!

    如果你用过网页版的百度地图,你大概3D全景图浏览是一种怎样的酷炫体验:在一个点可以360度环顾周围的建筑.景色,当然也可以四周移动,就像身临其境. 全景图共分为三种: ①球面全景图 利用一张全景图围成 ...

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

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

  6. 浏览器中实现3D全景浏览

    如果你用过网页版的百度地图,你大概3D全景图浏览是一种怎样的酷炫体验:在一个点可以360度环顾周围的建筑.景色,当然也可以四周移动,就像身临其境. 科普 全景图共分为三种: ①球面全景图 利用一张全景 ...

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

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

  8. 3D全景漫游

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

  9. 基于 HTML5 WebGL 构建智能数字化城市 3D 全景

    前言 自 2011 年我国城镇化率首次突破 50% 以来,<新型城镇化发展规划>将智慧城市列为我国城市发展的三大目标之一,并提出到 2020 年,建成一批特色鲜明的智慧城市.截至现今,全国 ...

随机推荐

  1. to prof. Choi

    Dear Prof. Choi It is my great pleasure to receive your reply ,but terribly sorry for my late reply ...

  2. 分享tiny4412,emmc烧录u-boot, 支持fastboot模式烧写emmc【转】

    本文转载自:http://www.arm9home.net/read.php?tid-80810.html 分享tiny4412,emmc烧录u-boot, 支持fastboot模式烧写emmc   ...

  3. JZOJ 5461 购物 —— 贪心

    题目:https://jzoj.net/senior/#main/show/5461 贪心,原来想了个思路,优先选优惠价最小的 K 个,然后其他按原价排序遍历: 如果当前物品没选过,原价选上,如果选过 ...

  4. JZOJ 1003 [ 东莞市选 2007 ] 拦截导弹 —— 递推

    题目:https://jzoj.net/senior/#main/show/1003 n^2 的话递推就可以啦. 代码如下: #include<iostream> #include< ...

  5. astgo-官方功能更新日志

    2014年9月 2014-9-7:更新 1.安卓.苹果客户端添加字幕广告(点击字幕跳转打开网址) 2.安卓.苹果客户端添加公告推送功能 3.修正Astgo软交换管理平台修删除充值卡.用户账号,造成整个 ...

  6. mvn scope (转)

    策略一: 对于容器提供的(如:servlet-api-2.3等)和测试需要时的(如:junit-3.81等),可以直接在pom.xml中去掉. maven的dependency中有一个tag是< ...

  7. 动态规划DP入门

    百度百科↓ 动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法.20世纪50年代初美国数学家R.E.Bellman ...

  8. wxwidgets安装环境配置

    一:安装VS2012 wxWidgets-2.9.5( 2.95版本为最稳定版本) 二:打开wxWidgets-2.9.5的安装目录,找到build-msw-wx_vc10.sln打开(等待) 三:打 ...

  9. 313 Super Ugly Number 超级丑数

    编写一段程序来寻找第 n 个超级丑数.超级丑数是指其所有质因数都在长度为k的质数列表primes中的正整数.例如,[1, 2, 4, 7, 8, 13, 14, 16, 19, 26, 28, 32] ...

  10. IIS Express配置多站点同时运行

    环境:Win10 Pro.Visual Studio 2015 Community.IIS Express 10 VS2015集成IIS Express,所以无需单独下载, 默认安装位置:C:\Pro ...