【3D商城】三维场景搭建与开发流程
创建一个场景组件
在views中创建一个场景组件Scene.vue并且在router的index.js中,通过路由映射到场景组件
const routes = [
{
path: "/scene",
component: () => import("../views/Scene.vue"),
name: "scene",
},
]
安装 threejs
打开终端,输入
npm i three --save
创建Base3d.js
在目录下创建utils文件夹并且在文件夹下创建文件Base3d.js,在Scene.vue里面导入Base3d.js,如下:
import Base3d from "../utils/Base3d.js";
渲染整个canvas画布,三维的效果立体的事件
<template>
<div class="scene" id="scene"></div>
</template>
<script setup>
import Base3d from "../utils/Base3d.js";
import { reactive, onMounted } from "vue";
const data = reactive({
base3d: {},
});
onMounted(() => {
data.base3d = new Base3d("#scene");
});
</script>
然后在Base3d.js中首先导入threejs
import * as THREE from "three";
导入RGBELoader
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";
初始化场景
在Base3d{}中构建一个constructor(),首先传入一个选择器selector,这个选择器代表我们要去找到一个对象。找到之后把整个画布渲染到对象当中,同时,需要一个摄像头对象(camera)。之所以看到的东西会动,其实物品没有动,只是摄像头在移动,所以看上去才会动,同时需要一个场景(scene),当摄像头和场景真正需要显示画面还需要一个render对象。构建需要初始化init,初始化需要把相机初始化出来,场景初始化出来。
class Base3d {
constructor(selector, onFinish) {
this.container = document.querySelector(selector);
this.camera;
this.scene;
this.renderer;
this.init();
this.animate();
}
init() {
// 初始化场景
this.initScene();
// 初始化相机
this.initCamera();
// 初始化渲染器
this.initRenderer();
}
initScene() {
this.scene = new THREE.Scene(); //构建出一个场景
this.setEnvMap("000");
}
}
初始化相机
在构建相机对象的时候,用透视相机PerspectiveCamera,因为有透视的视角才能看到三维立体的效果,视角通常为45度(屏幕上看上去更加真实),设置浏览器显示的宽高比例来设置相机(window.innerWidth / window.innerHeight),设置最近的距离0.25,最远距离200,设置相机的三维的位置,比如(-1.8,0.6,2.7)
initCamera() {
this.camera = new THREE.PerspectiveCamera( //透视相机
45,
window.innerWidth / window.innerHeight,
0.25,
200
);
this.camera.position.set(-1.8, 0.6, 2.7);
}
初始化渲染器
把画面显示出来需要初始化渲染器,为了更高清一点,避免锯齿使用抗锯齿(antialias),设置屏幕像素比(window.devicePixelRatio),渲染的尺寸大小(window.innerWidth, window.innerHeight)然后进行一个追加,把渲染器渲染的画面,放到选择器上.container.appendChild(this.renderer.domElement) 把渲染器渲染的元素放进来
initRenderer() {
this.renderer = new THREE.WebGLRenderer({ antialias: true });
// 设置屏幕像素比
this.renderer.setPixelRatio(window.devicePixelRatio);
// 渲染的尺寸大小
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.container.appendChild(this.renderer.domElement);
}
添加环境的纹理
用加载器来加载HDR
setEnvMap(hdr) {
new RGBELoader().setPath("./files/hdr/").load(hdr + ".hdr", (texture) => {
this.scene.background = texture;
this.scene.environment = texture;
});
}
现在渲染器已经初始化了但是还没有渲染,需要再写一个渲染函数render(),把摄像头和动画渲染进来,由于页面是不断刷新的需要一帧一帧画出来一帧一帧画出来我们可以做一个请求动画帧,就是不断地去渲染,不断地画出来。只要数据一变就可以画出新的内容,这样就可以形成一个动画,然后通过setAnimationLoop这个循环,进行对render绑定,一帧画完,画下一帧
render() {
this.renderer.render(this.scene, this.camera);
}
animate() {
this.renderer.setAnimationLoop(this.render.bind(this));
}
运行后显示效果如下
但是现在只是一张图没有显示在圆球里面,因此在纹理加载完成后我们需要进行设置纹理的一个映射:
texture.mapping = THREE.EquirectangularReflectionMapping;
setEnvMap(hdr) {
new RGBELoader().setPath("./files/hdr/").load(hdr + ".hdr", (texture) => {
texture.mapping = THREE.EquirectangularReflectionMapping;
this.scene.background = texture;
this.scene.environment = texture;
});
}
这么设置后画面会改过来:
内容正常,但是因为这是一个场景,摄像头看后面的内容是会比较模糊的,并不是因为图不够高清,而是因为摄像头聚焦中间,后面的背景会进行虚化,所以会模糊。