前言

事情是这样的,前几天我接到一个外包工头的新需求,某品牌要搭建一个在线VR展厅,用户可以在手机上通过陀螺仪或者拖动来360度全景参观展厅,这个VR展厅里会有一些信息点,点击之后可以呈现更多信息(视频,图文等)...

image.png

我第一反应是用3D引擎,因为我不久前刚用three.js做过一个BMW的在线展厅,基本把three.js摸熟了。

2021-06-03 11_01_41.gif

会另写一篇文章教大家用threejs做这个[BMW在线DIY],感兴趣的小伙伴请关注我吧~

方案一:WebGL3D引擎

使用3D引擎先搭一个基本的3D场景,下面的演示使用three.js,同类的3D引擎我还调研过babylon.js,playcanvas,使用都差不太多,学会一个基本都通的

var scene, camera, renderer;function initThree(){//场景scene = new THREE.Scene();//镜头camera = new THREE.PerspectiveCamera(90, document.body.clientWidth / document.body.clientHeight, 0.1, 100);camera.position.set(0, 0, 0.01);//渲染器renderer = new THREE.WebGLRenderer();renderer.setSize(document.body.clientWidth, document.body.clientHeight);document.getElementById("container").appendChild(renderer.domElement);//镜头控制器var controls = new THREE.OrbitControls(camera, renderer.domElement);//一会儿在这里添加3D物体loop();
}//帧同步重绘
function loop() {requestAnimationFrame(loop);renderer.render(scene, camera);
}window.onload = initThree;

现在我们能看到一个黑乎乎的世界,因为现在scene里什么都没有,接着我们要把三维物体放进去了,使用3D引擎的实现方式无非都是以下几种

使用立方体(box)实现

这种方式最容易理解,我们在一个房间里,看向天花板,地面,正面,左右两面,背面共计六面。我们把所有六个视角拍成照片就得到下面六张图

image.png

现在我们直接使用立方体(box)搭出这样一个房间

var materials = [];
//根据左右上下前后的顺序构建六个面的材质集
var texture_left = new THREE.TextureLoader().load( './images/scene_left.jpeg' );
materials.push( new THREE.MeshBasicMaterial( { map: texture_left} ) );var texture_right = new THREE.TextureLoader().load( './images/scene_right.jpeg' );
materials.push( new THREE.MeshBasicMaterial( { map: texture_right} ) );var texture_top = new THREE.TextureLoader().load( './images/scene_top.jpeg' );
materials.push( new THREE.MeshBasicMaterial( { map: texture_top} ) );var texture_bottom = new THREE.TextureLoader().load( './images/scene_bottom.jpeg' );
materials.push( new THREE.MeshBasicMaterial( { map: texture_bottom} ) );var texture_front = new THREE.TextureLoader().load( './images/scene_front.jpeg' );
materials.push( new THREE.MeshBasicMaterial( { map: texture_front} ) );var texture_back = new THREE.TextureLoader().load( './images/scene_back.jpeg' );
materials.push( new THREE.MeshBasicMaterial( { map: texture_back} ) );var box = new THREE.Mesh( new THREE.BoxGeometry( 1, 1, 1 ), materials );
scene.add(box);

2021-06-14 19_51_17.gif

好,现在我们把镜头camera(也就是人的视角),放到box内,并且让所有贴图向内翻转后,VR全景就实现了。

box.geometry.scale( 1, 1, -1 );

现在我们进入了这个盒子!!

2021-06-14 19_41_37.gif

threejs官方立方体全景示例

使用球体(sphere)实现

我们将房间360度球形范围内所有的光捕捉到一个图片上,再将这张图片展开为矩形,就能得到这样一张全景图片

image.png
var sphereGeometry = new THREE.SphereGeometry(/*半径*/1, /*垂直节点数量*/50, /*水平节点数量*/50);//节点数量越大,需要计算的三角形就越多,影响性能var sphere = new THREE.Mesh(sphereGeometry);
sphere.material.wireframe  = true;//用线框模式大家可以看得清楚是个球体而不是圆形
scene.add(sphere);

image.png

现在我们把这个全景图片贴到这个球体上

var texture = new THREE.TextureLoader().load('./images/scene.jpeg');
var sphereMaterial = new THREE.MeshBasicMaterial({map: texture});var sphere = new THREE.Mesh(sphereGeometry,sphereMaterial);
// sphere.material.wireframe  = true;

2021-06-14 14_54_38.gif

和之前一样,我们把镜头camera(也就是人的视角),放到球体内,并且让所有贴图向内翻转后,VR全景就实现了

现在我们进入了这个球体!!

var sphereGeometry = new THREE.SphereGeometry(/*半径*/1, 50, 50);
sphereGeometry.scale(1, 1, -1);

2021-06-14 15_15_28.gif

threejs官方球体全景示例

添加信息点

在VR全景中,我们需要放置一些信息点,用户点击之后做一些动作。

现在我们建立这样一个点的数组

var hotPoints=[{position:{x:0,y:0,z:-0.2},detail:{"title":"信息点1"}},{position:{x:-0.2,y:-0.05,z:0.2},detail:{"title":"信息点2"}}
];

遍历这个数组,并将信息点的指示图添加到3D场景中

var pointTexture = new THREE.TextureLoader().load('images/hot.png');
var material = new THREE.SpriteMaterial( { map: pointTexture} );for(var i=0;i<hotPoints.length;i++){var sprite = new THREE.Sprite( material );sprite.scale.set( 0.1, 0.1, 0.1 );sprite.position.set( hotPoints[i].position.x, hotPoints[i].position.y, hotPoints[i].position.z );scene.add( sprite );
}

看到HOT指示图了吗?

2021-06-14 20_22_12.gif

添加点击事件,首先将全部的sprite放到一个数组里

sprite.detail = hotPoints[i].detail;
poiObjects.push(sprite);

然后我们通过射线检测(raycast),就像是镜头中心向鼠标所点击的方向发射出一颗子弹,去检查这个子弹最终会打中哪些物体。

2021-06-15 01_35_14.gif
document.querySelector("#container").addEventListener("click",function(event){event.preventDefault();var raycaster = new THREE.Raycaster();var mouse = new THREE.Vector2();mouse.x = ( event.clientX / document.body.clientWidth ) * 2 - 1;mouse.y = - ( event.clientY / document.body.clientHeight ) * 2 + 1;raycaster.setFromCamera( mouse, camera );var intersects = raycaster.intersectObjects( poiObjects );if(intersects.length>0){alert("点击了热点"+intersects[0].object.detail.title);}
});

方案二:CSS3D

threejs等3d引擎太强大了,这些引擎的代码量都有大几百K,在今天的网速下显得无所谓,但在几年前我接到需求时仍然是重要的考量因素。既然我们只用到3D引擎的一点点功能,那么能否找到一个更加轻量的3D引擎呢。

有!css3d-engine,这个3d引擎只有14kb,并且在多个大牌商业项目中应用

  • 淘宝造物节 https://shrek.imdevsh.com/show/zwj/

  • adidas绝不凋谢 https://shrek.imdevsh.com/show/drose/

  • adidas胜势全开 https://shrek.imdevsh.com/show/bbcny/

  • adidas绝不跟随 https://shrek.imdevsh.com/show/crazylight/

使用skybox实现

window.onload=initCSS3D;function initCSS3D(){var s = new C3D.Stage();s.size(window.innerWidth, window.innerHeight).update();document.getElementById('container').appendChild(s.el);var box = new C3D.Skybox();box.size(954).position(0, 0, 0).material({front: {image: "images/scene_front.jpeg"},back: {image: "images/scene_back.jpeg"},left: {image: "images/scene_right.jpeg"},right: {image: "images/scene_left.jpeg"},up: {image: "images/scene_top.jpeg"},down: {image: "images/scene_bottom.jpeg"},}).update();s.addChild(box);function loop() {angleX += (curMouseX - lastMouseX + lastAngleX - angleX) * 0.3;angleY += (curMouseY - lastMouseY + lastAngleY - angleY) * 0.3;s.camera.rotation(angleY, -angleX, 0).updateT();requestAnimationFrame(loop);}loop();var lastMouseX = 0;var lastMouseY = 0;var curMouseX = 0;var curMouseY = 0;var lastAngleX = 0;var lastAngleY = 0;var angleX = 0;var angleY = 0;document.addEventListener("mousedown", mouseDownHandler);document.addEventListener("mouseup", mouseUpHandler);function mouseDownHandler(evt) {lastMouseX = curMouseX = evt.pageX;lastMouseY = curMouseY = evt.pageY;lastAngleX = angleX;lastAngleY = angleY;document.addEventListener("mousemove", mouseMoveHandler);}function mouseMoveHandler(evt) {curMouseX = evt.pageX;curMouseY = evt.pageY;}function mouseUpHandler(evt) {curMouseX = evt.pageX;curMouseY = evt.pageY;document.removeEventListener("mousemove", mouseMoveHandler);}
}

方案二的好处除了库很小以外,还是div+css来搭建三维场景的。但这个库的作者几乎不维护,遇到问题必须得自己想办法解决,比如使用在电脑上会看到明显的面片边缘

image.png

但是在手机上浏览的话表现还是相当完美的

2021-06-14 22_20_26.gif

添加信息点

我们继续为它添加可交互的信息点

var hotPoints=[{position:{x:0,y:0,z:-476},detail:{"title":"信息点1"}},{position:{x:0,y:0,z:476},detail:{"title":"信息点2"}}
];
function initPoints(){var poiObjects = [];for(var i=0;i<hotPoints.length;i++){var _p = new C3D.Plane();_p.size(207, 162).position(hotPoints[i].position.x,hotPoints[i].position.y,hotPoints[i].position.z).material({image: "images/hot.png",repeat: 'no-repeat',bothsides: true,//注意这个两面贴图的属性}).update();s.addChild(_p);_p.el.detail = hotPoints[i].detail;_p.on("click",function(e){console.log(e.target.detail.title);})}
}

这样就可以显示信息点了,并且由于是div,我们非常容易添加鼠标点击交互等效果

image.png

不过,bothsides属性为true时,背面的信息点图片是反的。

image.png

所以我们这里要做一点处理,根据其与相机的夹角重置一下信息点的旋转角度。(如果是那种怎么旋转都无所谓的图片,比如圆点则无需处理

var r = Math.atan2(hotPoints[i].position.z-0,0-0) * 180 / Math.PI+90;
_p.size(207, 162).position(hotPoints[i].position.x,hotPoints[i].position.y,hotPoints[i].position.z).material({image: "images/hot.png",repeat: 'no-repeat',bothsides: false,}).update();

需求升级了!

以上两个方案,我以为可以给客户交差了。但客户又提出了一些想法

  • 全景图质量需要更高,但加载速度不允许更慢

  • 每个场景的信息点挺多的,坐标编辑太麻烦了

当时我心里想,总共才收你万把块钱,难不成还得给你定制个引擎,再做个可视化编辑器?

直到客户发过来一个参考链接,我看完惊呆了,全景图非常清晰,但首次加载速度极快,像百度地图一样,是一块块从模糊到清晰被加载出来的。

2021-06-14 23_31_28.gif

通过检查参考链接网页的代码,发现了方案三

方案三:pano2vr

image.png

pano2vr是一款所见即所得的全景VR制作软件(正版149欧元),功能挺强大的,可以直接输出成HTML5静态网页,体验非常不错。

而其核心库pano2vr_player.js代码量也只有238kb

image.png

我们可以直接使用这个软件来可视化的添加信息点,输出成HTML5后,除了静态图片以外,所有配置信息都在这个pano.xml文件里

image.png

修改信息点图片

整体的交互体验都非常好,但默认的信息点样式不喜欢,我们可以通过下面的代码来修改信息点图片

pano.readConfigUrlAsync("pano.xml",()=>{var pois=pano.getPointHotspotIds();var hotScale = 0.2;for(var i=0;i<pois.length;i++){var ids=pois[i];var hotsopt=pano.getHotspot(ids);hotsopt.div.firstChild.src="data:images/hot.png";hotsopt.div.firstChild.style.width = 207*hotScale+"px";hotsopt.div.firstChild.style.height = 162*hotScale+"px";hotsopt.div.onmouseover = null;hotsopt.div.setAttribute("ids",ids);hotsopt.div.onclick=function() {//在这里可以响应信息点的点击事件啦console.log(this.getAttribute("ids"));};}
});

哈哈,没想到最终的方案不仅极其简单的就实现了体验良好的VR全景,还附送了非常方便的信息点编辑。除去第一次开发的耗时,以后再制作新的VR场景也就是花个10分钟即可搞定。

但想到外包工头经常压榨我的报价,压缩我的工期,无理变更需求

收到工程款的时候他请我去K歌,坐在KTV的包间里我没有告诉他使用pano2vr的事,而是对他说

每个VR场景的信息点都要花1天时间编辑

每制作一个新的VR场景,你收品牌方8k

我每个场景收你3k,你躺赚5k

毕竟咱们老朋友了,我够意思吧

他豪爽的干掉手中的啤酒说:“好兄弟,我给你唱一首!”

image.png

本故事纯属虚构,文末配图如有侵权,请联系我跟老板大哥喝一杯

查看本文配套视频教程

源码

关注“程序员成长指北”,回复“webvr”获得本文中三种方案的实现源码

如果觉得这篇文章还不错

点击下面卡片关注我

来个【分享、点赞、在看】三连支持一下吧

“分享、点赞、在看” 支持一波

三种前端实现VR全景看房的方案!说不定哪天就用得上!相关推荐

  1. 三种前端实现VR全景看房的方案!说不定哪天就用得上!(收藏)

    前言 事情是这样的,前几天我接到一个外包工头的新需求,某品牌要搭建一个在线VR展厅,用户可以在手机上通过陀螺仪或者拖动来360度全景参观展厅,这个VR展厅里会有一些信息点,点击之后可以呈现更多信息(视 ...

  2. 三种前端实现VR全景看房的方案,收藏吧说不定哪天就用得上

    前言 ===== 事情是这样的,前几天我接到一个外包工头的新需求,某品牌要搭建一个在线VR展厅,用户可以在手机上通过陀螺仪或者拖动来360度全景参观展厅,这个VR展厅里会有一些信息点,点击之后可以呈现 ...

  3. 三种前端实现VR全景看房的方案!

    前言 事情是这样的,前几天我接到一个外包工头的新需求,某品牌要搭建一个在线VR展厅,用户可以在手机上通过陀螺仪或者拖动来360度全景参观展厅,这个VR展厅里会有一些信息点,点击之后可以呈现更多信息(视 ...

  4. vr全景看房的方案 three.js

    WebGL3D引擎 http://www.webgl3d.cn/threejs/docs/#api/zh/core/Raycaster 一.客户端,加载六张图及按钮位置,点击按钮删除旧的图及按钮,加载 ...

  5. VR全景看房的具体优势有哪些

    "从西走到东.看房累断腿"."开车3小时,看房8分钟"......无论您是买房还是租房,都可能面临时间不够.交通不便.房源真实性等因素,让原本兴奋的看房行动,变 ...

  6. VR全景看房线上3D住宅商品房鉴赏

    VR全景看房又称:VR看房.360度/720度全景VR看房.全景3D数字沙盘.3D建模全景看房等等,其实都是全景看房."VR三维全景"是一种可以创建和体验虚拟VR三维空间的计算机3 ...

  7. VR全景说:VR全景看房新体验?如何玩儿转VR房产

    近年来,新型技术可谓是层出不穷(VR全景.AI.大数据等),其中最受人们期待,也大有发展前景的就数VR技术了,为什么这么说呢? 因为VR在经历了2016年的高热度后,VR种类更是层出不穷,如VR游戏. ...

  8. vr全景看房是怎么做的?vr全景看房平台有哪些?

    随着互联网时代的迅速发展,如今VR技术已经渗透到了各行各业,房地产行业也不例外.VR全景看房在房地产行业中普遍使用,VR技术的出现,帮助房产行业打开"新世界的大门",让购房者进入一 ...

  9. 3d怎么把两个面拼接在一起_vr全景看房用什么软件做的?3D看房是怎么拍摄的?...

    以往"看房跑断腿"的时代在vr全景看房出现后就得到了很大的解决,如今疫情和"宅经济"的双重影响下,各行各业不得不寻求线上转型:而vr全景对于房地产等行业便是一个 ...

最新文章

  1. python程序不出结果_超详细的Python入门教程,1小时就可以学会
  2. python项目ImportError: Plotly express requires pandas to be installed.解决方案
  3. linux当卡片机的手机,卡片机不如手机?看完你就不这么想了
  4. window7环境下安装neo4j server版本踩坑记录
  5. CF1039C Network Safety
  6. 使用foreach循环遍历集合元素
  7. mac os x 10.8 安装python-mysqldb
  8. Python学习笔记:利用sorted()函数对序列排序
  9. android 代码 华为 os,Android | 带你零代码实现安卓扫码功能
  10. Codejock Toolkit工具包专业版
  11. 找出冲突的Windows快捷键
  12. 微信支付功能和安装开发软件顺序
  13. Mac——技巧:修复 M1 Mac Mini 蓝牙问题
  14. xsmax进入dfu模式_iPhone如何进入DFU模式
  15. V4L2文档翻译(九)
  16. NinePatch图片
  17. Docker修改无法启动的容器的配置文件
  18. librecad二次开发
  19. python request.get
  20. 脑机接口数据分析工具EEGLAB04---绘制通道光谱图

热门文章

  1. 学习HTML(十五)——结合CSS文件在页中创建按钮
  2. ubuntu系统利用路由器上网设置
  3. manjaro安装php,manjaro 安装后设置
  4. 我的团队,我的产品,我的回忆——演员表
  5. 两道选择题,看完之后我真的只剩下沉默
  6. docker自学入门
  7. c/c++游戏编程之控制台贪吃蛇(一)
  8. python遍历读取文件夹下所有文件内容_python遍历文件夹下所有文件
  9. Linux查看当前ssh登录数,linux /centos 查看ssh登陆日志,从SSH登录情况分析服务器安全...
  10. PHP array合并,PHP array_merge():数组合并