three.js
需求一:导入3D模型车,修改车身的颜色
package.json
{"name": "car","private": true,"version": "0.0.0","type": "module","scripts": {"dev": "vite","build": "vite build","preview": "vite preview"},"dependencies": {"three": "^0.180.0","vue": "^3.5.21"},"devDependencies": {"@vitejs/plugin-vue": "^6.0.1","vite": "^7.1.7"}
}
CarScene.vue
<script setup lang='ts'>
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import * as THREE from "three";
import { onMounted, ref } from "vue";const container = ref()
let carBody;
const initScene = () => {//创建场景const scene = new THREE.Scene();//创建cameraconst camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)//创建渲染器const renderer = new THREE.WebGLRenderer()//设置渲染器的大小renderer.setSize(window.innerWidth / 2, window.innerHeight / 2)renderer.setClearColor(0x87CEEB)//将渲染器appendcontainer.value.appendChild(renderer.domElement)camera.position.set(0, 500, 150)const loader = new GLTFLoader()let modelloader.load('/src/assets/models/red_car.glb', (gltf) => {console.log('gltf', gltf);model = gltf.scenemodel.scale.set(3, 3, 3)// model旋转 60°model.rotation.y = Math.PI / 3scene.add(model)carBody = model.getObjectByName('carobjcleanermaterialmergergles')}, function (xhr) {},function (error) {console.log('An error happened');})const directionalLight = new THREE.DirectionalLight(0xffffff, 0.9);scene.add(directionalLight)//轨道const controls = new OrbitControls(camera, renderer.domElement)controls.update()function animate() {controls.update()renderer.render(scene, camera)}renderer.setAnimationLoop(animate)
}onMounted(() => {initScene()
})const colors = [{ value: '#000', name: '黑色' },{ value: '#fff', name: '白色' },{ value: '#1B6EF3', name: '蓝色' },{ value: '#41B883', name: '绿色' },
]
const changeColor = (color) => {if (!carBody) return;carBody.traverse((child) => {if (child.isMesh && child.material) {// 创建新材质或修改现有材质if (Array.isArray(child.material)) {// 处理材质数组child.material.forEach(material => {if (material instanceof THREE.MeshStandardMaterial) {material.color.set(color);material.needsUpdate = true;}});} else if (child.material.color) {//如果有贴图,那么修改颜色只会修改透明部分,所以需要为nullif (child.material.map) {child.material.map = null;}// 处理单个材质child.material.color.set(color);child.material.needsUpdate = true;}}});
}</script>
<template><div class="car-wrap"><div class="btn-color" v-for="(item, index) in colors" :key="index" @click="changeColor(item.value)":style="{ 'backgroundColor': item.value }"></div><div ref="container" class="three-container"></div></div>
</template>
<style scoped>
.car-wrap {background: #ccc;
}.btn-color {width: 50px;height: 30px;margin: 10px auto;
}
</style>
需求二:添加GridHelper,无线循环移动GridHelper,视角效果是车在移动。
MoveScene.vue
<script setup lang='ts'>
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import * as THREE from "three";
import { onMounted, ref } from "vue";const container = ref()
let carBody;
//网格大小
const gridSize = 1000;
const initScene = () => {//创建场景const scene = new THREE.Scene();//创建cameraconst camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)//创建渲染器const renderer = new THREE.WebGLRenderer({antialias: true})//设置渲染器的大小renderer.setSize(window.innerWidth / 2, window.innerHeight / 2)renderer.setClearColor(0xffffff)// Gridconst divisions = 50; //网格被分割成多少份const gridHelper = new THREE.GridHelper(gridSize, divisions);scene.add(gridHelper);// camera.position.set(0, 500, 150)camera.position.set(0, 500, 150);//将渲染器appendcontainer.value.appendChild(renderer.domElement)const loader = new GLTFLoader()let modelloader.load('/src/assets/models/red_car.glb', (gltf) => {console.log('gltf', gltf);model = gltf.scenemodel.scale.set(3, 3, 3)// model旋转 90°model.rotation.y = Math.PI / 2scene.add(model)carBody = model.getObjectByName('carobjcleanermaterialmergergles')}, function (xhr) {},function (error) {console.log('An error happened');})const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);scene.add(directionalLight)//轨道const controls = new OrbitControls(camera, renderer.domElement)controls.update()function animate() {controls.update()gridHelper.position.x += 5//当网格移出屏幕时,让网格重新出现做屏幕左侧,实现无线循环if (gridHelper.position.x > gridSize) {gridHelper.position.x = -gridSize}renderer.render(scene, camera)}renderer.setAnimationLoop(animate)
}onMounted(() => {initScene()
})const colors = [{ value: '#000', name: '黑色' },{ value: '#fff', name: '白色' },{ value: '#1B6EF3', name: '蓝色' },{ value: '#41B883', name: '绿色' },
]
const changeColor = (color) => {if (!carBody) return;carBody.traverse((child) => {if (child.isMesh && child.material) {// 创建新材质或修改现有材质if (Array.isArray(child.material)) {// 处理材质数组child.material.forEach(material => {if (material instanceof THREE.MeshStandardMaterial) {material.color.set(color);material.needsUpdate = true;}});} else if (child.material.color) {//如果有贴图,那么修改颜色只会修改透明部分,所以需要为nullif (child.material.map) {child.material.map = null;}// 处理单个材质child.material.color.set(color);child.material.needsUpdate = true;}}});
}</script>
<template><div class="car-wrap"><div class="btn-color" v-for="(item, index) in colors" :key="index" @click="changeColor(item.value)":style="{ 'backgroundColor': item.value }"></div><div ref="container" class="three-container"></div></div>
</template>
<style scoped>
.car-wrap {background: #ccc;
}.btn-color {width: 50px;height: 30px;margin: 10px auto;
}
</style>