纯js实现threejs导入mmd模型并播放动画

193 阅读3分钟

前言

本文可以实现对MMD模型的引入以及动作、镜头文件的绑定模型的功能,实现在web端播放MMD文件的效果。

须知

比较幽默的是threejs官方在r172版本把mmdloader给去除了

image.png

官方的说法

image.png

官方给出的补充方案(这个更幽默)

image.png

MMD相关知识

模型文件是pmx,模型会有贴图,不要只把mmd资源中的pmx单独提出来会导致模型加载的问题

动作文件和镜头文件是vmd,通常单独为一个文件。

我建议初学的人直接去模之屋找别人已经做好的,地址:www.aplaybox.com

技术版本

技术版本
node.js18.20.7
three.js0.170.0

three引入:

npm install three@0.170.0

主要代码内容

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link href="./styles/main.css" rel="stylesheet" type="text/css">
  <style>
    body {
      margin: 0;
    }
  </style>
  <script src="./node_modules/three/examples/jsm/libs/ammo.wasm.js"></script>
  <script type="module" src="./main.js"></script>
</head>

<body>
  <div id="scene-container">
  </div>
</body>


</html>

main.css

body {
  /* remove margins and scroll bars */
  margin: 0;
  overflow: hidden;

  /* style text */
  text-align: center;
  font-size: 12px;
  font-family: Sans-Serif;

  /* color text */
  color: #444;

  width: 100vw;
  height: 100vh;
}

h1 {
  /* position the heading */
  position: absolute;
  width: 100%;

  /* make sure that the heading is drawn on top */
  z-index: 1;
}

#scene-container {
  /* tell our scene container to take up the full page */
  position: absolute;
  width: 100%;
  height: 100%;

  /*
    Set the container's background color to the same as the scene's
    background to prevent flashing on load
  */
  background-color: black;
}

main.js

// 引入three.js
import * as THREE from 'three';
// 引入控制器
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// 引入MMDLoader
import { MMDLoader } from 'three/examples/jsm/loaders/MMDLoader.js';
import { MMDAnimationHelper } from 'three/examples/jsm/animation/MMDAnimationHelper.js';

// 引入容器元素
const container = document.querySelector('#scene-container');

// 创建场景
const scene = new THREE.Scene();
// 设置场景背景色
scene.background = new THREE.Color('#999999');

// 相机参数设置
const fov = 35; // 视角
const aspect = container.clientWidth / container.clientHeight; // 宽高比
const near = 0.1; // 最近距离
const far = 900; // 最远距离

// 创建相机
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);

// 设置相机位置
camera.position.z = 75;
camera.position.y = 2;
camera.position.x = 2;
// 设置相机朝向
camera.lookAt(0, 0, 0);

// 添加世界坐标辅助器,可选
// const axesHelper = new THREE.AxesHelper(5);

// scene.add(axesHelper);

// 创建渲染器
const renderer = new THREE.WebGLRenderer();
// 渲染器设置
renderer.setSize(container.clientWidth, container.clientHeight);

renderer.setPixelRatio(window.devicePixelRatio);

renderer.physicallyCorrectLights = true;
// 将渲染器dom添加到容器中
container.append(renderer.domElement);

// 创建控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置阻尼
controls.enableDamping = true;
// 设置阻尼系数
controls.dampingFactor = 0.05;
// 添加环境光
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
// 创建平行光
const light = new THREE.DirectionalLight(0xffffff, 1);
// 设置平行光位置
light.position.set(1, 1, 1);
// 添加平行光
scene.add(light);

// 创建MMDLoader
const mmdLoader = new MMDLoader();
// 创建MMDAnimationHelper
const helper = new MMDAnimationHelper();

// 时间轴
const clock = new THREE.Clock();
// 加载mmd
Ammo().then(function (AmmoLib) {
  Ammo = AmmoLib;
  mmdLoader.loadWithAnimation(
    '模型文件.pmx',// 替换为你所需要的模型文件路径
    '动作文件.vmd',// 替换为你所需要的动作文件路径
    function (mmd) {
      // console.log(mmd);
      // 回调中添加模型到场景中
      scene.add(mmd.mesh);
      helper.add(mmd.mesh, {
        animation: mmd.animation,
        physics: true,
      });

      mmdLoader.loadAnimation(
        '镜头文件.vmd',// 替换为你所需要的镜头文件路径
        camera,
        function (cameraAnimation) {
          helper.add(camera, {
            animation: cameraAnimation,
          });
        },
        null,
        null
      );

      // 执行渲染
      function animate() {
        controls.update();
        requestAnimationFrame(animate);
        helper.update(clock.getDelta());
        renderer.render(scene, camera);
      }
      animate();
    }
  );
});

重点关注

ammo.wasm.js文件可以自己从其他地方弄,但是我偷懒直接从依赖中引入了,这个一定要引入

效果图

image.png

结语

官方文档现在都没有mmd例子了,如果大家想看mmd的例子建议去找官方的旧文档下下来自己部署看,这只是一个简单的例子,还有一些内容深入得看各位的学习能力了