超简单,threejs+vue3+vite创建一个物体试试

426 阅读4分钟

使用vite创建vue3项目

这里我相信绝大多数前端开发者都会创建,就不过多介绍了

  • npm create vite study-three
  • cd study-three
  • npm i
  • npm run dev
  • npm i three @types/three(下载threejs和相关的类型支持包)

创建物体

举例

首先举一个例子,我们如果在漆黑的晚上在一个密闭的房间里面观察一个物体是看不见的,如果想看见这个物体必须要有灯光才行。这个例子里面有物体、灯光,我们人脑如果要识别这这个物体需要经过眼睛和视网膜,视网膜的作用就是成像。
以上例子中与threejs中一些对象的对应关系:

  • 物体: Mesh
  • 人眼: Camera
  • 视网膜: Render
  • 房间:Scene

有了以上几个关键对象我们才能看到物体,继续往下看我们要使用threejs实现了。

threejs使用

由于只是一个简单的dome我就不写的太严谨了,直接在App.vue里面写代码了。 先把threejs引入进来。

<script setup lang="ts">
import * as THREE from 'three'
</script>

继续往下写,创建一个对象里面存我们要使用的一些对象

interface IThreeObj {
    scene: THREE.Scene,
    camera: THREE.PerspectiveCamera,
    cube: THREE.Mesh,
    renderer: THREE.WebGLRenderer,
    ambient: THREE.AmbientLight,
    deriction: THREE.DirectionalLight
}

const ThreeObj = {} as IThreeObj

这里我解释一下这几个属性的含义:

  • scene, 这个就是场景,相当于之前例子里的房间,里面放物体灯光等东西
  • camera,这个相当于人眼
  • cube,相当于物体
  • renderer,就是渲染器
  • ambient,环境光。就相当于普通晴天的时候房间里面的光,物体的投影几乎看不见
  • deriction,平行光。相当于一个台灯照射出的光,投到物体上会出现投影

获取浏览器的宽高

const width = window.innerWidth
const height = window.innerHeight

创建一个正方体:

const createBox = () => {
    const gemory = new THREE.BoxGeometry(50, 50, 50)
    const material = new THREE.MeshLambertMaterial({ color: 0x00ff00 })
    const cube = new THREE.Mesh(gemory, material)
    cube.position.set(0, 0, 0)
    ThreeObj.cube = cube
}
  • gemory: 一个物体的外骨架
  • material:物体的材质
  • new THREE.Mesh(gemory, material): 通过传入骨架和材质创建一个物体,这非常合理,骨架外面贴上材质就是一个物体了。
  • cube.position.set(0, 0, 0):设置物体在0,0,0这个位置,这就是xyz坐标的原点

创建相机

const createCamera = () => {
    const camera = new THREE.PerspectiveCamera(75, width / height, 1, 1000)
    camera.position.set(200, 200, 200)
    camera.lookAt(ThreeObj.cube.position)
    ThreeObj.camera = camera
}

PerspectiveCamera的四个参数fov, aspect, near, far构成一个四棱台3D空间,被称为视锥体,只有视锥体之内的物体,才会渲染出来

image.png

camera.lookAt(ThreeObj.cube.position)是设置相机的聚焦点,这里直接放了物体的位置,这和合理,我们看的就是这个物体。

创建一个渲染器

 <div id="three"></div>
const createRenderer = () => {
    const renderer = new THREE.WebGLRenderer({
        antialias: true,
    })
    renderer.setPixelRatio(window.devicePixelRatio)
    renderer.setClearColor(0x000000)
    renderer.setSize(width, height)
    document.getElementById('three')?.appendChild(renderer.domElement)
    ThreeObj.renderer = renderer
}
  • WebGLRenderer里面的这个参数antialais设置成true就是开启抗锯齿
  • setPixelRatio用于设置渲染器的像素比,使其适应设备的像素比。这样可以确保在不同设备上都能获得清晰的渲染效果。
  • setClearColor设置背景颜色
  • setSize设置渲染器的大小
  • document.getElementById('three')?.appendChild(renderer.domElement)这段代码就是获取div元素将渲染器做为子元素加进去

创建场景里的元素

const create = () => {
    createBox()
    createCamera()
    createRenderer()
    createLight()
}

这段代码没什么好说的,就是调用函数创建相关的对象 初始化场景

const initScene = () => {
    const scene = new THREE.Scene()
    scene.add(ThreeObj.cube)
    scene.add(ThreeObj.ambient)
    scene.add(ThreeObj.deriction)
    ThreeObj.scene = scene
}
  • new THREE.Scene()创建一个场景对象
  • add将之前创建的灯光和物体加到场景里面

开始渲染

const render = () => {
    initScene()
    ThreeObj.renderer.render(ThreeObj.scene, ThreeObj.camera)
}
  • 这段代码注意调用顺序,只有先调用初始化场景之后才有相机、场景这两个对象
  • ThreeObj.renderer.render(ThreeObj.scene, ThreeObj.camera),将眼睛(camera)和屋子(scene)做为参数传到render函数里进行渲染。

完整代码

<script setup lang="ts">
import * as THREE from 'three'
import { onMounted } from 'vue';

const ThreeObj = {} as IThreeObj

const width = window.innerWidth
const height = window.innerHeight

const createBox = () => {
    const gemory = new THREE.BoxGeometry(50, 50, 50)
    const material = new THREE.MeshLambertMaterial({ color: 0x00ff00 })
    const cube = new THREE.Mesh(gemory, material)
    cube.position.set(0, 0, 0)
    ThreeObj.cube = cube
}

const createCamera = () => {
    const camera = new THREE.PerspectiveCamera(75, width / height, 1, 1000)
    camera.position.set(200, 200, 200)
    camera.lookAt(ThreeObj.cube.position)
    ThreeObj.camera = camera
}

const createRenderer = () => {
    const renderer = new THREE.WebGLRenderer({
        antialias: true,
    })
    renderer.setPixelRatio(window.devicePixelRatio)
    renderer.setClearColor(0x000000)
    renderer.setSize(width, height)
    document.getElementById('three')?.appendChild(renderer.domElement)
    ThreeObj.renderer = renderer
}

const createLight = () => {
    const ambient = new THREE.AmbientLight(0xffffff)
    const deriction = new THREE.DirectionalLight(0xffffff, 1)
    deriction.position.set(100, 200, 50)
    ThreeObj.ambient = ambient
    ThreeObj.deriction = deriction
}

const create = () => {
    createBox()
    createCamera()
    createRenderer()
    createLight()
}

const initScene = () => {
    const scene = new THREE.Scene()
    scene.add(ThreeObj.cube)
    scene.add(ThreeObj.ambient)
    scene.add(ThreeObj.deriction)
    ThreeObj.scene = scene
}

const render = () => {
    initScene()
    ThreeObj.renderer.render(ThreeObj.scene, ThreeObj.camera)
}

onMounted(() => {
    create()
    render()
})
</script>

<template>
    <div id="three"></div>
</template>

最后看效果

image.png