目录
- 🌟前言
- 🌟先看效果
- 🌟实现代码
- 🌟写在最后
🌟前言
哈喽小伙伴们,最近工作比较忙一直没有给大家更新,新的专栏 Three.js
第三篇,记录一下博主学习Three.js的过程;一起来看下吧。
🌟先看效果
Three-3D车模换肤
🌟实现代码
<template>
<div>
<div ref="canvas" class="canvas" />
<div class="car-color">
<div class="color1">
<div class="color-blue2" @click="setCarColor('#2e4e61')" />
<span>天际蓝</span>
</div>
<div class="color1">
<div class="color-white" @click="setCarColor('#c0c0c0')" />
<span>亮银色</span>
</div>
<div class="color1">
<div class="color-blank" @click="setCarColor('#222')" />
<span>星际黑</span>
</div>
<div class="color1">
<div class="color-red" @click="setCarColor('#ff0000')" />
<span>中国红</span>
</div>
<div class="color1">
<div class="color-green" @click="setCarColor('#9dc209')" />
<span>苹果绿</span>
</div>
<div class="color1">
<div class="color-blue" @click="setCarColor('#2443e2')" />
<span>雪邦蓝</span>
</div>
</div>
</div>
</template>
<script>
import * as THREE from 'three'
import { dracoLoader } from './dracoLoader.js'
// 导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
// 定义模型初始材质
const bodyMaterial = new THREE.MeshPhysicalMaterial({
color: '#2e4e61',
metalness: 1,
roughness: 0.5,
clearcoat: 1.0,
clearcoatRoughness: 0.03
})
export default {
data() {
return {
scene: null,
camera: null,
renderer: null,
controls: null,
animationMixer: null,
clock: null
}
},
computed: {},
watch: {},
mounted() {
this.initThree()
},
methods: {
initThree() {
// 创建场景
this.scene = new THREE.Scene()
// 创建天空盒
const path = '/skybox1/'
const urls = [
path + '6.png',
path + '3.png',
path + '2.png',
path + '1.png',
path + '5.png',
path + '4.png'
]
const textCube = new THREE.CubeTextureLoader().load(urls)
// textCube.encoding = THREE.sRGBEncoding
this.scene.background = textCube
// 创建相机 透视相机
// fov:角度 aspect:宽高比 near:近端 far:远端
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000)
// 设置相机位置
this.camera.position.set(-20, 20, -44)
this.scene.add(this.camera)
// 创建地面
const floorMat = new THREE.MeshStandardMaterial({
color: 0xa9a9a9 // 材质的颜色
})
const floorGeometry = new THREE.BoxGeometry(300, 300, 0.01, 1, 1, 1)
const floorMesh = new THREE.Mesh(floorGeometry, floorMat)
floorMesh.receiveShadow = true
floorMesh.rotation.x = -Math.PI / 2.0
this.scene.add(floorMesh)
this.animationMixer = new THREE.AnimationMixer(this.scene) // 常见动画混合器
// 时钟
this.clock = new THREE.Clock()
// 给场景增加环境光
// 设置环境光
this.scene.add(new THREE.AmbientLight(0xffffff, 0.5))
// 添加球光源
const hesLight = new THREE.HemisphereLight(0xffffff, 0x444444)
hesLight.intensity = 0.6
this.scene.add(hesLight)
// 自然光
// const dirLight = new THREE.DirectionalLight()
// dirLight.position.set(0, 0, 15)
// this.scene.add(dirLight)
const dirLight2 = new THREE.DirectionalLight()
dirLight2.position.set(0, 0, -15)
this.scene.add(dirLight2)
const dirLight3 = new THREE.DirectionalLight()
dirLight3.position.set(15, 0, 0)
this.scene.add(dirLight3)
const dirLight4 = new THREE.DirectionalLight()
dirLight4.position.set(-15, 0, 0)
this.scene.add(dirLight4)
const dirLight5 = new THREE.DirectionalLight()
dirLight5.position.set(0, 15, 0)
this.scene.add(dirLight5)
const dirLight6 = new THREE.DirectionalLight()
dirLight6.position.set(0, -15, 0)
this.scene.add(dirLight6)
const dirLight7 = new THREE.DirectionalLight()
dirLight7.position.set(5, 15, 5)
this.scene.add(dirLight7)
const dirLight8 = new THREE.DirectionalLight()
dirLight8.position.set(-5, -15, -5)
this.scene.add(dirLight8)
// 聚光灯
const sportLight = new THREE.SpotLight(0xffffff, 0.8)
sportLight.angle = Math.PI / 8 // 散射角度,跟水平线的夹角
sportLight.penumbra = 0.1 // 聚光锥的半影衰减百分比
sportLight.decay = 2 // 纵向:沿着光照距离的衰减量。
sportLight.distance = 10
sportLight.shadow.radius = 10
// 阴影映射宽度,阴影映射高度
sportLight.shadow.mapSize.set(512, 512)
sportLight.position.set(0, 15, 0)
// 光照射的方向
sportLight.target.position.set(0, 0, 0)
sportLight.castShadow = true
this.scene.add(sportLight)
// 加载模型
const modelUrl = '/3DModel/911-transformed.glb' // 定义所使用模型路径路径
dracoLoader(modelUrl)
.then((res) => {
// console.log(res.scene)
res.scene.name = '3dmodel'
res.scene.traverse(function(child) {
// console.log(child)
if (child.isMesh) {
child.frustumCulled = false
// 模型阴影
child.castShadow = true
// 模型自发
child.material.emissive = child.material.color
child.material.emissiveMap = child.material.map
}
// 获取不同部位
if (child.isMesh && child.name.indexOf('boot') !== -1 && child.name.indexOf('boot004') === -1) {
child.material = bodyMaterial
}
})
res.scene.scale.set(13, 13, 13)
res.scene.rotateY(Math.PI)
res.scene.rotation.z = Math.PI
res.scene.position.set(0, 9, 0)
this.scene.add(res.scene)
})
.catch((err) => {
console.log(err)
})
// 初始化渲染器
this.renderer = new THREE.WebGLRenderer()
this.renderer.shadowMap.enabled = true
this.renderer.antialias = true
// 设置渲染的尺寸大小
this.renderer.setSize(window.innerWidth, window.innerHeight)
this.renderer.setClearColor(0xffffff, 1.0)
// 监听屏幕大小的改变,修改渲染器的宽高和相机的比例:
window.addEventListener('resize', () => {
this.renderer.setSize(window.innerWidth, window.innerHeight)
this.camera.aspect = window.innerWidth / window.innerHeight
this.camera.updateProjectionMatrix()
})
// 将webgl渲染的canvas内容添加到body上
this.$refs.canvas.appendChild(this.renderer.domElement)
// 创建轨道控制器
this.controls = new OrbitControls(this.camera, this.renderer.domElement)
// 上下旋转范围
this.controls.minPolarAngle = 0// 默认值0
this.controls.maxPolarAngle = Math.PI / 2.1// 默认值Math.PI
this.controls.enableDamping = true // 开启阻尼
this.controls.dampingFactor = 0.1
this.controls.enableZoom = false
this.controls.addEventListener('change', (event) => {
console.log(event)
})
this.render()
},
render() {
this.controls && this.controls.update()// 每一帧都需要更新
this.animationMixer.update(this.clock.getDelta()) // 更新动画
this.renderer.render(this.scene, this.camera)
// 渲染下一帧的时候就会调用render函数
requestAnimationFrame(this.render)
},
setCarColor(color) {
console.log(color)
// 通过点击不同的色块,给初始的模型材质赋值
bodyMaterial.color.set(color)
}
}
}
</script>
<style scoped>
html, body {
overflow-y: hidden !important;
}
.canvas {
overflow-y: hidden;
overflow-x: hidden !important;
}
.car-color {
/* 设置这个div居中显示 */
margin: 0 auto;
position: fixed;
top: 10%;
right: -17%;
width: 40%;
height: auto;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
}
.color1 {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.color1 div {
width: 50px;
height: 50px;
border-radius: 80px;
cursor: pointer;
box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.3);
}
.color1 span {
color: #000;
}
.color-white {
background-color: #c0c0c0;
}
.color-blank {
background-color: #222;
}
.color-red {
background-color: #FF0000;
}
.color-green {
background-color: #9dc209;
}
.color-blue {
background-color: #2443e2;
}
.color-blue2 {
background-color: #2e4e61;
}
span {
margin-top: 5px;
}
</style>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
- 235.
- 236.
- 237.
- 238.
- 239.
- 240.
- 241.
- 242.
- 243.
- 244.
- 245.
- 246.
- 247.
- 248.
- 249.
- 250.
- 251.
- 252.
- 253.
- 254.
- 255.
- 256.
- 257.
- 258.
- 259.
- 260.
- 261.
- 262.
- 263.
- 264.
- 265.
- 266.
- 267.
- 268.
- 269.
- 270.
- 271.
- 272.
- 273.
- 274.
- 275.
- 276.
- 277.
- 278.
- 279.
- 280.
- 281.
- 282.
- 283.
- 284.
- 285.
- 286.
- 287.
- 288.
- 289.
- 290.
- 291.
- 292.
- 293.
🌟写在最后
更多Three知识以及API请大家持续关注,尽请期待。各位小伙伴让我们 let’s be prepared at all times!
✨原创不易,还希望各位大佬支持一下!
👍 点赞,你的认可是我创作的动力!
⭐️ 收藏,你的青睐是我努力的方向!
✏️ 评论,你的意见是我进步的财富!