使用vue学习three.js之创建动画-变形动画-通过设置morphTargetInfluences属性创建动画

Posted 点燃火柴

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用vue学习three.js之创建动画-变形动画-通过设置morphTargetInfluences属性创建动画相关的知识,希望对你有一定的参考价值。

1.demo效果

在这里插入图片描述

2. 实现要点

2.1 Geometry到BufferGeometry

最新的three.js中的 updateMorphTargets 方法不再支持Geometry几何体,需要使用 BufferGeometry 替代,而且原来Geometry对象中的morphTargets属性BufferGeometry中也没有,不过有替换方案。
查阅得知BufferGeometry有一个 morphAttributes 属性,用来存储了几何体的 morphTargets 信息,
这里可以存放顶点信息-position,也可以存放法线信息-normal

例如要存放两个目标形状的顶点信息:

geometry.morphAttributes.position = [
  new THREE.BufferAttribute(new Float32Array(array1), 3),
  new THREE.BufferAttribute(new Float32Array(array2), 3),
]

3.2 创建带有变形目标的网格对象

这里首先通过BoxBufferGeometry对象创建一个原几何体对象,两个目标几何体对象,然后将原几何体对象的morphAttributes属性的顶点信息使用两个目标几何体对象的顶点信息填充

// 创建立方体
var cubeGeometry = new THREE.BoxBufferGeometry(4, 4, 4)
var cubeMaterial = new THREE.MeshLambertMaterial({
  morphTargets: true,
  color: 0xff0000
})

// 创建morphtargets, 用来获取顶点信息
var cubeTarget1 = new THREE.BoxBufferGeometry(2, 10, 2)
var cubeTarget2 = new THREE.BoxBufferGeometry(8, 2, 8)

// 设置morphAttributes
cubeGeometry.morphAttributes.position = [
  new THREE.BufferAttribute(cubeTarget2.attributes.position.array, 3),
  new THREE.BufferAttribute(cubeTarget1.attributes.position.array, 3)
]

this.cube = new THREE.Mesh(cubeGeometry, cubeMaterial)

3.3 render中更新动画

render() {
  this.cube.morphTargetInfluences[0] = this.properties.influence1.value
  this.cube.morphTargetInfluences[1] = this.properties.influence2.value
  this.renderer.render(this.scene, this.camera)
  requestAnimationFrame(this.render)
}

4. demo代码

<template>
  <div>
    <div id="container" />
    <div class="controls-box">
      <section>
        <el-row>
          <div v-for="(item,key) in properties" :key="key">
            <div v-if="item&&item.name!=undefined">
              <el-col :span="8">
                <span class="vertice-span">{{ item.name }}</span>
              </el-col>
              <el-col :span="12">
                <el-slider v-model="item.value" :min="item.min" :max="item.max" :step="item.step" :format-tooltip="formatTooltip" />
              </el-col>
              <el-col :span="3">
                <span class="vertice-span">{{ item.value }}</span>
              </el-col>
            </div>
          </div>
        </el-row>
      </section>
    </div>
  </div>
</template>

<script>
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
export default {
  data() {
    return {
      properties: {
        influence1: {
          name: 'influence1',
          value: 0.01,
          min: 0,
          max: 1,
          step: 0.001
        },
        influence2: {
          name: 'influence2',
          value: 0.01,
          min: 0,
          max: 1,
          step: 0.001
        }
      },
      cube: null,
      camera: null,
      scene: null,
      renderer: null,
      controls: null
    }
  },
  mounted() {
    this.init()
  },
  methods: {
    formatTooltip(val) {
      return val
    },

    // 初始化
    init() {
      this.createScene() // 创建场景

      this.createLight() // 创建光源
      this.createCamera() // 创建相机
      this.createRender() // 创建渲染器
      this.createModels() // 创建模型
      this.createControls() // 创建轨道控制器
      this.render() // 渲染
    },
    // 创建场景
    createScene() {
      this.scene = new THREE.Scene()
    },
    // 创建模型
    createModels() {
      // 创建一个平面
      var planeGeometry = new THREE.PlaneGeometry(20, 20, 1, 1)
      var planeMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff })
      var plane = new THREE.Mesh(planeGeometry, planeMaterial)
      plane.rotation.x = -0.5 * Math.PI
      this.scene.add(plane)

      this.createCube() // 创建立方体
    },
    createCube(e) {
      // 创建立方体
      var cubeGeometry = new THREE.BoxBufferGeometry(4, 4, 4)
      var cubeMaterial = new THREE.MeshLambertMaterial({
        morphTargets: true,
        color: 0xff0000
      })

      // 创建morphtargets, 用来获取顶点信息
      var cubeTarget1 = new THREE.BoxBufferGeometry(2, 10, 2)
      var cubeTarget2 = new THREE.BoxBufferGeometry(8, 2, 8)

      // 设置morphAttributes
      cubeGeometry.morphAttributes.position = [
        new THREE.BufferAttribute(cubeTarget2.attributes.position.array, 3),
        new THREE.BufferAttribute(cubeTarget1.attributes.position.array, 3)
      ]

      this.cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
      this.cube.position.set(0, 3, 0)
      this.scene.add(this.cube)
    },

    // 创建光源
    createLight() {
      // 环境光
      const ambientLight = new THREE.AmbientLight(0x0c0c0c) // 创建环境光
      this.scene.add(ambientLight) // 将环境光添加到场景

      const spotLight = new THREE.SpotLight(0xffffff)
      spotLight.position.set(-25, 25, 15)
      this.scene.add(spotLight)
    },
    // 创建相机
    createCamera() {
      const element = document.getElementById('container')
      const width = element.clientWidth // 窗口宽度
      const height = element.clientHeight // 窗口高度
      const k = width / height // 窗口宽高比
      // PerspectiveCamera( fov, aspect, near, far )
      this.camera = new THREE.PerspectiveCamera(45, k, 0.1, 1000)
      this.camera.position.set(-15, 15, 15) // 设置相机位置

      this.camera.lookAt(new THREE.Vector3(0, 0, 0)) // 设置相机方向
      this.scene.add(this.camera)
    },
    // 创建渲染器
    createRender() {
      const element = document.getElementById('container')
      this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
      this.renderer.setSize(element.clientWidth, element.clientHeight) // 设置渲染区域尺寸
      this.renderer.setClearColor(0xeeeeee, 1) // 设置背景颜色
      element.appendChild(this.renderer.domElement)
    },

    render() {
      this.cube.morphTargetInfluences[0] = this.properties.influence1.value
      this.cube.morphTargetInfluences[1] = this.properties.influence2.value
      this.renderer.render(this.scene, this.camera)
      requestAnimationFrame(this.render)
    },
    // 创建轨道控制器
    createControls() {
      this.controls = new OrbitControls(this.camera, this.renderer.domElement)
    }
  }
}
</script>

<style>
#container {
  position: absolute;
  width: 100%;
  height: 100%;
}
.controls-box {
  position: absolute;
  right: 5px;
  top: 5px;
  width: 300px;
  padding: 10px;
  background-color: #fff;
  border: 1px solid #c3c3c3;
}
.vertice-span {
  line-height: 38px;
  padding: 0 8px 0 10px;
}
</style>

以上是关于使用vue学习three.js之创建动画-变形动画-通过设置morphTargetInfluences属性创建动画的主要内容,如果未能解决你的问题,请参考以下文章

使用vue学习three.js之创建动画-使用外部模型创建动画,使用GLTF格式文件动画创建动画

使用vue学习three.js之创建动画-创建骨骼动画,使用SkinnedMesh制作蒙皮

使用vue学习three.js之创建动画-使用外部模型创建动画,使用Blender导出的JSON模型创建动画

Three.JS提升学习6:创建动画和移动摄像机

将变形目标从 Blender 导出到 Three.js

vue three.js 结合tween.js 实现动画过渡