ThreeJs 实例教学 -- 3D全景

Posted SegmentFault

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ThreeJs 实例教学 -- 3D全景相关的知识,希望对你有一定的参考价值。

前言

在现在市面上很多全景H5的环境下,要实现全景的方式有很多,可以用css3直接构建也可以用基于threeJs的库来实现,还有很多别的制作全景的软件使用。

本教学适用于未开发过3D全景的工程狮,如果觉得内容太无聊可以直接跳到最后。

理论

整个3D全景所用的相关理论就不多说了,就稍微讲一下本案例用到的相关理论,相信程序猿们会更加关注代码实现的内容。

这次讲解的demo是用css3DRender来构建一个正方体的全景场景。

想象一下,我们需要做的就是构建一个正方体的盒子,然后把镜头放在以下这个正方体盒子里,每个面都贴上我们场景的一个面,那么当镜头转动时看到的就是置身其中的全景。

详细理论的东西以后再说,这次先跑起来一个简单的demo吧

demo解析

本教学用到两个库:threeJS和基于它的CSS3DRender.js,代码是从官网上样例上扒下来做了一点调整。

 
   
   
 
  1. <!DOCTYPE html>

  2. <html>

  3. <head>

  4.    <title>three.js css3d - panorama</title>

  5.    <meta charset="utf-8">

  6.    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">

  7.    <style>

  8.        body {

  9.            background-color: #000000;

  10.            margin: 0;

  11.            cursor: move;

  12.            overflow: hidden;

  13.        }

  14.        .surface { width: 1026px; height: 1026px; background-size: cover; position: absolute; }

  15.        .surface .bg { position: absolute; width: 1026px; height: 1026px; }

  16.    </style>

  17. </head>

  18. <body>

  19. <div>

  20.    <div id="surface_0" class="surface">

  21.        <img class="bg" src="images/posx.jpg" alt="">

  22.    </div>

  23.    <div id="surface_1" class="surface">

  24.        <img class="bg" src="images/negx.jpg" alt="">

  25.    </div>

  26.    <div id="surface_2" class="surface">

  27.        <img class="bg" src="images/posy.jpg" alt="">

  28.    </div>

  29.    <div id="surface_3" class="surface">

  30.        <img class="bg" src="images/negy.jpg" alt="">

  31.    </div>

  32.    <div id="surface_4" class="surface">

  33.        <img class="bg" src="images/posz.jpg" alt="">

  34.    </div>

  35.    <div id="surface_5" class="surface">

  36.        <img class="bg" src="images/negz.jpg" alt="">

  37.    </div>

  38. </div>

  39. <script src="js/three.min.js"></script>

  40. <script src="js/CSS3DRenderer.min.js"></script>

  41. <script src="js/index.js"></script>

  42. </body>

  43. </html>

html这边没什么特别的,首先把每个面放进去,用div把每个面的图片放进去。

没有用官网demo的实现方式是因为官网是create一个img插入到页面,我们在对每个面添加元素的时候不太方便。先把六个面定义好,如果要在每个面上加入一些交互的元素,直接在html上添加dom就可以了。

一共就引入了3个js,除了index另外两个都是压缩过的js,不用关心,看一下index.js的实现:

 
   
   
 
  1. camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1000 );

  2. scene = new THREE.Scene();

那么很明显这两行代码,字面上的意思就是创建了一个相机,创建了一个场景。那这里稍微解释一下这两个类。

PerspectiveCamera

以下是官网的解释:

ThreeJs 实例教学 -- 3D全景

大概意思:这是一个模仿人眼的投影模式,它是用于渲染3D场景最常见的投影模式。

总之这个类就是new一个镜头。下面是样例代码:

ThreeJs 实例教学 -- 3D全景

这个类的构造函数接受四个参数:

ThreeJs 实例教学 -- 3D全景

那么这四个参数具体是什么东西?

ThreeJs 实例教学 -- 3D全景

图片来源:https://isux.tencent.com/3d.html

分别表示:镜头夹角,宽高比,最近焦距,最远焦距。

Scene

接下来,用Scene类创建场景。以下官方说明:

ThreeJs 实例教学 -- 3D全景

这东西创建了一个场景,这个场景允许你对某个东西某个位置通过threeJs渲染场景。创建了场景和相机,我们需要往场景里面放入之前说的正方体。

首先定义好六个面的数据,每个面的位置,3D旋转的旋转角度。

position三个参数分别对应的x,y,z轴的位置。因为我选的面宽度是1024px,所以位置是基于中心点的正负1024/2。

rotation的三个参数分贝对应xyz轴的旋转角度。Math.PI/2代表90度。

 
   
   
 
  1. var sides = [

  2.    {

  3.        position: [ -512, 0, 0 ],//位置

  4.        rotation: [ 0, Math.PI / 2, 0 ]//角度

  5.    },

  6.    {

  7.        position: [ 512, 0, 0 ],

  8.        rotation: [ 0, -Math.PI / 2, 0 ]

  9.    },

  10.    {

  11.        position: [ 0,  512, 0 ],

  12.        rotation: [ Math.PI / 2, 0, Math.PI ]

  13.    },

  14.    {

  15.        position: [ 0, -512, 0 ],

  16.        rotation: [ - Math.PI / 2, 0, Math.PI ]

  17.    },

  18.    {

  19.        position: [ 0, 0,  512 ],

  20.        rotation: [ 0, Math.PI, 0 ]

  21.    },

  22.    {

  23.        position: [ 0, 0, -512 ],

  24.        rotation: [ 0, 0, 0 ]

  25.    }

  26. ];

  27. /**

  28. * 根据六个面的信息,new出六个对象放入场景中

  29. */

  30. for ( var i = 0; i < sides.length; i ++ ) {

  31.    var side = sides[ i ];

  32.    var element = document.getElementById("surface_"+i);

  33.    element.width = 1026; // 2 pixels extra to close the gap.多余的2像素用于闭合正方体

  34.    var object = new THREE.CSS3DObject( element );

  35.    object.position.fromArray( side.position );

  36.    object.rotation.fromArray( side.rotation );

  37.    scene.add( object );

  38. }

CSS3DObject

那么这里有一个新出现的类CSS3DObject,不过这个类不属于官方类,而是我们引用的3DRender库里的类。没有文档我们看一下代码:

 
   
   
 
  1. THREE.CSS3DObject = function (element) {

  2.    THREE.Object3D.call(this);

  3.    this.element = element;

  4.    this.element.style.position = 'absolute';

  5.    this.addEventListener('removed', function (event) {

  6.        if (this.element.parentNode !== null) {

  7.            this.element.parentNode.removeChild(this.element);

  8.            for (var i = 0, l = this.children.length; i < l; i++) {

  9.                this.children[i].dispatchEvent(event)

  10.            }

  11.        }

  12.    })

  13. }

  14. ;

  15. THREE.CSS3DObject.prototype = Object.create(THREE.Object3D.prototype);

可以看出这是一个继承于THREE.Object3D的类,将传入的element的postion改为绝对定位,然后加了个被移除时的事件。

没有定义什么别的特别的东西,那么我们查一下官方Object3D的类。

Object3D

这个类就是一个定义对象的基本类,其中new的对象包含以下两个属性

 
   
   
 
  1. .position

  2. The object's local position.

  3. .rotation

  4. Object's local rotation (see Euler angles), in radians.

分别表示对象的位置和旋转角度。那么for循环就是定义六个对象加入场景中。好,我们继续:

 
   
   
 
  1. renderer = new THREE.CSS3DRenderer();

  2. renderer.setSize( window.innerWidth, window.innerHeight );

  3. document.body.appendChild( renderer.domElement );

CSS3DRenderer

这是我们引用的库里的类,这个类的主要功能是根据three中的场景和镜头的相关信息,使用dom元素和css3D的属性来渲染出来。

在这里只是new了这个类和设置了宽高,但是CSS3DRender在这里还没有开始渲染页面。

 
   
   
 
  1. document.addEventListener( 'mousedown', onDocumentMouseDown, false );

  2. document.addEventListener( 'wheel', onDocumentMouseWheel, false );

  3. document.addEventListener( 'touchstart', onDocumentTouchStart, false );

  4. document.addEventListener( 'touchmove', onDocumentTouchMove, false );

  5. window.addEventListener( 'resize', onWindowResize, false );

这里的事件绑定就不详细说了,接下来解析一下渲染时的代码。

 
   
   
 
  1. animate();

 
   
   
 
  1. function animate() {

  2.    requestAnimationFrame( animate );

  3.    // lat +=  0.1;

  4.    lat = Math.max( - 85, Math.min( 85, lat ) );

  5.    phi = THREE.Math.degToRad( 90 - lat );

  6.    theta = THREE.Math.degToRad( lon );

  7.    target.x = Math.sin( phi ) * Math.cos( theta );

  8.    target.y = Math.cos( phi );

  9.    target.z = Math.sin( phi ) * Math.sin( theta );

  10.    camera.lookAt( target );

  11.    /**

  12.     * 通过传入的scene和camera

  13.     * 获取其中object在创建时候传入的element信息

  14.     * 以及后面定义的包括位置,角度等信息

  15.     * 根据场景中的obj创建dom元素

  16.     * 插入render本身自己创建的场景div中

  17.     * 达到渲染场景的效果

  18.     */

  19.    renderer.render( scene, camera );

  20. }

requestAnimationFrame(animate); 这个方法可以根据帧速率触发animate方法。

 
   
   
 
  1. lat = Math.max( - 85, Math.min( 85, lat ) );

  2.    phi = THREE.Math.degToRad( 90 - lat );

  3.    theta = THREE.Math.degToRad( lon );

  4.    target.x = Math.sin( phi ) * Math.cos( theta );

  5.    target.y = Math.cos( phi );

  6.    target.z = Math.sin( phi ) * Math.sin( theta );

  7.    camera.lookAt( target );

这段代码根据现成的(通过手指滑动或鼠标滑动实时更新的)属性值,调整camera镜头的位置.

 
   
   
 
  1. renderer.render( scene, camera );

然后渲染。

因为render里面的代码比较多,这里就不贴代码了,大概总结一下render做的事情就是首先render自己创建一个作为场景的div,通过传入的scene和camera获取其中object在创建时候传入的element信息以及后面定义的包括位置,角度等信息,根据场景中的obj创建dom元素(就是通过dom实现本应在canvas里的东西),插入render本身自己创建的场景div中。当镜头方向变了,获取到的参数就变了,通过传入的对象身上带有的变化的参数改变页面上dom元素的位置,达到渲染场景的效果。

代码下载

链接: http://pan.baidu.com/s/1eR2Rlb8 密码: sdyt


相关文章推荐



相关讲堂推荐

以上是关于ThreeJs 实例教学 -- 3D全景的主要内容,如果未能解决你的问题,请参考以下文章

threejs+lumion 3D全景图

PCB 全景图技术实现

Threejs:与热点交互的全景图

Three.js 实现VR看房

百度地图threejs相关

panolens.js - 基于threejs全景图库