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. JSTL判断list的size()大小,以及choose(相当于if else作用)

    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@ tag ...

  2. luogu 3383【模板】线性筛素数

    我太菜了 %韩神 #include<iostream> #include<cstdio> #include<cmath> #include<cstdlib&g ...

  3. luogu 1939 【模板】矩阵加速(数列)

    题目大意: a[1]=a[2]=a[3]=1 a[x]=a[x-3]+a[x-1] (x>3) 求a数列的第n项%1000000007 思路: 使用矩阵快速幂进行加速 在草稿纸上填了填数 然后就 ...

  4. servlet3.0 JQuary Ajax基本使用

    servlet3.0 没有web.xml文件,需要使用注解进行配置. js: $(document).ready(function(){ $("#btn").click(funct ...

  5. PID204特种部队

    特种部队   题目描述 Description 某特种部队接到一个任务,需要潜入一个仓库.该部队士兵分为两路,第一路士兵已经在正面牵制住了敌人,第二路士兵正在悄悄地从后方秘密潜入敌人的仓库.当他们到达 ...

  6. [Swift通天遁地]七、数据与安全-(3)Cocopods的安装和开源类库对JSON的解析

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  7. 观察者模式-C#实现

    定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新. 观察者模式有四个角色:抽象主题.具体主题.抽象观察者.具体观察者. 抽象主题:把所有观察者对象 ...

  8. JavaScript--如何插入JS

    我们来看看如何写入JS代码?你只需一步操作,使用<script>标签在HTML网页中插入JavaScript代码.注意, <script>标签要成对出现,并把JavaScrip ...

  9. ansible剧本

    yaml简介 YAML是"YAML Ain't a Markup Language"(YAML不是一种置标语言)的递归缩写,早先YAML的意思其实是:"Yet Anoth ...

  10. 自定义View(8)关于measure->onMeasur->setMeasuredDimension及getDefaultSize,resolveSizeAndState

    参考: http://www.cnblogs.com/xiaorenwu702/p/5185436.html 当外层容器组件调用其内部的某个组件view1.measure(xxx)时,view1的on ...