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

本文分享了使用three.js创建3D全景图的经验,详细介绍了搭建three.js环境、使用OrbitControls.js控件以及实现鼠标操作的方法。通过具体示例,展示了如何加载纹理图片、创建3D模型和设置场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、初识three.js

平时喜欢在各博客看些文章,偶然情况下看到了关于webgl制作的一些相当有趣网站于是便去webgl的官网上了解知识,期间发现three.js这个封装webgl api的库,官网的例子感觉很牛,于是去b站等看了些three.js写的炫酷特效,自己也尝试不看视频的情况下写了一个3D全景图demo,截了demo的上下左右前后的效果图,在页面底部。于是记录下期间收获的一些知识和技巧,如果想看的话可以参考我的typescript版git代码,git地址:链接: git.threeDemo.

二、谈下自己的three.js开发思路

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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值