CubismNativeFramework(D3D11)でモデルの行列を変更したときにマスク描画が正しくされないことについて

【Cubism SDKのバージョン】Cubism 5 SDK for Native R5
【OS名・バージョン】Windows11
【開発使用ツール】Visual Studio 2026
【不具合の状況】
CubismNativeFramework (D3D11)を用いたプログラムを作成していてサンプルデータ Ren を表示できるかテストしたところ瞳のテクスチャが描画されない問題が発生しました。
瞳のテクスチャはマスク生成してそれを合成する処理がされているようですが、そのときのマスクの位置か何かがずれてるのでしょうか?

私のプログラムではモデルの描画時にパースのついたプロジェクション行列をかけているためZ位置なども同時に調整しています。
この処理が原因でこの問題が発生していると思いますがどのように修正すれば対応可能でしょうか?

試しにサンプルプロジェクトを用い症状を発生させてみました

ファイル LAppLive2DManager.cpp 関数 OnUpdate()

 #if (0)
        if (model->GetModel()->GetCanvasWidth() > 1.0f && windowWidth < windowHeight)
        {
            // 横に長いモデルを縦長ウィンドウに表示する際モデルの横サイズでscaleを算出する
            model->GetModelMatrix()->SetWidth(2.0f);
            projection.Scale(1.0f, static_cast<float>(windowWidth) / static_cast<float>(windowHeight));
        }
        else
        {
            projection.Scale(static_cast<float>(windowHeight) / static_cast<float>(windowWidth), 1.0f);
        }
#else
        float fov = DirectX::XMConvertToRadians(45.0f);
        float aspect = (float)windowWidth / (float)windowHeight;
        float nearZ = 0.01f;
        float farZ = 100.0f;
        DirectX::XMMATRIX persp = DirectX::XMMatrixPerspectiveFovLH(fov, aspect, nearZ, farZ);
        DirectX::XMMATRIX trans = DirectX::XMMatrixTranslation(0, 0, 2.5f);
        DirectX::XMMATRIX mat = trans * persp;
        projection.SetMatrix((float*)mat.r);
#endif

        // 必要があればここで乗算
        if (_viewMatrix != NULL)
        {
            projection.MultiplyByMatrix(_viewMatrix);
        }

よろしくお願いいたします。

@wanwanswan

いつも弊社製品をご愛顧のほど誠にありがとうございます。
Lvie2D スタッフでございます。

このたびは描画時の不具合によりご迷惑をおかけし、誠に申し訳ございません。

調査の結果、オフスクリーン描画でマスクを使用する際に、mvp行列の逆行列を計算する過程でw成分が1.0以外(ご提供いただいたサンプルコードでは2.5)となっている場合、シェーダー内でのw除算によりUV座標のスケールがずれ、正しいマスク範囲が表示されないことが判明いたしました。これまで正射影行列を前提としていたため、透視投影行列を用いたケースでは問題が発生しておりました。

現在のSDKにおいては、逆行列を求める前にw成分で正規化を行う処理を追加することで解決可能です。この修正は次回リリースで対応予定です。

お急ぎの場合は、以下の修正をSDKに適用いただけますと幸いです。

Framework\src\Math\CubismMatrix44.cpp に正規化関数の実装の追加

CubismMatrix44 CubismMatrix44::GetNormalizedByW() const
{
    const csmFloat32 w = _tr[15];

    if (CubismMath::AbsF(w) < CubismMath::Epsilon)
    {
        return *this;
    }

    CubismMatrix44 dst;

    // X
    dst._tr[0] = _tr[0] / w;
    dst._tr[4] = _tr[4] / w;
    dst._tr[8] = _tr[8] / w;
    dst._tr[12] = _tr[12] / w;
    // Y
    dst._tr[1] = _tr[1] / w;
    dst._tr[5] = _tr[5] / w;
    dst._tr[9] = _tr[9] / w;
    dst._tr[13] = _tr[13] / w;
    // Z
    dst._tr[2] = _tr[2] / w;
    dst._tr[6] = _tr[6] / w;
    dst._tr[10] = _tr[10] / w;
    dst._tr[14] = _tr[14] / w;
    // W
    dst._tr[3] = 0.0f;
    dst._tr[7] = 0.0f;
    dst._tr[11] = 0.0f;
    dst._tr[15] = 1.0f;

    return dst;
}

Framework\src\Math\CubismMatrix44.hpp に正規化関数の宣言の追加

CubismMatrix44 GetNormalizedByW() const;

Framework\src\Rendering\CubismClippingManager.tpp に正規化関数の呼び出しの追加 267,268行目当たりの修正

修正前
// clipCpntext * mvp^-1
CubismMatrix44 invertMvp = mvp.GetInvert();

修正後
// clipContext * normalizedMvp^-1
CubismMatrix44 invertMvp = mvp.GetNormalizedByW().GetInvert();

ご不明な点がございましたら、お気軽にお問い合わせください。 引き続きどうぞよろしくお願いいたします。

ご対応ありがとうございます。早速お教えいただきましたコードで試してみました。

確かにサンプルで再現したコードでは問題はありませんでしたが、行列に回転が加わると瞳の描画が安定しませんでした。

以下のようにファイル LAppLive2DManager.cpp 関数 OnUpdate()の変更部分を変更して試すと瞳の一部が欠ける感じです

#else
        float fov = DirectX::XMConvertToRadians(45.0f);
        float aspect = (float)windowWidth / (float)windowHeight;
        float nearZ = 0.01f;
        float farZ = 100.0f;
        DirectX::XMMATRIX persp = DirectX::XMMatrixPerspectiveFovLH(fov, aspect, nearZ, farZ);
        DirectX::XMMATRIX trans = DirectX::XMMatrixTranslation(0, 0, 2.5f);
		float angle = 15;
        DirectX::XMMATRIX rot = DirectX::XMMatrixRotationY(DirectX::XMConvertToRadians(angle));
        DirectX::XMMATRIX mat = rot * trans * persp;
        projection.SetMatrix((float*)mat.r);
#endif

@wanwanswan
Lvie2D スタッフでございます。

行列の回転の考慮が漏れており、重ねてご不便をおかけしてしまい大変申し訳ございません。

再度調査を行い、修正コードを作成致しました。
前回の回答での修正コードは破棄いただいたうえで、以下の修正コードにて再度ご確認いただければと思います。

Framework\src\Math\CubismMatrix44.cpp にMatrix配列ゲッターの実装の追加

const csmFloat32* CubismMatrix44::GetArray() const
{
    return _tr;
}

Framework\src\Math\CubismMatrix44.hpp にMatrix配列ゲッターの宣言の追加

const csmFloat32*     GetArray() const;

Framework\src\Rendering\D3D11\CubismRenderer_D3D11.cpp のオフスクリーン描画時の処理の修正 316行目辺り

修正前
if (drawableObjectType == CubismRenderer::DrawableObjectType_Offscreen)
{
    // clipContext * mvp^-1
    CubismMatrix44 invertMvp = renderer->GetMvpMatrix().GetInvert();
    clipContext->_matrixForDraw.MultiplyByMatrix(&invertMvp);
}

修正後
if (drawableObjectType == CubismRenderer::DrawableObjectType_Offscreen)
{
    CubismMatrix44 projClip = CubismRenderer::CreateOffscreenMaskClipMatrix(renderer->GetMvpMatrix(), clipContext->_matrixForDraw);
    clipContext->_matrixForDraw.SetMatrix(projClip.GetArray());
}

Framework\src\Rendering\CubismClippingManager.tpp のオフスクリーン描画時の処理の修正

修正前
if (CubismRenderer::DrawableObjectType_Offscreen == drawableObjectType)
{
    // clipCpntext * mvp^-1
    CubismMatrix44 invertMvp = mvp.GetInvert();
    clipContext->_matrixForDraw.MultiplyByMatrix(&invertMvp);
}

修正後
if (CubismRenderer::DrawableObjectType_Offscreen == drawableObjectType)
{
    CubismMatrix44 projClip = CubismRenderer::CreateOffscreenMaskClipMatrix(mvp, clipContext->_matrixForDraw);
    clipContext->_matrixForDraw.SetMatrix(projClip.GetArray());
}

Framework\src\Rendering\CubismRenderer.cpp にオフスクリーン描画用マスクのMatrix定義関数の実装の追加

CubismMatrix44 CubismRenderer::CreateOffscreenMaskClipMatrix(const CubismMatrix44& mvp, const CubismMatrix44& matrixForDraw)
{
    // 3x3行列を抽出
    const csmFloat32* const mvpArray = mvp.GetArray();
    const csmFloat32 r00 = mvpArray[0];
    const csmFloat32 r01 = mvpArray[1];
    const csmFloat32 r02 = mvpArray[3];
    const csmFloat32 r10 = mvpArray[4];
    const csmFloat32 r11 = mvpArray[5];
    const csmFloat32 r12 = mvpArray[7];
    const csmFloat32 r20 = mvpArray[12];
    const csmFloat32 r21 = mvpArray[13];
    const csmFloat32 r22 = mvpArray[15];

    // 3×3行列の逆行列を計算
    const csmFloat32 det = r00 * (r11 * r22 - r12 * r21) -
        r01 * (r10 * r22 - r12 * r20) +
        r02 * (r10 * r21 - r11 * r20);

    CubismMatrix44 dst;
    if (CubismMath::AbsF(det) < CubismMath::Epsilon)
    {
        dst.LoadIdentity();
        return dst;
    }

    const csmFloat32 invDet = 1.0f / det;

    csmFloat32 invMatrix[3][3];
    invMatrix[0][0] = (r11 * r22 - r12 * r21) * invDet;
    invMatrix[0][1] = -(r01 * r22 - r02 * r21) * invDet;
    invMatrix[0][2] = (r01 * r12 - r02 * r11) * invDet;
    invMatrix[1][0] = -(r10 * r22 - r12 * r20) * invDet;
    invMatrix[1][1] = (r00 * r22 - r02 * r20) * invDet;
    invMatrix[1][2] = -(r00 * r12 - r02 * r10) * invDet;
    invMatrix[2][0] = (r10 * r21 - r11 * r20) * invDet;
    invMatrix[2][1] = -(r00 * r21 - r01 * r20) * invDet;
    invMatrix[2][2] = (r00 * r11 - r01 * r10) * invDet;

    // 描画行列から3×3行列を抽出
    const csmFloat32* const draw = matrixForDraw.GetArray();
    const csmFloat32 drawMatrix[3][3] = {
        { draw[0], draw[1], draw[3] },
        { draw[4], draw[5], draw[7] },
        { draw[12], draw[13], draw[15] }
    };

    // invMatrix * drawMatrix を計算
    csmFloat32 resultMatrix[3][3] = {};
    for (csmInt32 i = 0; i < 3; ++i)
    {
        for (csmInt32 j = 0; j < 3; ++j)
        {
            for (csmInt32 k = 0; k < 3; ++k)
            {
                resultMatrix[i][j] += invMatrix[i][k] * drawMatrix[k][j];
            }
        }
    }

    // 3×3の結果を4×4にする
    csmFloat32 clipMatrix[16] = {};
    clipMatrix[0] = resultMatrix[0][0];
    clipMatrix[1] = resultMatrix[0][1];
    clipMatrix[3] = resultMatrix[0][2];
    clipMatrix[4] = resultMatrix[1][0];
    clipMatrix[5] = resultMatrix[1][1];
    clipMatrix[7] = resultMatrix[1][2];
    clipMatrix[12] = resultMatrix[2][0];
    clipMatrix[13] = resultMatrix[2][1];
    clipMatrix[15] = resultMatrix[2][2];

    dst.SetMatrix(clipMatrix);
    return dst;
}

Framework\src\Rendering\CubismRenderer.hpp にオフスクリーン描画用マスクのMatrix定義関数の宣言の追加

static CubismMatrix44 CreateOffscreenMaskClipMatrix(const CubismMatrix44& mvp, const CubismMatrix44& matrixForDraw);

ご不明な点がございましたら、お気軽にお問い合わせください。 引き続きどうぞよろしくお願いいたします。

ご対応ありがとうございます。
ご教示いただいたコードにより、Ren の瞳の描画問題は改善いたしました。

ただ、そのままキャラをクリックして Tap モーションを再生すると、
画面にノイズが乗り始めるタイミングから描画位置がずれる現象が発生しました。

こちらの現象についても、対処方法をご教示いただけますと幸いです。

@wanwanswan
Lvie2D スタッフでございます。

現在、調査に時間を要しております。

大変お待たせしており申し訳ございませんが、何卒よろしくお願いいたします。

大変、お手数をおかけします。

引き続きよろしくお願いいたします。