three.js--3D全景图开发

一、初识three.js 平时喜欢在各博客看些文章,偶然情况下看到了关于webgl制作的一些相当有趣网站于是便去webgl的官网上了解知识,期间发现three.js这个封装webgl api的库,官网的例子感觉很牛,于是去b站等看了些three.js写的炫酷特效,自己也尝试不看视频的情况下写了一个3D全景图demo,截了demo的上下左右前后的效果图,在页面底部。于是记录下期间收获的一些知识和技巧,如果想看的话可以参考我的typescript版git代码,git地址:链接: git.threeDemo.
二、谈下自己的three.js开发思路
【three.js--3D全景图开发】threeJs基本骨架是场景、相机、渲染器。关于这几点可以专门封装一个plugin初始化函数,初始化的时候也有套路,例如
1、场景。初始化即可

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

2、相机。初始化–>设置相机的三维坐标–>设置相机看向的位置
this.camera = new PerspectiveCamera(75, this.width / this.height, 0.1, 1000); this.camera.position.set(0, 0, 10); this.camera.lookAt(this.scene.position);

3、渲染器。初始化–>设置宽高–>设置背景颜色
// 渲染器 this.renderer = new WebGL1Renderer({antialias: true}); this.renderer.setSize(this.width, this.height); this.renderer.setClearColor(0xEEEEEE, 1);

4、3D环境和内容的开发。看第三的讲解 5、执行
// 将场景和相机加到渲染器里 this.renderer.render(this.scene, this.camera); // 最终将渲染好的canvas加入页面 document.getElementById(this.domWrapId).appendChild(this.renderer.domElement);

三、3D环境和内容的开发的思路
思路是先画个小一点正方形盒子(木块),给方块贴上木头的纹理图(利用threeJs的loader模块载入图片,随便去网上搜一张,吐槽一下百度还真不好搜…我去Google搜到的,git里有这块的图片资源),然后再画一个比木块大的盒子(环境),木块和相机在环境内部(这个就相当于黑箱世界了),给环境的上下左右前后贴上天空地面和远方的纹理图,即实现了3D场景。
1、画正方形盒子(木块)并载入图片。初始化几何体–>载入纹理图–>给几何体贴上图–>添加到场景里
// 木箱图片 // !!! 注意这里载入图片的是异步函数,我利用async和await实现同步写法的 const textureLoader = new TextureLoader(); const boxTexture = await textureLoader.load('./static/img/box.jpeg'); // 生成方块 const boxGeometry = new BoxGeometry(2, 2, 2); // 方块贴图, side: DoubleSide是给几何体的内外层都贴图 const meshBasicMaterial = new MeshBasicMaterial({map: boxTexture, side: DoubleSide}); // 合并几何体和图片 const mesh = new Mesh(boxGeometry, meshBasicMaterial); mesh.position.set(1, 1, 1); // 给这个几何体起名字,方便在 this.render()里控制动画旋转 mesh.name = 'box'; // !!! 要记得最后添加到场景里 this.scene.add(mesh);

2、给环境贴图。环境的那个几何体添加多个材质纹理图时,材质数组push纹理图六个面的顺序是:左-右-上-下-前-后
// 生成环境,贴图顺序左右上下前后 const skyGeometry = new BoxGeometry(200, 200, 200); const shyBasicMaterial = []; const skyLeft = await textureLoader.load('./static/img/posx.jpg'); const skyRight = await textureLoader.load('./static/img/negx.jpg'); const skyTop = await textureLoader.load('./static/img/posy.jpg'); const skyBottom = await textureLoader.load('./static/img/negy.jpg'); const skyFront = await textureLoader.load('./static/img/posz.jpg'); const skyBack = await textureLoader.load('./static/img/negz.jpg'); shyBasicMaterial.push( new MeshBasicMaterial({map: skyLeft, side: DoubleSide}), new MeshBasicMaterial({map: skyRight, side: DoubleSide}), new MeshBasicMaterial({map: skyTop, side: DoubleSide}), new MeshBasicMaterial({map: skyBottom, side: DoubleSide}), new MeshBasicMaterial({map: skyFront, side: DoubleSide}), new MeshBasicMaterial({map: skyBack, side: DoubleSide}), ); const skyMesh = new Mesh(skyGeometry, shyBasicMaterial); this.scene.add(skyMesh);

四、引入实现鼠标左键操作、右键平移的控件OrbitControls.js
这里需要单独引入three.js的一个工具文件–OrbitControls.js,其实库里有这个文件(路径在/example/js/controls/下),但是有问题需要改造,所以把它复制出来改造完单独引入即可,改造如下:
// 1、顶部引入THREE依赖 import * as THREE from 'three'; // 2、底部导出这个函数 export const OrbitControls = THREE.OrbitControls;

引入后在0.1.ts这个demo里引入并实例化
// 顶部引入 import {OrbitControls} from '../../assets/js/OrbitControls'; // class函数内部使用 // 初始化的仅一次渲染 private onceRender() { // controls new OrbitControls(this.camera, this.renderer.domElement); this.render(); }

五、完整代码
import { AxesHelper, BoxGeometry, DoubleSide, Mesh, MeshBasicMaterial, PerspectiveCamera, Scene, TextureLoader, WebGL1Renderer } from 'three'; import {OrbitControls} from '../../assets/js/OrbitControls'; import Stats from 'stats.js/src/Stats'; import * as dat from 'dat.gui/build/dat.gui'; class ThreeFn { private scene: Scene; private camera: PerspectiveCamera; private renderer: WebGL1Renderer; private stats: Stats; private domWrapId = 'canvas-frame'; private width = window.innerWidth; private height = window.innerHeight; private guiControls = new function() { this.rotationSpeed = 0.02; }; constructor() { this.runPlugin(); }/*********** 辅助函数 ***********/ private windowResize() { this.renderer.setSize(window.innerWidth, window.innerHeight); }private insertDom() { const el = document.createElement('div'); const body = document.getElementsByTagName('body')[0]; el.id = this.domWrapId; body.appendChild(el); }/*********** three组件 ***********/ private async createBox() { // 木箱图片 const boxGeometry = new BoxGeometry(2, 2, 2); const textureLoader = new TextureLoader(); const boxTexture = await textureLoader.load('./static/img/box.jpeg'); // 生成方块 const meshBasicMaterial = new MeshBasicMaterial({map: boxTexture, side: DoubleSide}); const mesh = new Mesh(boxGeometry, meshBasicMaterial); mesh.position.set(1, 1, 1); // 给这个几何体起名字,方便在 this.render()里控制动画旋转 mesh.name = 'box'; this.scene.add(mesh); // 生成环境,贴图顺序左右上下前后 const skyGeometry = new BoxGeometry(200, 200, 200); const shyBasicMaterial = []; const skyLeft = await textureLoader.load('./static/img/posx.jpg'); const skyRight = await textureLoader.load('./static/img/negx.jpg'); const skyTop = await textureLoader.load('./static/img/posy.jpg'); const skyBottom = await textureLoader.load('./static/img/negy.jpg'); const skyFront = await textureLoader.load('./static/img/posz.jpg'); const skyBack = await textureLoader.load('./static/img/negz.jpg'); shyBasicMaterial.push( new MeshBasicMaterial({map: skyLeft, side: DoubleSide}), new MeshBasicMaterial({map: skyRight, side: DoubleSide}), new MeshBasicMaterial({map: skyTop, side: DoubleSide}), new MeshBasicMaterial({map: skyBottom, side: DoubleSide}), new MeshBasicMaterial({map: skyFront, side: DoubleSide}), new MeshBasicMaterial({map: skyBack, side: DoubleSide}), ); const skyMesh = new Mesh(skyGeometry, shyBasicMaterial); this.scene.add(skyMesh); // 初始化的仅一次渲染 this.onceRender(); }/*********** 执行函数 ***********/ // 初始化配置 private runPlugin() { // createDom this.insertDom(); // 场景 this.scene = new Scene(); // 相机 this.camera = new PerspectiveCamera(75, this.width / this.height, 0.1, 1000); this.camera.position.set(0, 0, 10); this.camera.lookAt(this.scene.position); // 渲染器 this.renderer = new WebGL1Renderer({antialias: true}); this.renderer.setSize(this.width, this.height); this.renderer.setClearColor(0xEEEEEE, 1); // 坐标 const axesHelper = new AxesHelper(100); this.scene.add(axesHelper); // 生成其它相关threeJs组件 this.createBox(); } // 初始化的仅一次渲染 private onceRender() { // fps显示器 this.stats = new Stats(); this.stats.showPanel(0); window.document.body.appendChild(this.stats.dom); // dat.gui const gui = new dat.GUI(); gui.add(this.guiControls, 'rotationSpeed', 0, 0.5); // controls new OrbitControls(this.camera, this.renderer.domElement); this.render(); } // 渲染到浏览器 private render() { this.stats.begin(); window.requestAnimationFrame(() => { this.render() }); const box = this.scene.getObjectByName('box'); box.rotation.x += this.guiControls.rotationSpeed; box.rotation.y += this.guiControls.rotationSpeed; box.rotation.z += this.guiControls.rotationSpeed; this.renderer.render(this.scene, this.camera); this.stats.end(); }// 执行渲染 public run() { document.getElementById(this.domWrapId).appendChild(this.renderer.domElement); window.addEventListener('resize', () => { this.windowResize(); }); } }const threeFn = new ThreeFn(); threeFn.run();

六、demo上下左右前后效果图
three.js--3D全景图开发
文章图片

three.js--3D全景图开发
文章图片

three.js--3D全景图开发
文章图片

three.js--3D全景图开发
文章图片

three.js--3D全景图开发
文章图片

three.js--3D全景图开发
文章图片

    推荐阅读