方案一: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)实现
这种方式最容易理解,我们在一个房间里,看向天花板,地面,正面,左右两面,背面共计六面。我们把所有六个视角拍成照片就得到下面六张图
现在我们直接使用立方体(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);
好,现在我们把镜头camera(也就是人的视角),放到box内,并且让所有贴图向内翻转后,VR全景就实现了。
box.geometry.scale( 1, 1, -1 );
现在我们进入了这个盒子!!
使用球体(sphere)实现
我们将房间360度球形范围内所有的光捕捉到一个图片上,再将这张图片展开为矩形,就能得到这样一张全景图片
var sphereGeometry = new THREE.SphereGeometry(/*半径*/1, /*垂直节点数量*/50, /*水平节点数量*/50);//节点数量越大,需要计算的三角形就越多,影响性能 var sphere = new THREE.Mesh(sphereGeometry); sphere.material.wireframe = true;//用线框模式大家可以看得清楚是个球体而不是圆形 scene.add(sphere);
现在我们把这个全景图片贴到这个球体上
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;
和之前一样,我们把镜头camera(也就是人的视角),放到球体内,并且让所有贴图向内翻转后,VR全景就实现了
现在我们进入了这个球体!!
var sphereGeometry = new THREE.SphereGeometry(/*半径*/1, 50, 50); sphereGeometry.scale(1, 1, -1);
添加信息点
在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指示图了吗?
添加点击事件,首先将全部的sprite放到一个数组里
sprite.detail = hotPoints[i].detail; poiObjects.push(sprite);
然后我们通过射线检测(raycast),就像是镜头中心向鼠标所点击的方向发射出一颗子弹,去检查这个子弹最终会打中哪些物体。
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); } });