0.前言
前两天用 VRoid Studio 捏了一个 3D 的 loli ,后来想想能不能把这个 3D 形象替换到那个博客站点的原来的 Live2D 看板娘那边去。花了一上午时间在 GitHub 上找相关的 Demo,又花一下午带一晚上反复调试,终于把这个 3D 看板娘调通了。
手机端暂时还没有实现出来效果,主要是尺寸对不上,不过也就是在那个 JS 里面调一下尺寸的事情。晚点再来研究研究。
我们找的这个 GitHub 代码是 Momijinn/SampleWebMMD ,他实现了在网页上加载 PMX 格式的 MMD 模型、 VMD 格式的动作,以及相应的音频档案,调用的是 Three.js 这个库,以及 MMDLoader 这个插件,这个库鄙人也没有研究过,只是之前没跳槽之前看产品经理在业务群里面发过这个库的演示页面,鄙人稍微瞅了一眼,毕竟鄙人不是前端。。。
1.修改 SampleWebMMD
把他的代码拉下来,然后做一些必要的修改。
index.html
里面原来的几个手动切换动作的按钮去掉,不要。
把捏好并转换好的 .pmx
的模型连同相应的相对路径的贴图文件丢进pmx
目录,最好pmx
里面再建一个子目录再丢。
把做好的.vmd
的动作文件丢进vmd
目录。
然后修改 JS。
原来的模型太亮了,去掉全局光,改成点光。 原来的
Init = () => {
scene = new THREE.Scene();
const ambient = new THREE.AmbientLight(0xeeeeee);
scene.add(ambient);
renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xcccccc, 0);
// documentにMMDをセットする
document.body.appendChild(renderer.domElement);
//cameraの作成
camera = new THREE.PerspectiveCamera(40, windowWidth / windowHeight, 1, 1000);
camera.position.set(0, 18, 10);
}
改成
Init = () => {
scene = new THREE.Scene();
const ambient = new THREE.AmbientLight(0xcccccc);
// scene.add(ambient);
spotLight = new THREE.SpotLight();
spotLight.color = new THREE.Color(0xffffff);
spotLight.castShadow = true;
spotLight.position.set(0, -10, 60);
// 光的强度 默认值为1
spotLight.intensity = 1;
// 从发光点发出的距离,光的亮度,会随着距离的远近线性衰减
spotLight.distance = 350;
// 光色散角度,默认是 Math.PI * 2
spotLight.angle = 0.4;
// 光影的减弱程度,默认值为0, 取值范围 0 -- 1之间
spotLight.penumbra = 0.1;
// 光在距离上的量值, 和光的强度类似(衰减指数)
spotLight.decay = 1;
// 设置阴影分辨率
spotLight.shadow.mapSize.width = 1024;
spotLight.shadow.mapSize.height = 1024;
// 投影近点 --> 从距离光源的哪一才产生阴影
spotLight.shadow.camera.near = 0.1;
// 投影原点 --> 到光源的哪一点位置不产生阴影
spotLight.shadow.camera.far = 300;
// 投影视场
spotLight.shadow.camera.fov = 40;
scene.add(spotLight);
renderer = new THREE.WebGLRenderer({ alpha: true });
effect = new THREE.OutlineEffect( renderer, {
defaultThickness: 0.01,
defaultColor: [ 0, 0, 0 ],
defaultAlpha: 0.8,
defaultKeepAlive: true // keeps outline material in cache even if material is removed from scene
} );
renderer.setPixelRatio(window.devicePixelRatio);
// renderer.setSize(window.innerWidth, window.innerHeight);
if (window.innerWidth < window.innerHeight) document.body.remove(); // 手机端暂不展示
renderer.setSize(480 * window.innerWidth / window.innerHeight, 480);
renderer.setClearColor(0xcccccc, 0);
renderer.domElement.style.pointerEvents = 'none';
// documentにMMDをセットする
document.body.appendChild(renderer.domElement);
//cameraの作成
camera = new THREE.PerspectiveCamera(40, windowWidth / windowHeight, 1, 1000);
camera.position.set(0, 18, 52);
effect.render( scene, camera );
}
每次动作执行完还要停两秒才循环下一次,去掉这个停顿。 原来的
helper = new THREE.MMDAnimationHelper({ afterglow: 2.0, resetPhysicsOnLoop: true });
改成
helper = new THREE.MMDAnimationHelper({ afterglow: 0.0, resetPhysicsOnLoop: true });
把他加载的模型改成我们自己的模型 ,原来的
const Pmx = "./pmx/pronama/プロ生ちゃん.pmx";
改成
const Pmx = "./pmx/mlj/mlj.pmx"; // 这里改成你准备好的 pmx 文件的路径
把他加载的动作改成我们自己的动作。 原来的
const MotionObjects = [
{ id: "loop", VmdClip: null, AudioClip: false },
{ id: "kei_voice_009_1", VmdClip: null, AudioClip: true },
{ id: "kei_voice_010_2", VmdClip: null, AudioClip: true },
];
改成
const MotionObjects = [
{ id: "float_with_rotate", VmdClip: null, AudioClip: false }, // 改成你的 vmd 文件名,不要后缀
{ id: "kei_voice_009_1", VmdClip: null, AudioClip: false },
{ id: "kei_voice_010_2", VmdClip: null, AudioClip: false },
{ id: "loop", VmdClip: null, AudioClip: false },
];
原来的
//Set VMD on Mesh
VmdControl("loop", true);
改成
//Set VMD on Mesh
VmdControl("float_with_rotate", true); // 改成你的 vmd 文件名,不要后缀
2.部署到对象存储
然后把改好的整个仓库的文件上传到一个网络空间。因为贴图有十几兆大小,国外的空间速度肯定慢,还是找一个阿里云或者别的什么云的对象存储或者CDN丢上去,得要那种文件名不变且可以带有子路径的那种。鄙人用的是百度智能云。但是这样的话流量就是个问题,如果网站流量比较大的话就要考虑其他方式了,因为对象存储是按流量计费的。
3.修改 Live2D Wrapper
之前我们用的是 galnetwen 做的 Live2D Wrapper,我们就直接在他的基础上修改相关的 NJK 文件。我们之前已经把引用的 JS 放进了单独的 NJK 文件,然后用布局 NJK 连接的。将 themes/next/layout/_third-party/live2d/live2d.njk
中的
<script type="text/javascript">
var message_Path = '/live2d/'
var home_Path = 'https://haremu.com/' //此处修改为你的域名,必须带斜杠
</script>
<script type="text/javascript" src="/live2d/js/live2d.js"></script>
<script type="text/javascript" src="/live2d/js/message.js"></script>
<script type="text/javascript">
loadlive2d("live2d", "/live2d/model/tia/model.json");
</script>
直接去掉,或者注释掉。
然后再把前面的
<div id="landlord">
<div class="message" style="opacity:0"></div>
<canvas id="live2d" width="280" height="250" class="live2d"></canvas>
<div class="hide-button">隐藏</div>
</div>
改为
<div id="landlord">
<div class="message" style="opacity:0"></div>
<!-- <canvas id="live2d" width="280" height="550" class="live2d"></canvas> -->
<iframe src="放置显示 3D 模型的 HTML 页面 URL" frameborder="no" style="pointer-events: none; width: 100%; height: 100%"></iframe>
<div class="hide-button">隐藏</div>
</div>
其中的 iframe
需要改为第二大块你放在 CDN 或者对象存储的HTML文件地址。
然后通过 CSS 控制模型展示的大小,还有不能影响鼠标点击网页元素了。鄙人写的是
<style>
#landlord {
bottom: -50px;
width: 1000px;
height: 550px;
left: -300px;
pointer-events: none;
}
#live2d {
display: block;
height: 100%;
width: 100%;
}
</style>
如果之前没有用过这个 Live2D 的话,还要在 head
里面加一个 CSS 文件。在themes\next\layout\_partials\head\head.njk
中添加
<link rel="stylesheet" href="/live2d/css/live2d.css" />
其中的 href
改成你准备好的这个 wrapper 的 CSS 的URL。
此外,鄙人是单独把原来 Live2D 的 Wrapper 放到单独的 NJK 里面了。布局的 NJK themes\next\layout\_third-party\index.njk
是这样的。
{% include 'baidu-push.njk' %}
{% include 'rating.njk' %}
{%- if theme.algolia_search.enable %}
{% include 'search/algolia-search.njk' %}
{% elif theme.swiftype_key %}
{% include 'search/swiftype.njk' %}
{% elif theme.local_search.enable %}
{% include 'search/localsearch.njk' %}
{%- endif %}
{% include 'live2d/live2d.njk' %}
{% include 'chat/chatra.njk' %}
{% include 'chat/tidio.njk' %}
{% include 'tags/pdf.njk' %}
{% include 'tags/mermaid.njk' %}
4.效果
这样就基本完成了修改。效果是这样的。