🌟前言

哈喽小伙伴们,最近工作比较忙一直没有给大家更新,新的专栏 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>

🌟写在最后

更多Three知识以及API请大家持续关注,尽请期待。各位小伙伴让我们 let’s be prepared at all times!

08-08 22:04