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上下左右前后效果图
文章图片
文章图片
文章图片
文章图片
文章图片
文章图片