一、项目初始化
npm install -g vite
npm init vite@latest threejs-vite-vue -- --template vue
cd threejs-vite-vue
npm install
npm run dev
项目创建成功
注意threejs的版本
"@types/three": "^0.155.1",
项目创建成功在IDE当中导入项目
1、添加一些依赖项
npm install vue-router
npm install three
npm install @types/three -D
npm 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、绘制板子,立方体,球体
<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、材质和光照
设置导航菜单组件
<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 = true
const 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 = 0
scene.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、材质和光照和动画
复制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 = true
const 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 = 0
scene.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、性能监控
安装一个插件
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、交互控制
复制一下index4.vue 为index5.vue