Three kinds of front-end VR panoramic viewing solutions, bookmark it, maybe it will be useful someday

picture

foreword

=====

The thing is like this, I received a 外包工头new request a few days ago. A certain brand wants to build an online VR showroom. Information point, after clicking, more information (video, graphics, etc.) can be presented...

picture

image.png

My first reaction was to use a 3D engine, because I just used three.jsan BMWonline showroom not long ago, and I am basically three.jsfamiliar with it.

picture

2021-06-03 11_01_41.gif

I will write another article to teach you to use threejs to make this [BMW Online DIY], please follow me if you are interested~

Solution 1: WebGL3D engine

Use a 3D engine to build a basic 3D scene. The following demonstration uses three.js. I have also researched babylon.js and playcanvas for similar 3D engines. They are all similar in use, and I can learn a basic one.

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;

Now we can see a dark world, because scenethere is nothing in it now, and then we have to put three-dimensional objects in it. The implementation methods of using a 3D engine are nothing more than the following

Realized using a cube (box)

This way is the easiest to understand. We are in a room, looking at the ceiling, the ground, the front, the left and right sides, and the back, a total of six sides. We take all six angles of view as photos to get the following six pictures

picture

image.png

Now we directly use the cube (box) to build such a room

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);

picture

2021-06-14 19_51_17.gif

Ok, now we put the lens camera (that is, the human perspective) into the box, and after flipping all the textures inward, the VR panorama is realized.

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

Now we're inside the box! !

picture

2021-06-14 19_41_37.gif

threejs official cube panorama example

Implemented using a sphere

We capture all the light in the 360-degree spherical range of the room onto a picture, and then expand the picture into a rectangle to get such a panoramic picture

picture

image.png

var sphereGeometry = new THREE.SphereGeometry(/*半径*/1, /*垂直节点数量*/50, /*水平节点数量*/50);//节点数量越大,需要计算的三角形就越多,影响性能

var sphere = new THREE.Mesh(sphereGeometry);
sphere.material.wireframe  = true;//用线框模式大家可以看得清楚是个球体而不是圆形
scene.add(sphere);

picture

image.png

Now let's paste this panorama onto this 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;

picture

2021-06-14 14_54_38.gif

As before, we put the lens camera (that is, the perspective of the person) into the sphere, and after flipping all the textures inward, the VR panorama is realized

Now we are inside this sphere! !

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

picture

2021-06-14 15_15_28.gif

threejs official sphere panorama example

Add information points

In the VR panorama, we need to place some information points, and the user will do some actions after clicking.

Now we create an array of points like this

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"
        }
    }
];

Traverse this array and add the indication map of the information point to the 3D scene

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 );
}

See the HOT instruction map?

picture

2021-06-14 20_22_12.gif

Add a click event, first put all sprites into an array

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

Then we pass ray detection (raycast), which is like launching a bullet from the center of the lens to the direction where the mouse is clicked, to check which objects the bullet will eventually hit.

picture

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);
    }
});

Solution 2: CSS3D

threejsWhen the 3D engine is too powerful, the code size of these engines is hundreds of K, which does not matter at today's Internet speed, but it was still an important consideration when I received the demand a few years ago. Since we only use a little function of the 3D engine, can we find a lighter 3D engine?

have! css3d-engine, this 3d engine is only 14kbused in many big-name commercial projects

  • Taobao Creation Festival https://shrek.imdevsh.com/show/zwj/

  • adidas will never wither https://shrek.imdevsh.com/show/drose/

  • adidas is in full swing https://shrek.imdevsh.com/show/bbcny/

  • adidas will never follow https://shrek.imdevsh.com/show/crazylight/

Use skybox to achieve

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);
    }
}

The advantage of option two is that apart from the small library, it is still div+css to build a three-dimensional scene. However, the author of this library hardly maintains it. When encountering problems, you have to find a way to solve it yourself. For example, you will see obvious mesh edges when using it on a computer.

picture

image.png

But when browsing on mobile phones, the performance is quite perfect.

picture

2021-06-14 22_20_26.gif

Add information points

We continue to add interactive information points to it

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,//Pay attention to the properties of this two-sided map         }).update();         s.addChild(_p);




_p.el.detail = hotPoints[i].detail;

_p.on(“click”,function(e){
            console.log(e.target.detail.title);
        })
    }
}


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

![图片](https://mmbiz.qpic.cn/mmbiz_png/t1ynS50Irh3H6sn2Pc31b8LLcjL2Ht5ZoZiaHPARu2sZK6j80sU6cON6FlsmeicUVCT1MXH939IfolSwpXwIYZibQ/640?wx_fmt=png)

image.png

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

![图片](https://mmbiz.qpic.cn/mmbiz_png/t1ynS50Irh3H6sn2Pc31b8LLcjL2Ht5ZzSdsibZk0RFGWeeIAtkhBp0d7Vw5UZibSOQCKt4RymZOvj9U8NL56QoA/640?wx_fmt=png)

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();


需求升级了!
======

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

*   **全景图质量需要更高,但加载速度不允许更慢**
    
*   **每个场景的信息点挺多的,坐标编辑太麻烦了**
    

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

![图片](https://mmbiz.qpic.cn/mmbiz_png/t1ynS50Irh3H6sn2Pc31b8LLcjL2Ht5ZuvWUIWrbxbxPic6eENNodMP4WEicexf8FVPyJvE1uPTBZrPdjzUoPCZA/640?wx_fmt=png)

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

![图片](https://mmbiz.qpic.cn/mmbiz_gif/t1ynS50Irh3H6sn2Pc31b8LLcjL2Ht5Zic1oKjJhuYVpDBhPOWYiaXYmuo2IVV5U78CyKCTkibUlgnbMlRYQI8u8A/640?wx_fmt=gif)

2021-06-14 23\_31\_28.gif

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

方案三:pano2vr
===========

![图片](https://mmbiz.qpic.cn/mmbiz_png/t1ynS50Irh3H6sn2Pc31b8LLcjL2Ht5Z3WzDvhFfhGYnFsSk0De26v1icBO2RUmDXyh2hjR0FNPRhNr2vS8xhBw/640?wx_fmt=png)

image.png

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

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

![图片](https://mmbiz.qpic.cn/mmbiz_png/t1ynS50Irh3H6sn2Pc31b8LLcjL2Ht5Z3OhqPFq0TuMjjdv8QFdIzSqt10YAM4wWI2D366NBpY9YKw2vAauA9g/640?wx_fmt=png)

image.png

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

![图片](https://mmbiz.qpic.cn/mmbiz_png/t1ynS50Irh3H6sn2Pc31b8LLcjL2Ht5Zy0tBacniaJPXAbELib4DibId7wELeeDxOzGNyBTL32qVhkf51PObkNAkQ/640?wx_fmt=png)

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="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() {                    // Here you can respond to the click event of the information point                    console.log(this.getAttribute(“ids”));             };     } });













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

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

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

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

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

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

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

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

![图片](https://img-blog.csdnimg.cn/img_convert/e10d17c70a0e55f074f88a6ebe4ecfd3.png)

image.png

* * *

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

查看本文配套视频教程

源码
==

微信搜索并关注“`大帅老猿`”,回复“`webvr`”获得本文中三种方案的实现源码

> 如果觉得我是个有趣的程序员,请关注我;如果觉得本文还不错,记得点赞收藏哦,说不定哪天就用得上!

Guess you like

Origin blog.csdn.net/ezshine/article/details/124312151