前言
在上一篇文章简单粗略的描述了开发3D汽车展厅,笔者想再写一篇比较详细的教程。对于笔者来说Three.js说难不难,说简单也不简单。说简单因为他简化了对三维知识的理解,简化了很多操作。说难是因为api很多,要用熟也不是一朝半夕的时间。笔者在这给大家介绍一下以最简单方式打造一个3D汽车展示厅。这个3D汽车展厅实现出来也不算完整,主要想让同学们找找感觉,找些成就感,有感觉自己也有学下去的动力。_
简单粗略了解三维
在2D里只有两个坐标,分别是X轴,和Y轴。在3D就多了一个Z轴。相信刚学3D的同学对X轴和Y轴都比较熟悉,Z轴是比较陌生,笔者建议大家可以上 three编辑器的网站尝试创建一些几何物体,找找对3D理解。
完整效果
需要了解这几个概念
笔者用舞台表演来比如:
- 场景
Sence
相当于在一个舞台,在这里是布置场景物品和表演者表演的地方 - 相机
Carma
相当于观众的眼睛去观看 - 几何体
Geometry
相当于舞台的表演者 - 灯光
light
相当于舞台灯光照射 - 控制
Controls
相当于这出舞台剧的总导演
既然知道这几个概念,我们就根据这几大概念以函数形式区分,就很好理解。在这个three程序我分别创建了:
setScene
、setCarma
、 loadfile
、setLight
、setControls
分别对应以上几个概念
创建场景
首先我们还是用vue3的setup方式编写,npm安装three包, 引入 Scene
,WebGLRenderer
两个对象,创建两个变量 scene
、renderer
并赋值,这样就简单搭建了一个场景,场景背景默认是黑色。创建一个init
初始化函数,并在onMounted
调用
<script setup>
import {
onMounted} from 'vue'
import {
Scene,WebGLRenderer,PerspectiveCamera} from 'three'
let scene,renderer
//创建场景
const setScene = ()=>{
scene = new Scene()
renderer = new WebGLRenderer()
renderer.setSize(innerWidth, innerHeight)
document.querySelector('.boxs').appendChild(renderer.domElement)
}
//初始化所有函数
const init = () => {
setScene()
}
//用vue钩子函数调用
onMounted(init)
</script>
创建相机
有了场景就要加相机,相机相当于人的眼睛去观察几何物体,引入PerspectiveCamera
,
参数有4个,具体可以看看官网文档。然后通过实例方法position.set
设置相机坐标
<script setup>
import {
Scene,WebGLRenderer,PerspectiveCamera} from 'three'
let scene,renderer,camera
//相机的默认坐标
const defaultMap = {
x: 510,
y: 128,
z: 0,
}
//创建场景
const setScene = ()=>{
scene = new Scene()
renderer = new WebGLRenderer()
renderer.setSize(innerWidth, innerHeight)
document.querySelector('.boxs').appendChild(renderer.domElement)
}
//创建相机
const setCamera = () => {
const {
x, y, z} = defaultMap
camera = new PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000)
camera.position.set(x, y, z)
}
//初始化所有函数
const init = () => {
setScene()
setCamera()
}
//用vue钩子函数调用
onMounted(init)
</script>
引入特斯拉模型
在three我们除了可以通过api创建几何物体,还可以引入第三方3d模型,具体可以上 sketchfab ,国外一个3d模型下载网站,里面有很多免费的模型下载,这次用特斯拉汽车模型为例,下载一个gltf
格式的3D模型。引入GLTFLoader
创建一个loadfile
函数并通过Promise返回模型数据,在init
函数加上async
调用loadfile
得到返回模型数据并添加到场景scene
import {
GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader'
import {
Scene,WebGLRenderer,PerspectiveCamera} from 'three'
let scene,renderer,camera,directionalLight,dhelper
let isLoading = ref(true)
let loadingWidth = ref(0)
//相机的默认坐标
const defaultMap = {
x: 510,
y: 128,
z: 0,
}
//创建场景
const setScene = ()=>{
scene = new Scene()
renderer = new WebGLRenderer()
renderer.setSize(innerWidth, innerHeight)
document.querySelector('.boxs').appendChild(renderer.domElement)
}
//创建相机
const setCamera = () => {
const {
x, y, z} = defaultMap
camera = new PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000)
camera.position.set(x, y, z)
}
//通过Promise处理一下loadfile函数
const loadFile = (url) => {
return new Promise(((resolve, reject) => {
loader.load(url,
(gltf) => {
resolve(gltf)
}, ({
loaded, total}) => {
let load = Math.abs(loaded / total * 100)
loadingWidth.value = load
if (load >= 100) {
setTimeout(() => {
isLoading.value = false
}, 1000)
}
console.log((loaded / total * 100) + '% loaded')
},
(err)