Vue3集成ThreeJS实现3D效果,threejs+Vite+Vue3+TypeScript 实战课程【一篇文章精通系列】

在这里插入图片描述

Vue3集成ThreeJS实现3D效果,threejs+Vite+Vue3+TypeScript 实战课程【一篇文章精通系列】

    • 项目简介
    • 一、项目初始化
      • 1、添加一些依赖项
    • 二、创建3D【基础搭建】
      • 1、绘制板子,立方体,球体
      • 2、材质和光照
      • 3、材质和光照和动画
      • 4、性能监控
      • 5、交互控制
      • 6、响应窗口变化
    • 三、基础场景搭建
      • 1、创建基础场景【实现添加几何体和删除几何体】
      • 2、实现雾化场景
      • 3、重写材质
      • 4、常见几何体
      • 5、修改几何体属性
      • 6、相机切换
      • 7、相机跟随
    • 四、光照
      • 1、环境光
      • 2、点光源
      • 3、聚光灯
      • 4、平行光
      • 5、半球光
    • 五、小车案例
      • 1、基础环境搭建
      • 2、载入模型,实现轨道控制器
      • 3、实现模型颜色材质调整,轮子转动
      • 4、源代码下载

项目简介

这是一个使用Vue3,TypeScript,Vite和Three.js的项目。Vue3是一个流行的JavaScript框架,用于构建用户界面。TypeScript是一种静态类型的编程语言,它是JavaScript的超集,可以编译成纯JavaScript。Vite是一个由Evan You开发的新的前端构建工具,能够提供快速的冷启动和即时热更新。

Three.js是一个轻量级的3D库,能够让我们在任何浏览器中创建和显示动画的3D计算机图形。在该项目中,我们将Three.js集成到了Vue3和TypeScript的环境中,使得我们可以在Vue组件中使用Three.js来创建3D图形。

此外,项目中还可能包含一些封装了Three.js的代码,以便于更方便的使用Three.js进行3D开发。

这样的技术组合可以让我们在前端环境中实现复杂的3D可视化效果,为项目增加更丰富的视觉体验。

🔸3D模型下载网站:https://sketchfab.com/feed
🔸3D人物动作绑定:www.mixamo.com
🔸3D角色生产工具:https://readyplayer.me/
🔸模型压缩网站:gltf.report
🔸查找天空背景:google key words: equirectangular sky / skybox background
🔸材质贴图素材:https://www.textures.com
🔸hdr素材库(环境贴图): https://polyhaven.com
🔸二次元风3D角色生产软件VRoid Studio: https://vroid.com/en/studio

🕹Sketchfab公用账号:
Login: lingo3dchina@gmail.com
PW: Lingo3dxoxo
Code:640841

一、项目初始化

npm install -g vitenpm init vite@latest threejs-vite-vue -- --template vuecd threejs-vite-vuenpm installnpm run dev

项目创建成功
在这里插入图片描述注意threejs的版本

在这里插入图片描述 "@types/three": "^0.155.1",

项目创建成功在IDE当中导入项目

1、添加一些依赖项

npm install vue-routernpm install threenpm install @types/three -Dnpm install ant-design-vue

在这里插入图片描述在这里插入图片描述创建一些路由相关
在这里插入图片描述在这里插入图片描述在这里插入图片描述

import {createRouter,createWebHistory,RouteRecordRaw} from "vue-router";
const routes: RouteRecordRaw[] = [
]
const router = createRouter({history:createWebHistory(),routes
})
router.beforeEach((to)=>{document.title = 'three+vite+vue3'+to.meta.title as string
})export default router

在这里插入图片描述

import { createApp } from 'vue'
import './style.css';
import Antd from 'ant-design-vue';
import App from './App.vue';
import router from './router/index';
import 'ant-design-vue/dist/reset.css';let app = createApp(App)
app.use(router)
app.use(Antd)
app.mount('#app')

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

import {RouteRecordRaw} from "vue-router";
const chapter1 : RouteRecordRaw[] = [
]export default chapter1;

在这里插入图片描述

import {createRouter,createWebHistory,RouteRecordRaw} from "vue-router";
import chapter1 from "./chapter1";
const routes: RouteRecordRaw[] = [...chapter1
]
const router = createRouter({history:createWebHistory(),routes
})
export default router

在这里插入图片描述

<template><router-view></router-view>
</template>
<script setup>
</script>
<style scoped>
</style>

在这里插入图片描述

<template><div>第一个场景</div>
</template>
<script>
export default {name: "index"
}
</script>
<style scoped>
</style>

二、创建3D【基础搭建】

在这里插入图片描述

.container{width: 100vw;height: 100vh;
}

1、绘制板子,立方体,球体

Three.js来绘制一个简单的3D场景,包括一个平面(板子)、一个立方体和一个球体
在这里插入图片描述

<template><div ref="containerRef" class="container"></div>
</template>
<script lang="ts" setup>
import {onMounted, ref} from "vue";
import {AxesHelper, BoxGeometry,Color,Mesh,MeshBasicMaterial,PerspectiveCamera,PlaneGeometry,Scene, SphereGeometry,WebGLRenderer
} from "three";const containerRef = ref<HTMLDivElement>()
//创建场景
const scene = new Scene();
//创建摄像机
const camera = new PerspectiveCamera(45,window.innerWidth / window.innerHeight,0.1,1000)
//设置摄像机位置
camera.position.set(-30,40,30)
//设置摄像机朝向
camera.lookAt(scene.position)//重置webGL的颜色
const renderer =  new WebGLRenderer();
renderer.setClearColor(new Color(0xeeeeee))
renderer.setSize(window.innerWidth,window.innerHeight)//添加坐标系
const ases = new AxesHelper(20)
scene.add(ases)//绘制板子,设置板子的宽度为60,设置板子的高度为20
const planeGeometry = new PlaneGeometry(60,20);
const meshBasicMaterial = new MeshBasicMaterial({color:0xcccccc});//设置材质颜色
const plane = new Mesh(planeGeometry,meshBasicMaterial)plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15
plane.position.y = 0
plane.position.z = 0
scene.add(plane)//绘制立方体,设置板子的长宽高分别是4,4,4
const cubeGeometry = new BoxGeometry(4,4,4)
const cubeMaterial = new MeshBasicMaterial({color:0xff0000,wireframe:true})
const cube = new Mesh(cubeGeometry,cubeMaterial)
cube.position.set(2,2,2)
scene.add(cube)//绘制球体,设置球体的半径为4
const sphereGeometry = new SphereGeometry(4)
const sphereMaterial = new MeshBasicMaterial({color: 0x7777ff,wireframe:true
})
const sphere = new Mesh(sphereGeometry,sphereMaterial)
sphere.position.x = 15
sphere.position.y = 4
sphere.position.z = 2
scene.add(sphere)onMounted(()=>{//设置摄像头朝向containerRef.value?.appendChild(renderer.domElement)renderer.render(scene,camera)
})</script>
<style scoped></style>

在这里插入图片描述

2、材质和光照

在Three.js中,材质和光照是让物体看起来更为真实的关键因素。材质定义了物体表面的外观,如颜色、纹理和光照效果。Three.js提供了多种类型的材质,适用于不同的光照效果。

物理基础渲染(Physically Based Rendering, PBR)是一种基于物理的渲染技术,使用物理基础渲染代码和材料处理技术来模拟光线和材料之间的物理相互作用,以创建逼真的材料外观和光照效果。这种渲染技术可以提供更真实的阴影,高光,反射和漫反射效果,使场景看起来更加真实。Three.js核心也包含了与Unreal、Unity、Disney和Pixar等巨头使用的相同的基于物理的渲染 (PBR) 算法。

对于纹理的应用,可以通过加载图片并设置其重复模式、采样模式以及重复次数来实现贴图效果。例如,创建一个地平面,并用下方展示的 2x2 像素的黑白格图片来作为纹理。首先加载这个纹理,设置重复模式(wrapS, wrapT),采样模式(magFilter)以及重复的次数。因为贴图是 2x2 大小,通过设置成平铺模式,并且重复次数是边长的一半,就可以让每个格子正好是1个单位的大小。

设置导航菜单组件
在这里插入图片描述

<template><a-menu mode="horizontal" style="position: fixed"><a-sub-menu key="demo"><template #title>第一章</template><a-menu-item key="1"><router-link to="/"> 第一个场景 </router-link></a-menu-item><a-menu-item key="2"><router-link to="/chapter1/2"> 第一个场景 </router-link></a-menu-item></a-sub-menu></a-menu><router-view></router-view>
</template>
<script setup>
</script>
<style scoped>
</style>

复制index,生成index2
在这里插入图片描述

<template><div ref="containerRef" class="container"></div>
</template>
<script lang="ts" setup>
import {onMounted, ref} from "vue";
import {AxesHelper, BoxGeometry,Color,Mesh,MeshBasicMaterial,PerspectiveCamera,PlaneGeometry,Scene, SphereGeometry,WebGLRenderer
} from "three";const containerRef = ref<HTMLDivElement>()
//创建场景
const scene = new Scene();
//创建摄像机
const camera = new PerspectiveCamera(45,window.innerWidth / window.innerHeight,0.1,1000)
//设置摄像机位置
camera.position.set(-30,40,30)
//设置摄像机朝向
camera.lookAt(scene.position)//重置webGL的颜色
const renderer =  new WebGLRenderer();
renderer.setClearColor(new Color(0xeeeeee))
renderer.setSize(window.innerWidth,window.innerHeight)//添加坐标系
const ases = new AxesHelper(20)
scene.add(ases)//绘制板子,设置板子的宽度为60,设置板子的高度为20
const planeGeometry = new PlaneGeometry(60,20);
const meshBasicMaterial = new MeshBasicMaterial({color:0xcccccc});//设置材质颜色
const plane = new Mesh(planeGeometry,meshBasicMaterial)plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15
plane.position.y = 0
plane.position.z = 0
scene.add(plane)//绘制立方体,设置板子的长宽高分别是4,4,4
const cubeGeometry = new BoxGeometry(4,4,4)
const cubeMaterial = new MeshBasicMaterial({color:0xff0000,wireframe:true})
const cube = new Mesh(cubeGeometry,cubeMaterial)
cube.position.set(2,2,2)
scene.add(cube)//绘制球体,设置球体的半径为4
const sphereGeometry = new SphereGeometry(4)
const sphereMaterial = new MeshBasicMaterial({color: 0x7777ff,wireframe:true
})
const sphere = new Mesh(sphereGeometry,sphereMaterial)
sphere.position.x = 15
sphere.position.y = 4
sphere.position.z = 2
scene.add(sphere)onMounted(()=>{//设置摄像头朝向containerRef.value?.appendChild(renderer.domElement)renderer.render(scene,camera)
})</script>
<style scoped></style>

在这里插入图片描述

import {RouteRecordRaw} from "vue-router";
import Index from '../lesson/chapter1/index.vue'
import Index2 from '../lesson/chapter1/index2.vue'const chapter1 : RouteRecordRaw[] = [{path:'/',component: Index,meta:{title:"第一个场景"}},{path:'/chapter1/2',component: Index2,meta:{title:"第二个场景"}}
]
export default chapter1;

实现第二个场景

<template><div ref="containerRef" class="container"></div>
</template>
<script lang="ts" setup>
import {onMounted, ref} from "vue";
import {AxesHelper, BoxGeometry,Color,Mesh,MeshBasicMaterial, MeshLambertMaterial,PerspectiveCamera,PlaneGeometry,Scene, SphereGeometry, SpotLight,WebGLRenderer
} from "three";const containerRef = ref<HTMLDivElement>()
//创建场景
const scene = new Scene();
//创建摄像机
const camera = new PerspectiveCamera(45,window.innerWidth / window.innerHeight,0.1,1000)
//设置摄像机位置
camera.position.set(-30,40,30)
//设置摄像机朝向
camera.lookAt(scene.position)//重置webGL的颜色
const renderer =  new WebGLRenderer();
renderer.setClearColor(new Color(0xeeeeee))
renderer.setSize(window.innerWidth,window.innerHeight)
renderer.shadowMap.enabled = trueconst spotLight = new SpotLight(0xffffff)
spotLight.castShadow = true
spotLight.position.set(-40,60,-10)
scene.add(spotLight)//添加坐标系
const axes = new AxesHelper(20)
scene.add(axes)//绘制板子,设置板子的宽度为60,设置板子的高度为20
const planeGeometry = new PlaneGeometry(60,20);
const meshBasicMaterial = new MeshLambertMaterial({color:0xcccccc});//设置材质颜色
const plane = new Mesh(planeGeometry,meshBasicMaterial)
plane.receiveShadow = true //设置可以接收阴影
plane.rotation.x = -0.5 * Math.PI;
//plane.position.x = 15
//plane.position.y = 0
//plane.position.z = 0scene.add(plane)//绘制立方体,设置板子的长宽高分别是4,4,4
const cubeGeometry = new BoxGeometry(4,4,4)
const cubeMaterial = new MeshLambertMaterial({color:0xff0000,wireframe:false})
const cube = new Mesh(cubeGeometry,cubeMaterial)
cube.castShadow = true
cube.position.set(2,2,2)
scene.add(cube)//绘制球体,设置球体的半径为4
const sphereGeometry = new SphereGeometry(4)
const sphereMaterial = new MeshLambertMaterial({color: 0x7777ff,wireframe:false
})const sphere = new Mesh(sphereGeometry,sphereMaterial)
sphere.castShadow = true
sphere.position.x = 15
sphere.position.y = 4
sphere.position.z = 2
scene.add(sphere)onMounted(()=>{//设置摄像头朝向containerRef.value?.appendChild(renderer.domElement)renderer.render(scene,camera)
})</script>
<style scoped></style>

在这里插入图片描述

3、材质和光照和动画

Three.js提供了一套强大的动画系统,可以应用于物体的位置、旋转、缩放、材质的颜色或不透明度等各个方面。这套系统中主要包括了KeyFrameTrack、AnimationClip、AnimationMixer和AnimationAction四个组件。

在制作动画时,我们通常会使用关键帧动画,即在不同时间点设置关键帧,然后由动画系统通过补间过程自动填补各关键帧之间的变化。例如,要为一个弹跳的球设置动画,只需要指定弹跳的顶部和底部的点,Three.js将在这两点之间的所有点上平滑地生成动画。此外,我们还可以通过合成和混合多个动画来创造出更复杂的效果。
复制index2创建index3
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

<template><div ref="containerRef" class="container"></div>
</template>
<script lang="ts" setup>
import {onMounted, ref} from "vue";
import {AxesHelper, BoxGeometry,Color,Mesh,MeshBasicMaterial, MeshLambertMaterial,PerspectiveCamera,PlaneGeometry,Scene, SphereGeometry, SpotLight,WebGLRenderer
} from "three";const containerRef = ref<HTMLDivElement>()
//创建场景
const scene = new Scene();
//创建摄像机
const camera = new PerspectiveCamera(45,window.innerWidth / window.innerHeight,0.1,1000)
//设置摄像机位置
camera.position.set(-30,40,30)
//设置摄像机朝向
camera.lookAt(scene.position)//重置webGL的颜色
const renderer =  new WebGLRenderer();
renderer.setClearColor(new Color(0xeeeeee))
renderer.setSize(window.innerWidth,window.innerHeight)
renderer.shadowMap.enabled = trueconst spotLight = new SpotLight(0xffffff)
spotLight.castShadow = true
spotLight.position.set(-40,60,-10)
scene.add(spotLight)//添加坐标系
const axes = new AxesHelper(20)
scene.add(axes)//绘制板子,设置板子的宽度为60,设置板子的高度为20
const planeGeometry = new PlaneGeometry(100,50);
const meshBasicMaterial = new MeshLambertMaterial({color:0xcccccc});//设置材质颜色
const plane = new Mesh(planeGeometry,meshBasicMaterial)
plane.receiveShadow = true //设置可以接收阴影
plane.rotation.x = -0.5 * Math.PI;
//plane.position.x = 15
//plane.position.y = 0
//plane.position.z = 0scene.add(plane)//绘制立方体,设置板子的长宽高分别是4,4,4
const cubeGeometry = new BoxGeometry(4,4,4)
const cubeMaterial = new MeshLambertMaterial({color:0xff0000,wireframe:false})
const cube = new Mesh(cubeGeometry,cubeMaterial)
cube.castShadow = true
cube.position.set(2,2,2)
scene.add(cube)//绘制立方体,设置板子的长宽高分别是4,4,4
const cubeGeometry1 = new BoxGeometry(4,4,4)
const cubeMaterial1 = new MeshLambertMaterial({color:0xff0000,wireframe:false})
const cube1 = new Mesh(cubeGeometry1,cubeMaterial1)
cube1.castShadow = true
cube1.position.set(-10,2,2)
scene.add(cube1)//绘制球体,设置球体的半径为4
const sphereGeometry = new SphereGeometry(4)
const sphereMaterial = new MeshLambertMaterial({color: 0x7777ff,wireframe:false
})const sphere = new Mesh(sphereGeometry,sphereMaterial)
sphere.castShadow = true
sphere.position.x = 15
sphere.position.y = 4
sphere.position.z = 2
scene.add(sphere)//控制物体运动
let step = 0;function renderScene() {step += 0.04;cube.rotation.x += 0.02;cube.rotation.y += 0.02;cube.rotation.z += 0.02;cube1.rotation.x += -0.02;cube1.rotation.y += -0.02;cube1.rotation.z += -0.02;cube1.scale.set((2 + 1 * Math.cos(step)), (2 + 1 * Math.cos(step)), (2 + 1 * Math.cos(step)));//控制物体sphere.position.x = 20 + 10 * Math.cos(step); //cos为数据当中的函数 余弦函数sphere.position.y = 2 + 10 * Math.abs(Math.sin(step));  //abs为绝对值  sin为正弦函数requestAnimationFrame(renderScene)renderer.render(scene,camera)
}
renderScene()onMounted(()=>{//设置摄像头朝向containerRef.value?.appendChild(renderer.domElement)renderer.render(scene,camera)
})</script>
<style scoped></style>

在这里插入图片描述

4、性能监控

Three.js的性能监控工具Stats.js是一个强大的插件,它能够监测帧率、内存等数据的变化。在动画或网页开发中,帧率是衡量和描述动画是否流畅的一个重要单位。Stats.js可以帮助开发者实时了解Three.js的渲染性能,尤其是渲染帧率(FPS),即每秒钟完成的渲染次数。理想状态下,渲染帧率应该达到每秒60次。

在使用Stats.js时,首先需要引入相关的脚本文件。然后,实例化一个Stats对象,并将该对象生成的DOM元素添加到页面中。通过这种方式,我们可以在开发过程中实时监控Three.js的性能,及时发现并解决可能存在的问题,从而提升用户体验。

安装stats.js插件

npm install stats.js

复制index3.vue创建index4.vue
在这里插入图片描述

    import index4 from '../lesson/chapter1/index4.vue',{path:'/chapter1/4',component: index4,meta:{title:"性能监控"}}

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

 <div ref="statsRef"></div>const statsRef = ref<HTMLDivElement>()const stats = new Stats()
stats.showPanel(0)

在这里插入图片描述

  stats.update()//创建场景const scene = new Scene();stats.dom.style.top = "50px"statsRef.value?.append(stats.dom)

访问第四个场景
http://127.0.0.1:5173/chapter1/4

在这里插入图片描述

5、交互控制

dat.gui@0.7.9是一个轻量级的JavaScript库,它的主要功能是帮助用户添加交互式控制面板,以便在3D场景中调整对象参数并实时预览结果。

复制一下index4.vue 为index5.vue
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

 {path:'/chapter1/5',component: index5,meta:{title:"交互控制"}}

在这里插入图片描述

     <a-menu-item key="5"><router-link to="/chapter1/5"> 第五个场景 </router-link></a-menu-item>

安装dat.gui

npm install dat.gui@0.7.9

在这里插入图片描述

npm install @types/dat.gui@0.7.9 -D

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

import * as dat from "dat.gui"

在这里插入图片描述

const controlRef = ref({rotationSpeed:0.02,bouncingSpeed:0.03,
})
const gui = new dat.GUI();
gui.add(controlRef.value,"rotationSpeed",0,0.5)
gui.add(controlRef.value,"bouncingSpeed",0,0.5)step += 0.04;cube.rotation.x += controlRef.value.rotationSpeed;cube.rotation.y += controlRef.value.rotationSpeed;cube.rotation.z += controlRef.value.rotationSpeed;cube1.rotation.x += -controlRef.value.rotationSpeed;cube1.rotation.y += -controlRef.value.rotationSpeed;cube1.rotation.z += -controlRef.value.rotationSpeed;step += controlRef.value.bouncingSpeed;

在这里插入图片描述
放置重复初始化

if(document.querySelectorAll(".dg.ac>.dg.main.a").length === 0){const gui = new dat.GUI()gui.add(controlRef.value,"rotationSpeed",0,0.5)gui.add(controlRef.value,"bouncingSpeed",0,0.5)
}

index5.vue全部代码

<template><div ref="statsRef"></div><div ref="containerRef" class="container"></div>
</template>
<script lang="ts" setup>
import {onMounted, ref} from "vue";
import {AxesHelper, BoxGeometry,Color,Mesh,MeshBasicMaterial, MeshLambertMaterial,PerspectiveCamera,PlaneGeometry,Scene, SphereGeometry, SpotLight,WebGLRenderer
} from "three";
import Stats from "stats.js"
import * as dat from "dat.gui"const containerRef = ref<HTMLDivElement>()
const statsRef = ref<HTMLDivElement>()const stats = new Stats()
stats.showPanel(0)//创建场景
const scene = new Scene();
//创建摄像机
const camera = new PerspectiveCamera(45,window.innerWidth / window.innerHeight,0.1,1000)
//设置摄像机位置
camera.position.set(-30,40,30)
//设置摄像机朝向
camera.lookAt(scene.position)//重置webGL的颜色
const renderer =  new WebGLRenderer();
renderer.setClearColor(new Color(0xeeeeee))
renderer.setSize(window.innerWidth,window.innerHeight)
renderer.shadowMap.enabled = trueconst spotLight = new SpotLight(0xffffff)
spotLight.castShadow = true
spotLight.position.set(-40,60,-10)
scene.add(spotLight)//添加坐标系
const axes = new AxesHelper(20)
scene.add(axes)//绘制板子,设置板子的宽度为60,设置板子的高度为20
const planeGeometry = new PlaneGeometry(100,50);
const meshBasicMaterial = new MeshLambertMaterial({color:0xcccccc});//设置材质颜色
const plane = new Mesh(planeGeometry,meshBasicMaterial)
plane.receiveShadow = true //设置可以接收阴影
plane.rotation.x = -0.5 * Math.PI;
//plane.position.x = 15
//plane.position.y = 0
//plane.position.z = 0scene.add(plane)//绘制立方体,设置板子的长宽高分别是4,4,4
const cubeGeometry = new BoxGeometry(4,4,4)
const cubeMaterial = new MeshLambertMaterial({color:0xff0000,wireframe:false})
const cube = new Mesh(cubeGeometry,cubeMaterial)
cube.castShadow = true
cube.position.set(2,2,2)
scene.add(cube)//绘制立方体,设置板子的长宽高分别是4,4,4
const cubeGeometry1 = new BoxGeometry(4,4,4)
const cubeMaterial1 = new MeshLambertMaterial({color:0xff0000,wireframe:false})
const cube1 = new Mesh(cubeGeometry1,cubeMaterial1)
cube1.castShadow = true
cube1.position.set(-10,2,2)
scene.add(cube1)//绘制球体,设置球体的半径为4
const sphereGeometry = new SphereGeometry(4)
const sphereMaterial = new MeshLambertMaterial({color: 0x7777ff,wireframe:false
})const sphere = new Mesh(sphereGeometry,sphereMaterial)
sphere.castShadow = true
sphere.position.x = 15
sphere.position.y = 4
sphere.position.z = 2
scene.add(sphere)const controlRef = ref({rotationSpeed:0.02,bouncingSpeed:0.03,
})if(document.querySelectorAll(".dg.ac>.dg.main.a").length === 0){const gui = new dat.GUI()gui.add(controlRef.value,"rotationSpeed",0,0.5)gui.add(controlRef.value,"bouncingSpeed",0,0.5)
}//控制物体运动
let step = 0;function renderScene() {stats.update()step += 0.04;cube.rotation.x += controlRef.value.rotationSpeed;cube.rotation.y += controlRef.value.rotationSpeed;cube.rotation.z += controlRef.value.rotationSpeed;cube1.rotation.x += -controlRef.value.rotationSpeed;cube1.rotation.y += -controlRef.value.rotationSpeed;cube1.rotation.z += -controlRef.value.rotationSpeed;step += controlRef.value.bouncingSpeed;cube1.scale.set((2 + 1 * Math.cos(step)), (2 + 1 * Math.cos(step)), (2 + 1 * Math.cos(step)));//控制物体sphere.position.x = 20 + 10 * Math.cos(step); //cos为数据当中的函数 余弦函数sphere.position.y = 2 + 10 * Math.abs(Math.sin(step));  //abs为绝对值  sin为正弦函数requestAnimationFrame(renderScene)renderer.render(scene,camera)
}
renderScene()onMounted(()=>{//创建场景const scene = new Scene();stats.dom.style.top = "50px"statsRef.value?.append(stats.dom)//设置摄像头朝向containerRef.value?.appendChild(renderer.domElement)renderer.render(scene,camera)
})</script>
<style scoped></style>

6、响应窗口变化

和之前一样创建index6.vue
在这里插入图片描述

/*
监听在窗口变化的时候重新设置大小
* */
window.addEventListener('resize',()=>{camera.aspect = window.innerWidth / window.innerHeight;//更新相机投影矩阵camera.updateProjectionMatrix();renderer.setSize(window.innerWidth,window.innerHeight)
})

在这里插入图片描述

三、基础场景搭建

1、创建基础场景【实现添加几何体和删除几何体】

在这里插入图片描述
将index6复制到chapter2下的index

在这里插入图片描述


const controlRef = ref({rotationSpeed:0.02,bouncingSpeed:0.03,numberOfObjects:0,addCube:function (){//绘制立方体,设置板子的长宽高分别是4,4,4const cubeGeometry = new BoxGeometry(4,4,4)const cubeMaterial = new MeshLambertMaterial({color:0xff0000,wireframe:false})const cube = new Mesh(cubeGeometry,cubeMaterial)cube.name = "cube-"+scene.children.lengthcube.castShadow = truecube.position.x = -30 + Math.round((Math.random() * 60))cube.position.y =  Math.round((Math.random() * 5))cube.position.z = -20 + Math.round((Math.random() * 40))scene.add(cube)this.numberOfObjects = scene.children.length},removeCube:function (){const allChildren = scene.children;const lastObject = allChildren[allChildren.length - 1];if(lastObject instanceof Mesh && lastObject.name.startsWith('cube')){scene.remove(lastObject);}this.numberOfObjects = scene.children.length}
})

在这里插入图片描述


if(document.querySelectorAll(".dg.ac>.dg.main.a").length === 0){const gui = new dat.GUI()gui.add(controlRef.value,"addCube")gui.add(controlRef.value,"removeCube")gui.add(controlRef.value,"numberOfObjects").listen()gui.add(controlRef.value,"rotationSpeed",0,0.5)gui.add(controlRef.value,"bouncingSpeed",0,0.5)}

在这里插入图片描述

stats.update()//遍历场景当中的所有内容scene.traverse((e) =>{/** if ( e instanceof Mesh && e != plane){e.rotation.x += controlRef.value.rotationSpeed;e.rotation.y += controlRef.value.rotationSpeed;e.rotation.z += controlRef.value.rotationSpeed;}*/if ( e.name.startsWith('cube')){e.rotation.x += controlRef.value.rotationSpeed;e.rotation.y += controlRef.value.rotationSpeed;e.rotation.z += controlRef.value.rotationSpeed;}})

实现点击添加cube和删除cube
在这里插入图片描述

2、实现雾化场景

雾化效果是一种常见的视觉效果,它可以使场景中的物体看起来更加模糊和透明。在Three.js中,可以通过设置材质的透明度和混合模式来实现雾化效果
在这里插入图片描述

addFog:function (){scene.fog = new Fog(0xffffff,0.015,100)this.numberOfObjects = scene.children.length}
gui.add(controlRef.value,"addFog")

http://127.0.0.1:5173/chapter2/2
在这里插入图片描述
移除雾化
在这里插入图片描述

removeFog:function (){scene.fog = null}
  gui.add(controlRef.value,"removeFog")

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

3、重写材质

在Three.js中,材质是定义物体外观的关键。通过创建自定义材质,可以对物体的外观进行更精细的控制,包括如何设置材质的颜色、纹理和透明度等属性。
在这里插入图片描述

toggleMaterial:function (){if(!scene.overrideMaterial){scene.overrideMaterial = new MeshLambertMaterial({color:0xffffff,})}else{scene.overrideMaterial =null}}gui.add(controlRef.value,"toggleMaterial")

在这里插入图片描述

4、常见几何体

在Three.js中,几何体是一个数据结构,它包含了用于描述三维物体的基本信息,如顶点(vertices)、线(lines)和面(faces)。几何体可以被用来定义物体的形状和大小。

常见的几何体类型有以下几种:

BoxGeometry(立方体几何体):通过指定宽度、高度和深度来创建一个立方体。
SphereGeometry(球体几何体):通过指定半径来创建一个球体。
CylinderGeometry(圆柱体几何体):通过指定高度、半径和圆周上的段数来创建一个圆柱体。
PlaneGeometry(平面几何体):一种基础的二维几何体,可以用来绘制平面。
ConeGeometry(圆锥体几何体):通过指定高度、底部半径和顶部半径,以及圆周上的段数来创建一个圆锥体。
TubularGeometry(管状几何体):这是一种具有圆形截面的管道形状,需要指定管道的中心轴线、直径、高度和圆周上的段数。
在这里插入图片描述


const geoms:BufferGeometry[] = []
geoms.push(new CylinderGeometry(1,4,8))
geoms.push(new BoxGeometry(2,2,2))
geoms.push(new OctahedronGeometry(3))
geoms.push(new TetrahedronGeometry(3))
geoms.push(new TorusGeometry(3,1,10,10))
//材质
const materials = [new MeshLambertMaterial({color:Math.random() * 0xffffff,flatShading:true}),new MeshBasicMaterial({color: 0x000000,wireframe:true})
]
geoms.forEach((g,i) =>{const mesh = createMultiMaterialObject(g,materials)mesh.castShadow = truemesh.position.x = -24 + i * 10,mesh.position.y = 4,scene.add(mesh)
})

在这里插入图片描述

5、修改几何体属性

在Three.js中,几何体的属性可以通过修改其顶点、线和面的数据来改变物体的形状和大小。以下是一些常见的修改几何体属性的方法:

修改顶点数据:通过修改几何体的vertices属性,可以改变物体的形状。例如,可以将一个立方体的顶点数据修改为一个球体的顶点数据,从而创建一个球形物体。

修改线数据:通过修改几何体的lines属性,可以改变物体的边界线。例如,可以将一个立方体的边线修改为一个圆柱体的边线,从而创建一个圆柱形物体。

修改面数据:通过修改几何体的faces属性,可以改变物体的表面。例如,可以将一个立方体的面修改为一个球体的面,从而创建一个球形物体。

修改材质属性:通过修改几何体的material属性,可以改变物体的颜色、纹理和透明度等视觉效果。例如,可以将一个立方体的材质修改为一个半透明的红色材料,从而创建一个半透明的红色立方体。

需要注意的是,修改几何体属性需要对Three.js的底层实现有一定的了解,并且需要注意性能问题。如果频繁地修改几何体属性,可能会导致性能下降。因此,在实际应用中,应该根据需求选择合适的方法来修改几何体属性。
在这里插入图片描述

scaleX:1,scaleY:1,scaleZ:1,positionX:1,positionY:1,positionZ:1,translateZ:1,translateY:1,translateX:1,obj:{x:0,y:0,z:0,},translate:function (){this.obj.x = this.translateXthis.obj.y = this.translateYthis.obj.z = this.translateZ}

在这里插入图片描述

/*设置大小*/const scaleFolder = gui.addFolder("scale")scaleFolder.add(controlRef.value,"scaleX",0,5);scaleFolder.add(controlRef.value,"scaleY",0,5);scaleFolder.add(controlRef.value,"scaleZ",0,5);/*设置位置*/const positionFolder = gui.addFolder("position")positionFolder.add(controlRef.value,"positionX",-5,5);positionFolder.add(controlRef.value,"positionY",-5,5);positionFolder.add(controlRef.value,"positionZ",-5,5);/*设置斜角位置*/const translateFolder = gui.addFolder("translate")translateFolder.add(controlRef.value,"translateX",-5,5);translateFolder.add(controlRef.value,"translateY",-5,5);translateFolder.add(controlRef.value,"translateZ",-5,5);translateFolder.add(controlRef.value,"translate");

在这里插入图片描述

watch(()=>controlRef.value.obj,(n)=>{cube.translateX(n.x)cube.translateY(n.y)cube.translateZ(n.z)
},{deep:true})

在这里插入图片描述

6、相机切换

在Three.js中,相机是用于渲染场景的工具。主要包括透视相机(PerspectiveCamera)和正交相机(OrthographicCamera)。

透视相机可以创建具有深度感的三维效果,而正交相机则可以在二维平面上进行投影。

实现相机视角的切换,主要有两种方法。一种是使用Tween.js库来实现平滑过渡的效果。Tween.js库可以很容易实现两个值之间的过度,中间值都会自动计算出来。

另一种是通过鼠标拉拽来改变相机的位置、旋转角度等,比如使用OrbitControls类。

OrbitControls类是Three.js提供的鼠标、方向键与场景交互的控件,通过鼠标的操作可以改变相机的视角,从而改变视觉,使得视觉效果更具有真实感。

此外,如果想要切换不同的场景,可以通过创建多个场景对象,并在每个场景中添加不同的模型、灯光等元素。

使用renderer.render(scene, camera)方法在渲染循环中渲染当前场景,使用scene.dispose()方法清除当前场景中的元素,释放内存。当需要切换到下一个场景时,重复上述步骤,并将下一个场景设置为当前场景。

设置场景和多个物体
在这里插入图片描述

//绘制板子,设置板子的宽度为60,设置板子的高度为20
const planeGeometry = new PlaneGeometry(100,50,1);
const meshBasicMaterial = new MeshLambertMaterial({color:0xffffff});//设置材质颜色
const plane = new Mesh(planeGeometry,meshBasicMaterial)
plane.receiveShadow = true //设置可以接收阴影
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15
plane.position.y = 0
plane.position.z = 0scene.add(plane)const cubeGeometryC = new BoxGeometry(4,4,4)
const cubeMaterialC = new MeshLambertMaterial({color:0xff0000,wireframe:false})
const cube = new Mesh(cubeGeometryC,cubeMaterialC)
cube.castShadow = true
cube.position.set(2,2,2)
scene.add(cube)for (let j = 0;j < planeGeometry.parameters.height / 2;j++){for (let i = 0;i < planeGeometry.parameters.width / 2;i++){const cube = new Mesh(cubeGeometryC,cubeMaterialC)cube.position.z = -(planeGeometry.parameters.height / 2) + 2 + j * 5;cube.position.x = -(planeGeometry.parameters.height / 2) + 2 + i * 5;cube.position.y = 0;scene.add(cube)}
}

在这里插入图片描述

设置切换相机
在这里插入图片描述

 gui.add(controlRef.value,"camera").listen()gui.add(controlRef.value,"switchCamera")

在这里插入图片描述

watch(()=>controlRef.value.camera,(n)=>{if(n === 'Orthographic'){cameraRef.value = new OrthographicCamera(window.innerWidth / -16 ,window.innerWidth / 16 ,window.innerHeight / 16 ,window.innerHeight / -16,-200,500 )cameraRef.value.position.set(-120,60,180)cameraRef.value.lookAt(scene.position)}else{cameraRef.value = new PerspectiveCamera(45,window.innerWidth / window.innerHeight ,0.1,1000 )cameraRef.value.position.set(-120,60,180)cameraRef.value.lookAt(scene.position)}
})

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

7、相机跟随

在Three.js中,相机跟随物体的技术广泛应用于实现如游戏中的摄像机跟随角色、VR中的视点跟踪等效果。

要实现这一功能,首先需要获取到目标物体(例如一个游戏角色或者一个3D模型)的位置信息,然后将相机的位置设置为该物体的对应位置,从而实现视角的跟随。

此外,关于具体的实现方式,有多种可选的策略。

如果你想要创建一个第一人称视角的效果,可以使用键盘的WASD键控制相机的移动方向;而如果你希望实现第三人称视角的效果,则可以通过鼠标来控制相机的视角朝向。

另外,对于复杂的场景,比如管道内的视野展示或者物体在三维空间中任意方向移动的情况,你可能需要结合使用一些额外的工具和方法。

例如,你可以创建一个管道模型来帮助你观察物体的运动方向,并通过调整相机的位置和朝向,使得镜头能够紧密地跟随物体的移动。
在这里插入图片描述


const lookAtGeom = new SphereGeometry(20)
const lookAtMesh = new Mesh(lookAtGeom,new MeshLambertMaterial({color:0xff0000})
)
scene.add(lookAtMesh)//控制物体运动
let step = 0;function renderScene() {stats.update()if (cameraRef.value){step += 0.01const x = 10 + 100 * Math.sin(step)cameraRef.value.lookAt(new Vector3(x,10,0))  // Vector3是三维的坐标lookAtMesh.position.copy(new Vector3(x,10,0))cameraRef.value.lookAt(new Vector3(x,10,0))renderer.render(scene,cameraRef.value)}
}  

在这里插入图片描述

四、光照

1、环境光

Three.js中环境光(AmbientLight)是一种全局光照,它能够均匀地照亮场景中的物体。

与点光源和平行光源不同,环境光不会直接照亮物体,而是与场景中的颜色相乘,从而使得物体的颜色变暗或变亮。

环境光通常用于模拟全局的光照效果,例如在室外场景中模拟太阳的光线、室内场景中模拟灯光的反射等。

通过调整环境光的颜色和强度,可以改变整个场景的亮度和色调,从而增强渲染的真实感。
在这里插入图片描述

ambientColor:"#0c0c0c"const ambientLight = new AmbientLight(controlRef.value.ambientColor)
scene.add(ambientLight)watch(()=>controlRef.value.ambientColor,(n)=>{ambientLight.color = new Color(n)
})gui.addColor(controlRef.value,"ambientColor")

在这里插入图片描述

2、点光源

Three.js库中的THREE.PointLight(点光源)是一种单点发光、照射所有方向的光源,比如夜空中的照明弹。

在这里插入图片描述

 ambientColor:"#0c0c0c",pointColor:"#ccffcc",distance: 100,
const pointLight = new PointLight(controlRef.value.pointColor)
pointLight.distance = 100
pointLight.position.copy(lookAtMesh.position)
scene.add(pointLight)watch(() => controlRef.value.pointColor,() => {pointLight.color = new Color(controlRef.value.pointColor)}
)
watch(() => controlRef.value.distance,() => {pointLight.distance = controlRef.value.distance}
)
 gui.addColor(controlRef.value,"pointColor")gui.add(controlRef.value,"distance",-1000,1000)

3、聚光灯

Three.js中的聚光灯(SpotLight)是一种光源类型,用于在场景中创建聚焦光照。

它有一个锥形的照射范围,可以模拟手电筒或灯塔等发出的光线。

聚光灯具有方向和角度,可以通过调整其属性来控制照射范围和强度
在这里插入图片描述
在这里插入图片描述

target:'plane',

在这里插入图片描述


watch(() => controlRef.value.target,(t) => {if(t === 'cube'){spotLight.target = cube}else if( t === 'sphere'){spotLight.target = sphere}else {spotLight.target = plane}}
)

在这里插入图片描述

4、平行光

Three.js中的平行光(DirectionalLight)是一种光源类型,它发出的光线是平行的并且沿特定方向传播。

这种光源模拟太阳光等效果,因为它的表现像是无限远,从它发出的光线都是平行的。平行光通常用于模拟太阳光、月光等远离物体的光源。

你可以通过调整平行光的颜色、强度以及方向属性来控制照射效果。

在着色器中计算时,平行光的方向向量会直接与模型顶点的法线方向进行点乘操作,从而确定该点的亮度。
在这里插入图片描述

//添加平行光
const directionalColor = "#ff5808"
const directionalLight = new DirectionalLight(directionalColor)
directionalLight.position.set(-40,60,-10)
directionalLight.castShadow = true
directionalLight.intensity = 0.5
scene.add(directionalLight)

5、半球光

Three.js中的半球光(HemisphereLight)是一种光源类型,它模拟了天空和地面的反射效果。这种光源的特性在于,其发出的光线颜色从天空光线颜色渐变到地面光线颜色。

具体来说,半球光的原理由两部分组成,一部分是从下往上的平行光,另一部分是从上半球往中心点的光。

这样,实现了模拟模型法线向上的部分天空光线照射到物体上,法线向下的部分接收来自于地面的反射环境光。

然而需要注意的是,半球光无法投射阴影。

在创建半球光时,可以分别指定天空和地面的颜色。
在这里插入图片描述

//添加半球光
// 创建球体几何体和材质
const sphereGeometry1 = new SphereGeometry(2, 32, 32);
const sphereMaterial1 = new MeshLambertMaterial({color: 0x7777ff,wireframe:false
})
// 创建网格对象并添加到场景中
const spherea = new  Mesh(sphereGeometry1, sphereMaterial1);
scene.add(spherea);
// 渲染循环
function animate() {requestAnimationFrame(animate);// 更新球体材质的emissive属性以实现半球光效果const time = Date.now() * 0.001;sphereMaterial.emissive.setRGB(Math.sin(time) * 0.5 + 0.5, Math.cos(time) * 0.5 + 0.5, Math.sin(time * 2) * 0.5 + 0.5);renderer.render(scene, camera);
}
animate();

在这里插入图片描述

五、小车案例

1、基础环境搭建

在这里插入图片描述

<template><div ref="statsRef"></div><div ref="containerRef" class="container"></div>
</template><script lang="ts" setup>import {ACESFilmicToneMapping, AxesHelper,Color,EquirectangularReflectionMapping, Fog, GridHelper, Material,PerspectiveCamera,Scene,sRGBEncoding,WebGLRenderer
} from "three";
import Stats from "stats.js"
import * as dat from "dat.gui"
import {onMounted, ref, watchEffect} from "vue";
//
import venice_sunset_1k from '../../assets/venice_sunset_1k.hdr?url'
import car from '../../assets/car.glb?url'
import {RGBELoader} from "three/examples/jsm/loaders/RGBELoader";const scene = new Scene();const grid = new GridHelper(20,40,0xfffff,0xffff);const containerRef = ref<HTMLDivElement>()
const statsRef = ref<HTMLDivElement>()const stats = new Stats();
const controlRef = ref({bodyColor:"#0c0c0c",glassColor:"#0c0c0c",detailColor:"#0c0c0c",
})
const cameraRef = ref<PerspectiveCamera>()
const rendererRef = ref<WebGLRenderer>()//它会检查当前网页中是否存在具有特定类名(即".dg.ac>.dg.main.a")的元素。
// 如果不存在,它将创建一个新的dat.GUI对象,
// 并在该对象中添加三个颜色控件:bodyColor、glassColor和detailColor。
// 这些颜色控件的值都是从名为controlRef的引用所指向的对象中获取的
function initGUI() {if(document.querySelectorAll(".dg.ac>.dg.main.a").length === 0){const gui = new dat.GUI()gui.addColor(controlRef.value,"bodyColor")gui.addColor(controlRef.value,"glassColor")gui.addColor(controlRef.value,"detailColor")}
}
/*
它使用PerspectiveCamera构造函数创建了一个新的透视相机对象。
这个构造函数需要四个参数:视角角度(在这里为45度)、纵横比(在这里为窗口宽度除以窗口高度)、近裁剪平面距离(在这里为0.1)
以及远裁剪平面距离(在这里为1000)。然后,它设置了摄像机的位置坐标为(-30,40,30),使摄像机面向场景的位置
* */
function initCamera() {cameraRef.value =  new PerspectiveCamera(45,window.innerWidth / window.innerHeight,0.1,1000)cameraRef.value.position.set(-30,40,30)cameraRef.value.lookAt(scene.position)
}/*创建了一个新的WebGL渲染器并将其赋值给rendererRef。这个渲染器启用了抗锯齿功能,
设置了像素比例为窗口的设备像素比,
并将宽度和高度设置为窗口的内宽度和内高度。它还设置了输出编码为sRGBEncoding,
色调映射为ACESFilmicToneMapping,色调映射曝光为0.85。* */
function initRenderer(){rendererRef.value = new WebGLRenderer({antialias:true})rendererRef.value.setPixelRatio(window.devicePixelRatio)rendererRef.value.setSize(window.innerWidth,window.innerHeight)rendererRef.value.outputEncoding = sRGBEncodingrendererRef.value.toneMapping = ACESFilmicToneMappingrendererRef.value.toneMappingExposure = 0.85
}/*
将背景颜色设为深灰色(#333),然后加载名为"venice_sunset_1k"的环境贴图,并将其映射方式设为EquirectangularReflectionMapping。这将会使场景具有反射光照的效果。接下来的几行代码修改了grid组件所用材质的透明度、深度写入以及透明度属性,使其呈现出半透明效果。最后,它向场景中添加了一个长度为20的新坐标轴助手。
*
*/
function initScene(){scene.background = new Color(0x333333)scene.environment = new RGBELoader().load(venice_sunset_1k)scene.environment.mapping = EquirectangularReflectionMapping;// scene.fog = new Fog(0x333333,10,15)const material = grid.material as Materialmaterial.opacity = 0.2material.depthWrite = falsematerial.transparent = truescene.add(grid)const axes = new AxesHelper(20)scene.add(axes)}initGUI()
onMounted(()=>{//创建场景stats.dom.style.top = "50px"statsRef.value?.append(stats.dom)initScene()initCamera()initRenderer()
})/*
持续运行的循环渲染函数,用于不断更新和重新绘制3D场景。每一帧开始时,它会先调用stats对象的update方法,用于统计当前性能信息。接着,它会调用requestAnimationFrame方法再次请求下一帧的动画。这个方法会在浏览器认为适合的时候安排一次重绘,通常是在下一次刷新周期之前。如果存在cameraRef引用,则使用该相机进行当前帧的渲染。rendererRef指针表示的是已经初始化好的WebGL渲染器,而scene则是需要渲染的三维场景。最后,根据性能计数器的时间戳,对网格物体的位置做了一次平移操作,使得网格能够以一定的速度沿Z轴方向移动。这里使用了取模运算符%来让网格的位置在其运动过程中始终保持在一个范围内。注意requestAnimationFrame(renderScene)这一句的作用。这是一个JavaScript API,它可以在浏览器下次重绘之前,要求浏览器执行指定的函数(在这个例子中就是renderScene())。这样做的好处是可以减少不必要的CPU和GPU工作,从而提高页面性能。每个requestAnimationFrame()调用都会返回一个定时ID,你可以用这个ID取消未执行的动画。如果要停止动画,只需清除对应的定时ID即可。在这个例子中,requestAnimationFrame(renderScene)会在每次渲染完成后立即调用自己,从而形成一个无限循环,不断地重复执行渲染过程。只要页面没有关闭,这个函数就会一直被调用下去。
* */
function renderScene() {stats.update()requestAnimationFrame(renderScene)if(cameraRef.value){rendererRef.value!.render(scene,cameraRef.value)}const time = -performance.now() / 1000grid.position.z = -(time) % 1
}renderScene()/*
这段代码是一个Vue watchEffect钩子函数,当某些数据发生变化时,会触发此函数执行。函数内部主要做了两件事:将rendererRef的domElement属性(即渲染器的DOM元素)添加到containerRef指定的容器中。这意味着该渲染器将会在对应的HTML元素中显示。
在窗口大小发生改变时,监听并响应事件。当窗口尺寸发生改变时,会更新相机的宽高比,计算新的投影矩阵,并且重新设置渲染器的尺寸,使其与窗口尺寸保持一致。
因此,这段代码的作用是将渲染结果正确地显示出来,并确保在窗口尺寸改变时能够及时更新视口大小和视角。
* */
watchEffect(()=>{containerRef.value?.appendChild(rendererRef.value!.domElement)window.addEventListener('resize',()=>{cameraRef.value!.aspect = window.innerWidth / window.innerHeight//更新相投影矩阵cameraRef.value!.updateProjectionMatrix();rendererRef.value!.setSize(window.innerWidth,window.innerHeight)},false)
})</script><style scoped></style>

在这里插入图片描述

2、载入模型,实现轨道控制器

在这里插入图片描述

import venice_sunset_1k from '../../assets/venice_sunset_1k.hdr?url'
import Car from '../../assets/car.glb?url'

在这里插入图片描述

//轨道控制器
const controlsRef = ref<OrbitControls>()

在这里插入图片描述


function initGLTF() {const dracoLoader = new DRACOLoader();dracoLoader.setDecoderPath('/gltf/');const  loader = new GLTFLoader();loader.setDRACOLoader (dracoLoader) ;loader.load(Car,(gltf: GLTF) => {console.log(gltf)const carModel = gltf.scene.children[0];scene.add(carModel)})
}function initControl() {if(cameraRef.value) {controlsRef.value = new OrbitControls(cameraRef.value,containerRef.value);controlsRef.value.enableDamping = truecontrolsRef.value.maxDistance = 9controlsRef.value.target.set(0,0.5,0)controlsRef.value.update()}
}
initGUI()
initGLTF()
onMounted(()=>{//创建场景stats.dom.style.top = "50px"statsRef.value?.append(stats.dom)initScene()initCamera()initRenderer()initControl()
})

3、实现模型颜色材质调整,轮子转动

在这里插入图片描述

//轨道控制器
const controlsRef = ref<OrbitControls>()
const bodyMaterial = new MeshPhysicalMaterial({color:0xff0000,metalness:1.0,roughness:0.5,clearcoat:1.0,clearcoatRoughness:0.03,sheen:0.5
})
const glassMaterial = new MeshPhysicalMaterial({color:0xffffff,metalness:0.25,roughness:0,transmission:1.0
})
const detailMaterial = new MeshPhysicalMaterial({color:0xff0000,metalness:1.0,roughness:0.5
})

在这里插入图片描述

watch(()=> controlRef.value.bodyColor,(c)=>{bodyMaterial.color.set(c);
})
watch(()=> controlRef.value.glassColor,(c)=>{glassMaterial.color.set(c);
})
watch(()=> controlRef.value.detailColor,(c)=>{detailMaterial.color.set(c);
})

在这里插入图片描述

  const carModel = gltf.scene.children[0];(carModel.getObjectByName('body') as Mesh).material = bodyMaterial;(carModel.getObjectByName('glass') as Mesh).material = glassMaterial;(carModel.getObjectByName('rim_fl') as Mesh).material = detailMaterial;(carModel.getObjectByName('rim_fr') as Mesh).material = detailMaterial;(carModel.getObjectByName('rim_rr') as Mesh).material = detailMaterial;(carModel.getObjectByName('rim_rl') as Mesh).material = detailMaterial;(carModel.getObjectByName('trim') as Mesh).material = detailMaterial;wheels.push(carModel.getObjectByName('wheel_fl'),carModel.getObjectByName('wheel_fr'),carModel.getObjectByName('wheel_rl'),carModel.getObjectByName('wheel_rr'),)

在这里插入图片描述

for (let i = 0;i < wheels.length;i++){wheels[i]!.rotation.x = time * Math.PI * 2}

实现效果
在这里插入图片描述

4、源代码下载

https://download.csdn.net/download/qq_44757034/88582419

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/196251.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【有机化学(药学类)】醛和酮3

第一题 思考 格氏试剂与不饱和醛酮的亲核加成反应&#xff0c;主要发生1,2加成&#xff08;注意&#xff1a;氧原子算是1&#xff09; 第二题 思考 叶立德反应&#xff0c;看到磷原子就应该想到这个&#xff01; 第三题 思考 涉及到两个反应&#xff1a; 亲核加成反应&…

5. Jetson Orin Nano CUDA 配置

5. Jetson Orin Nano CUDA 配置 1&#xff1a;安装Jtop jtop安装主要有以下三个步骤&#xff1a; 安装pip3 我们需要使用pip3来安装jtop&#xff0c;所以先安装pip3 sudo apt install python3-pip安装jtop sudo -H pip3 install -U jetson-stats运行jtop服务 sudo -H pip3 in…

SAAS版专业级条码标签打印软件解决方案

一。新一代互联网打印模式 saas云标签打印软件支持条码、二维码、表格等模式组合打印&#xff0c;支持批量打印标签、表格模拟数据 、在线预览二维码打印 、在线条码生成打印标签 ● 条码/二维码/标签打印&#xff0c;支持表格批量打印标签&#xff1b; ● 条码/二维码尺寸…

每天一点python——day85

#每天一点Python——85 #python常见的异常类型&#xff1a; #如图&#xff1a; #①数学运算异常【由于会报错&#xff0c;我直接全部注释掉了】 print(10/0) 输出&#xff1a;ZeroDivisionError: division by zero#②索引错误list1[1,2,3,4] print(list1[5])#找索引为4的元素 输…

CUDA简介——同步

1. 引言 前序博客&#xff1a; CUDA简介——基本概念CUDA简介——编程模式CUDA简介——For循环并行化CUDA简介——Grid和Block内Thread索引CUDA简介——CUDA内存模式 本文重点关注Thread同步和Barriers。 Threads并行执行&#xff0c;可能存在如下问题&#xff1a; 1&#…

数学建模-基于集成学习的共享单车异常检测的研究

基于集成学习的共享单车异常检测的研究 整体求解过程概述(摘要) 近年来&#xff0c;共享单车的快速发展在方便了人们出行的同时&#xff0c;也对城市交通产生了一定的负面影响&#xff0c;其主要原因为单车资源配置的不合理。本文通过建立单车租赁数量的预测模型和异常检测模型…

智能优化算法应用:基于秃鹰算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于秃鹰算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于秃鹰算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.秃鹰算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…

HTML5+CSS3+Vue小实例:饮料瓶造型文字旋转特效

实例:饮料瓶造型文字旋转特效 技术栈:HTML+CSS+Vue 效果: 源码: 【HTML】【JS】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" …

根据关键词写作文章的软件,根据标题写作文章的工具

在当今信息化时代&#xff0c;人工智能技术的飞速发展&#xff0c;智能AI写作工具逐渐成为文案创作者的得力助手。这些工具不仅能够根据标题迅速生成文章&#xff0c;而且在提高创作效率的同时&#xff0c;也为我们节省了大量时间和精力。 人工智能的基本原理&#xff1a;人工智…

LeetCode(48)插入区间【区间】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 插入区间 1.题目 给你一个 无重叠的&#xff0c;按照区间起始端点排序的区间列表。 在列表中插入一个新的区间&#xff0c;你需要确保列表中的区间仍然有序且不重叠&#xff08;如果有必要的话&#xff0c;可以合并区间&am…

一天一个设计模式---生成器模式

概念 生成器模式&#xff08;Builder Pattern&#xff09;是一种创建型设计模式&#xff0c;用于构建复杂对象。它允许您逐步构造一个对象&#xff0c;将构建过程与最终对象的表示分离开来。这种模式通常用于创建复杂的对象&#xff0c;这些对象可能有多个部分组成&#xff0c…

MySQL系统函数

select version();查看mysql版本。 select user();可以查看数据库用户名。 select database();可以查看数据库名。 select system_use();可以查看系统用户名。 show variables like %basedir%;可以展示数据库读取路径。 show variables like %sets_dir%;可以看一下安…

Linux 下命令行启动与关闭WebLogic的相关服务

WebLogic 的服务器类型 WebLogic提供了三种类型的服务器&#xff1a; 管理服务器节点服务器托管服务器 示例和关系如下图&#xff1a; 对应三类服务器&#xff0c; 就有三种启动和关闭的方式。本篇介绍使用命令行脚本的方式启动和关闭这三种类型的服务器。 关于WebLogic 的…

优维产品最佳实践第17期:善用控制台

「 背 景 」 遇到页面报错时&#xff0c;是不是感到困扰&#xff0c;不知如何解决&#xff1f; 页面响应缓慢时&#xff0c;是否感到迷茫&#xff0c;不清楚从何入手排查&#xff1f; 面对主机高负载时&#xff0c;是不是觉得确认异常根因很有挑战&#xff1f; 本期最佳实践…

主动学习入门Week1

主动学习&#xff08;Active Learning&#xff09; 介绍实例详解模型分类基本查询策略经典方法应用方向引用 介绍 主动学习是一种通过主动选择最有价值的样本进行标注的机器学习或人工智能方法。其目的是使用尽可能少的、高质量的样本标注使模型达到尽可能好的性能。也就是说&…

Ant Design Pro初始化报错

今天按照官网步骤初始化项目&#xff0c;第一次报错 fatal: unable to access https://github.com/ant-design/ant-design-pro/: SSL certificate problem: unable to get local issuer certificate 致命&#xff1a;无法访问https://github.com/ant-design/ant-design-pro/&…

12.4_黑马MybatisPlus笔记(下)

目录 11 12 thinking&#xff1a;关于Mybatis Plus中BaseMapper和IService&#xff1f; 13 ​编辑 thinking&#xff1a;CollUtil.isNotEmpty? 14 thinking&#xff1a;Collection、Collections、Collector、Collectors&#xff1f; thinking&#xff1a;groupBy&#…

【Virtual Box】显示界面后无反应

本文记录本人在使用Virtual Box中遇到的问题 1.Virtual Box启动后无反应点击菜单栏是可用的&#xff0c;但界面里的无法操作 【解决方法】&#xff1a;以管理员身份启动virtual Box

数据结构 第5 6 章作业 图 哈希表 西安石油大学

第6章 图 1&#xff0e;选择题 &#xff08;1&#xff09;在一个图中&#xff0c;所有顶点的度数之和等于图的边数的&#xff08; &#xff09;倍。 A&#xff0e;1/2 B&#xff0e;1 C&#xff0e;2 D&#xff0e;4 答案&#xff1a…

linux 命令 tmux 用法详解

一、tmux 解决的痛点&#xff08;screen命令一样可以解决&#xff0c;但是tmux功能更强大&#xff09; 痛点一&#xff1a;大数据传输的漫长一夜 相信做过 Linux 服务运维的同学&#xff0c;都用 scp 进行过服务器间的大文件网络传输。一般这需要很长的时间&#xff0c;这期间…