接触webGl(three.js)之全景(VR)看房的实现旅程总结

文章目录


前言

这几天可能是机缘巧合吧,我朋友让我帮他弄一下全景(VR)看房功能(类似贝壳看房不过最后做不出来....),这时候我默认大家都了解了什么是OpenGL,什么是WebGL,以及Three.js。不过不了解也关系,我是从萌新角度去介绍如何开发的(引导开发式)。


实现全景(VR)看房的方式

让我介绍一下我这几天自己琢磨的方式吧,虽然很一般都是对第一次接触的人还是有很大的帮助。

一、纯Three.js方式

1.安装包

npm install three

// 过渡动画
npm install tween

2.实现的业务代码

代码(vue+three)简单实现了全景看房,以及点击获取3维坐标位置并把相机移动过去。

<template>
  <div>
      <div id="container" ref="container"></div>
  </div>
</template>

<script>
  import * as THREE from 'three';
  import TWEEN from '@tweenjs/tween.js'
  import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"
  export default {
    data () {
        return {
            bigImg: require("../../assets/1.png"), // 图片路径
            container: null, // 页面容器
            camera: null, // 相机
            renderer: null, // 渲染器
            scene: null, // 场景
            material: null,  // 添加材质
            texture: null,// 创建纹理贴图
            skyBox: null, // 网格
            controls: null, // 轨道控制
            clock: null, // 轨道更新时间
            // 鼠标属性
            bMouseDown: false,
            x: -1,
            y: -1,
            isClickCamera: false, // 是否点运动相机
            raycaster: null,
            mouse: null
        }
    },
    mounted () {
        this.$nextTick(() => {
            this.init();
            this.animate();
        })
    },
    created () {
    },
    methods: {
        // 初始化轨道控制
        initControls() {
            this.controls = new OrbitControls(this.camera, this.renderer.domElement);
            this.controls.target = new THREE.Vector3(0, 0, 0);
            this.controls.minDistance = 18; // 相机最近
            this.controls.maxDistance = 500; // 相机最远
            this.controls.autoRotate = false; // 图片自动旋转
            this.controls.enableDamping = true; // 使动画循环使用时阻尼或自转 意思是否有惯性
            this.controls.enablePan = false; // 是否开启右键拖拽
            this.controls.autoRotateSpeed = 0.5; // 阻尼系数
        },
        init() {
            // 页面容器
            this.container = document.getElementById('container');

            // 创建渲染器
            this.renderer = new THREE.WebGLRenderer();
            this.renderer.setPixelRatio(window.devicePixelRatio);

            // 设置画布的宽高
            this.renderer.setSize(window.innerWidth, window.innerHeight);

            // 判断容器中子元素的长度
            let childs = this.container.childNodes;
            if (this.container.childNodes.length > 0) {
                this.container.removeChild(childs[0]);
                this.container.appendChild(this.renderer.domElement);
            } else {
                this.container.appendChild(this.renderer.domElement);
            }
            // container.appendChild(renderer.domElement);

            // 创建场景
            this.scene = new THREE.Scene();

            // 创建相机
            this.camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 1, 10000);
            this.camera.position.set(5, 0, 0);
            this.camera.lookAt(new THREE.Vector3(0, 0, 0)); //让相机指向原点 

            // 创建轨道控制器
            this.initControls();

            // 添加材质
            this.material = new THREE.MeshBasicMaterial();
            // 创建纹理贴图
            this.texture = new THREE.TextureLoader().load(this.bigImg);
            this.material.map = this.texture;

            // 创建网格对象
            this.skyBox = new THREE.Mesh(new THREE.SphereBufferGeometry(100, 100, 100), this.material);
            this.skyBox.geometry.scale(1, 1, -1);
            // 显示坐标光线
            var axisHelper = new THREE.AxisHelper(600); // 显示光线(红色代表X轴,绿色代表Y轴,蓝色代表Z轴)
            // 添加到场景中去
            this.scene.add(axisHelper);
            this.scene.add(this.skyBox);
        
            // 鼠标事件监听
            this.renderer.domElement.addEventListener('pointerdown', this.onMouseDown, false);
            this.renderer.domElement.addEventListener('pointerup', this.onMouseUp, false);
            this.renderer.domElement.addEventListener('pointermove', this.onMouseMove, false);

            // 监听布局变化
            window.addEventListener('resize', this.onWindowResize, false);

           
        },
         // 更新相机动画
        tweenCamera(position, target) {
            new TWEEN.Tween(this.camera.position).to({
                x: position.x,
                y: position.y,
                z: position.z
            }, 600).easing(TWEEN.Easing.Sinusoidal.InOut).start();

            new TWEEN.Tween(this.controls.target).to({
                x: target.x,
                y: target.y,
                z: target.z
            }, 600).easing(TWEEN.Easing.Sinusoidal.InOut).start();
        },
        // 鼠标按下
        onMouseDown(event) {
            event.preventDefault(); // 取消默认事件
            console.log("---onMouseDown---");
            this.isClickCamera = true;
        },
        // 鼠标放开
        onMouseUp(event) {
            event.preventDefault(); // 取消默认事件
            console.log("---onMouseUp---");
            if (this.isClickCamera) {
                console.log("---移动相机---", event);
                // 红色代表X轴,绿色代表Y轴,蓝色代表Z轴
                this.mouse = new THREE.Vector3(); // 三维坐标对象
                // 屏幕坐标到标准化设备坐标(Normalized Device Coordinates, NDC)转换
                this.mouse.set((event.clientX / window.innerWidth ) * 2 - 1, - (event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
                this.mouse.unproject(this.camera);

                this.raycaster = new THREE.Raycaster(this.camera.position, this.mouse.sub(this.camera.position).normalize()); // 投手
                var intersects = this.raycaster.intersectObjects(this.scene.children);
                
                if (intersects.length > 0) {
                    var selected = intersects[0]; // 取第一个物体
                    console.log("x坐标:"+ selected.point.x);
                    console.log("y坐标:"+ selected.point.y);
                    console.log("z坐标:"+ selected.point.z);

                    // var direction = new THREE.Vector3();
                    // this.camera.getWorldDirection( direction );
                    // this.camera.position.add( direction );
                    // this.camera.position.add(direction.multiplyScalar(selected.point.x, selected.point.y, selected.point.z) );
                    // this.camera.lookAt(new THREE.Vector3(selected.point.x, selected.point.y, selected.point.z)); //让相机指向原点 
                    this.camera.position.set(selected.point.x, selected.point.y, selected.point.z);

                    // this.tweenCamera(selected.point, selected.point);
                }
            }
        },
        // 鼠标移动
        onMouseMove(event) {
            event.preventDefault(); // 取消默认事件
            console.log("---onMouseMove---");
            this.isClickCamera = false;
        },
        onWindowResize() {
            // 窗口缩放的时候,保证场景也跟着一起缩放
            this.camera.aspect = window.innerWidth / window.innerHeight;
            this.camera.updateProjectionMatrix();
            this.renderer.setSize(window.innerWidth, window.innerHeight);
        },
        animate() {
            requestAnimationFrame(this.animate);
            this.controls.update(); // 更新轨道控制
            TWEEN.update();
            this.renderer.render(this.scene, this.camera);
        }
    }
  }
</script>

<style scoped>

</style>

3.实现演示

ps:动态图太大上传不了只能截图

二、 使用photo-sphere-viewer

1.安装包

npm install photo-sphere-viewer

2.实现的业务代码

代码(vue+photo-sphere-viewer)简单实现了全景看房。

<template>
  <div id="PSViewer"></div>
</template>

<script>
  import 'photo-sphere-viewer/dist/photo-sphere-viewer.css'
  import { Viewer } from 'photo-sphere-viewer';
  import MarkersPlugins from 'photo-sphere-viewer/dist/plugins/markers';
  export default {
    data () {
      return {
        factoryLink: require('../../assets/1.png'),
        redPin: require('../../assets/pin-red.png'),
        viewer: null
      }
    },
    mounted () {
      this.initPhotoSphere()
    },
    created () {
    },
    methods: {
      initPhotoSphere() {
        this.viewer = new Viewer({
          container: document.querySelector('#PSViewer'),
          plugins: [
            [MarkersPlugins, {
              markers: [ 
                {
                  id: 'image',
                  longitude: 30.32,
                  latitude: 30.11,
                  image: this.redPin,
                  width: 32,
                  height: 32
                },
              ],
            }], 
          ],
          panorama: this.factoryLink,
        })
        const markersPlugin = this.viewer.getPlugin(MarkersPlugins);
        markersPlugin.on('image', (e, marker) => {
          console.log("---点击---")
          markersPlugin.updateMarker({
            id: marker.id,
            image: this.redPin,
          });
        });
      }
    }
  }
</script>

<style scoped>
 #PSViewer {
  width: 100vw;
  height: 60vh;
}
</style>

3.实现演示

三、pano2vr实现

ps:这是一个设计软件

1.下载软件

链接: https://pan.baidu.com/s/1hbkGuCJxWgddvgc0xcUdAQ 
提取码: 6kpp
解压密码:www.zhenxz.com

 

2.安装软件

正常安装一路同意下一步即可

安装完成之后把 破解文件 pano2vr.exe   放置到你安装的目录里面  覆盖替换

然后 断开网络  接着 打开软件,输入 许可证秘钥

BYuu*vuG:2QxYky3mJLgxTxdChU)wezBmKJPXxUt!rrm*iz8LmeduVgGJhfKGRMe*ChYmqYVcXqb:icz3iD*cWp*nHUnfVk(K2k4Zz4G3C)AfGcg4rq4q49fnRZFUdaj

3.实现演示

我用这软件做了一个案例,其实很简单主要是大家要学会 皮肤制作,别的都很简单。

四、krpano方式

krpano案例

当前案例

五、其他方式

肯定还有别的方式的,只是我这边就不做多的演示了,不过我是建议大家还是使用第一种方式进行深入开发(纯Three.js)。


总结

OpenGL是一套规范,不是接口,学习这套规范,就可以在支持 OpenGL 的机器上正常使用这些规范,在显示器上看到绘制的结果。

WebGL 是一种 3D 绘图标准,这种绘图技术标准把 JavaScript 和 OpenGL ES 2.0 结合在一起,通过 HTML5 的 Canvas 来和 DOM 打交道,为HTML5 Canvas 提供硬件 3D 加速渲染。WebGL 技术标准免去了开发网页专用渲染插件的麻烦,可被用于创建具有复杂 3D 结构的网站页面,甚至可以用来设计 3D 网页游戏等。

Three.js是一款webGL框架,由于其易用性被广泛应用。Three.js在WebGL的api接口基础上,又进行的一层封装。

哈哈哈~理论还是要有的,今天我介绍了三种实现方式,具体看你业务需求需要怎么实现。

猜你喜欢

转载自blog.csdn.net/echo_Ae/article/details/113534075