Halo 部署 Live2D 模型

受到 GitHub - alsdhkauuhw/halo-plugin-live2D: live2D for halo-plugin 项目启发,尝试通过Halo插件实现前端Live2D的渲染,中间遇到了不少坑,在此记录一下。

前端框架选择

目前Live2D的生态是基于Cubism的,受到闭源限制和其提供SDK的开发难度,目前可简易使用的前端包只有 guansss/pixi-live2d-displayLSTM-Kirigaya/Live2dRender。因为基于pixi的框架可更好的调用Cubism内部API,故在此之上进行开发。但是这个项目已经几乎停止维护了,所以使用它的兼容Cubism 5.0和PixiJS 8的fork omniwaifu/pixi-live2d5 进行开发。在之后的开发中,也遇到不少底层问题,所以直接从他的playground项目入手进行改了,这样可以直接操作原始代码,而不是外部导入。当然这种方式也会导致项目难以更新,但是既然是个人使用所以就不管了。

纹理异常 / MaskBufferCount

在初次部署模型的时候遇到了texture图层混乱的问题,模型渲染异常。之前以为是模型问题,但是viewer里一切正常,尝试了很久也没有解决。最后通过打开logging发现遇到了MaskBufferCount不够导致的报错问题,需要调用initialize函数处设置足够的MaskBufferCount(Cubism5InternalModel类中的init方法里有renderer的initialize)。

这些代码处于Cubism自己的 Live2D/CubismWebFramework 中,所以需要进行修改

/**
 * WebGL用の描画命令を実装したクラス
 */
export class CubismRenderer_WebGL extends CubismRenderer {
  /**
   * レンダラの初期化処理を実行する
   * 引数に渡したモデルからレンダラの初期化処理に必要な情報を取り出すことができる
   *
   * @param model モデルのインスタンス
   * @param maskBufferCount バッファの生成数
   */
  public initialize(model: CubismModel, maskBufferCount = 1): void {
    if (model.isUsingMasking()) {
      this._clippingManager = new CubismClippingManager_WebGL(); // クリッピングマスク・バッファ前処理方式を初期化
      this._clippingManager.initialize(model, maskBufferCount);
    }

    this._sortedDrawableIndexList.resize(model.getDrawableCount(), 0);

    super.initialize(model); // 親クラスの処理を呼ぶ

参数应用不生效 / CubismIdHandle

不论是现有大部分文章,还是代码补全提示,推荐的设置参数值的方式都是使用如下代码:

coreModel.setParameterValueById("ParamBreath", 1);

但是这种方法在5.X中已经失效了,因为这类字符串已经全部被替换为CubismIdHandle类型。需要通过如下方法从字符串获得CubismIdHandle。

coreModel.setParameterValueById(CubismFramework.getIdManager().getId("ParamBreath"), 1);

分辨率 / 抗锯齿

为了解决在高分辨率下的渲染画质过低问题,需要向PixiJS设置以下参数。

await app.init({
    canvas,
    autoStart: true,
    resizeTo: container,
    background: 0x000000,
    backgroundAlpha: 0,
    preference: "webgl",
+   resolution: window.devicePixelRatio || 1,
+   autoDensity: true,
+   antialias: true,
});

IOS端滚动时渲染闪烁

IOS端上滚动时可能会出现模型闪烁消失的问题。这个问题来自于PixiJS的WebGL渲染,通过以下代码可以解决。

if (isMobile) {
    let isScrolling = false;
    let scrollTimer: number;

    const handleScroll = () => {
        if (!isScrolling) {
            isScrolling = true;
            app.render(); // 重要
            app.renderer.gl.flush(); // 重要
            requestAnimationFrame(() => {
                
                isScrolling = false;
            });
        }

        clearTimeout(scrollTimer);
        scrollTimer = window.setTimeout(() => {
            app.render(); // 重要
            app.renderer.gl.flush(); // 重要
        }, 100);
    };

    window.addEventListener('scroll', handleScroll, { passive: true });
    window.addEventListener('touchmove', handleScroll, { passive: true });
}

基本就是滚动时触发render和gl.flush。.renderer.gl 可能会提示类型错误,忽略即可(或者用as any),因为PixiJS不只有WebGL可用。

优化:使用OSS提供模型文件和IndexDB缓存

如果你的服务器带宽不够,加载较大的模型可能速度会慢,一种方法是使用IndexDB进行缓存,另一种方法就是使用OSS提供,使用起来基本没有问题(也可以两者一起使用)。如果使用IndexDB,可以代理cubism中用于获取模型的XHRLoader类。

Comments