三、Live2d 移植 QT

本文介绍了如何在QT环境中将Live2D模型移植到QOpenGLWidget和QOpenGLWindow上。详细步骤包括设置文件清单、配置QT项目、自定义OpenGL实现、修改LAppDelegate类、添加模型资源以及调整窗口尺寸获取方式。同时对比了QOpenGLWidget和QOpenGLWindow的使用差异和优劣。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

且举世誉之而不加劝,举世非之而不加沮,定乎内外之分,辩乎荣辱之境,斯已矣。

——《逍遥游·北冥有鱼》庄周

零、文件清单(Release X64环境)

文件源于官方 SDK 、生成的 Demo 项目、以及操作系统,

1、系统库

需要找到项目生成的四个库以及对应的头文件,Framework.lib、glew32.lib、glfw3.lib、libglew32.lib,

1.1、Framework

Framework 库把 src 下 .hpp .cpp 文件以及文件夹全部拷贝,

1.2、GLFW 与 GLEW

注意保留 GLFW 目录,

注意保留 GL 目录,

2、官方库

官方 SDK 提供的 Core 库(D:\CubismSdkForNative-4-r.7\Core\lib),根据生成项目时选择 MD 或者 MT,

2.1、Core

Live2DCubismCore_MT.lib、Live2DCubismCore_MTd.lib,

备注:v143是Vs2022,v142是Vs2019,v141是Vs2017,

3、系统库

从项目->属性->链接器中,可以看到项目需要引入的系统库,

Framework\Release\Framework.lib
glfw\src\Release\glfw3.lib
opengl32.lib
glu32.lib
-NODEFAULTLIB:libcmt.lib
D:\CubismSdkForNative-4-r.7\Samples\OpenGL\Demo\proj.win.cmake\..\..\..\..\Core\lib\windows\x86_64\143\Live2DCubismCore_MT.lib
lib\Release\libglew32.lib
kernel32.lib
user32.lib
gdi32.lib
winspool.lib
shell32.lib
ole32.lib
oleaut32.lib
uuid.lib
comdlg32.lib
advapi32.lib

主要需要引用下面这五个库,

opengl32.lib
glu32.lib
kernel32.lib
user32.lib
gdi32.lib

4、第三方库

stb_image.h,

5、源代码

Demo 下的源代码,包括头文件,

源代码统一打包到 live2d 文件夹下,

6、文件清单

整合好的文件清单目录结构如下,

├───inc
│   ├───Core
│   ├───Framework
│   │   ├───Effect
│   │   ├───Id
│   │   ├───Math
│   │   ├───Model
│   │   ├───Motion
│   │   ├───Physics
│   │   ├───Rendering
│   │   │   ├───Cocos2d
│   │   │   ├───D3D11
│   │   │   ├───D3D9
│   │   │   ├───Metal
│   │   │   └───OpenGL
│   │   ├───Type
│   │   └───Utils
│   ├───GL
│   ├───GLFW
│   └───stb
├───libs_debug
│   ├───Core
│   │   └───lib
│   │       └───windows
│   │           └───x86_64
│   │               └───142
│   ├───Framework
│   │   └───lib
│   ├───GL
│   │   └───lib
│   └───GLFW
│       └───lib
├───libs_release
│   ├───Core
│   │   └───lib
│   │       └───windows
│   │           └───x86_64
│   │               └───142
│   ├───Framework
│   │   └───lib
│   ├───GL
│   │   └───lib
│   └───GLFW
│       └───lib
└───live2d

一、移植思路

QT 封装了 OpenGL 类 QOpenGLWidget、QOpenGLWindow,我们通过自定义实现该类将模型渲染到自定义的窗口上,

二、QOpenGLWidget

1、QT 工程

注意这里编译器使用 MSVC 对应的版本,

2、配置 QT

配置文件清单里面的链接库、源文件、头文件,

在 mainwindow.h 中手动链接系统库,

#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")

在 pro 文件配置生成库 include 跟 lib,以及预定义宏,

# 添加 OpenGL 模块
QT += openglwidgets

# add header inclue
INCLUDEPATH += $$PWD/inc
INCLUDEPATH += $$PWD/inc/Core
INCLUDEPATH += $$PWD/inc/Framework
INCLUDEPATH += $$PWD/inc/stb

# add link lib, release mode
LIBS += -l$$PWD/libs_release/Core/lib/windows/x86_64/142/Live2DCubismCore_MT
LIBS += -l$$PWD/libs_release/GLFW/lib/glfw3
LIBS += -l$$PWD/libs_release/Framework/lib/Framework
LIBS += -l$$PWD/libs_release/GL/lib/libglew32

# add link lib, debug mode
#LIBS += -l$$PWD/libs_debug/Core/lib/windows/x86_64/142/Live2DCubismCore_MT
#LIBS += -l$$PWD/libs_debug/GLFW/lib/glfw3
#LIBS += -l$$PWD/libs_debug/Framework/lib/Framework
#LIBS += -l$$PWD/libs_debug/GL/lib/libglew32d

# 预定义宏 => 表示在 WINDOWS 平台
DEFINES += CSM_TARGET_WIN_GL

inc => 头文件,libs => 链接库,live2d => 源代码,

对应 release 模式运行,可以看到正常启动,

3、自定义实现 MyOpenGL

添加自定义类 MyOpenGL,

myopengl.h 添加头文件,继承 QOpenGLWidget,

#ifndef MYOPENGL_H
#define MYOPENGL_H

#include <QOpenGLWidget>

class MyOpenGL : public QOpenGLWidget
{
public:
    MyOpenGL(QWidget *parent = nullptr);

protected:
    void initializeGL();
    void resizeGL(int w, int h);
    void paintGL();
};

#endif // MYOPENGL_H
#include "live2d/LAppDelegate.hpp"
#include "live2d/LAppView.hpp"
#include "live2d/LAppPal.hpp"
#include "live2d/LAppLive2DManager.hpp"
#include "live2d/LAppDefine.hpp"

#include "myopengl.h"

MyOpenGL::MyOpenGL(QWidget *parent)
    :QOpenGLWidget{parent} {

}

void MyOpenGL::initializeGL(){

}

void MyOpenGL::resizeGL(int w, int h){

}

void  MyOpenGL::paintGL(){

}

4.1、通过可视化工具类提升添加

在 mainwindow.ui 界面拖拽一个 QOpenGLWiget 控件,提升为我们自定义的类 MyOpenGL,

运行,

4.2、通过代码手动添加

# 构造函数中添加
myopengl = new MyOpenGL(this);
setCentralWidget(myopengl);

无论是 4.1 还是 4.2 方式添加 MyOpenGL 类,最终运行效果,

5、修改 LAppDelegate 类

5.1、修改头文件

在 LAppDelegate.hpp 中包含 myopengl.h 头文件,然后在 public 下新增两个函数声明,

注释原来的GLFWwindow*_window,改成MyOpenGL*_window,然后修改GetWindow函数,

修改Initialize()函数,新增参数,

最终修改后的头文件是这样的,

/**
 * Copyright(c) Live2D Inc. All rights reserved.
 *
 * Use of this source code is governed by the Live2D Open Software license
 * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
 */

#pragma once

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "LAppAllocator.hpp"
#include "myopengl.h"

class LAppView;
class LAppTextureManager;

/**
* @brief   アプリケーションクラス。
*   Cubism SDK の管理を行う。
*/
class LAppDelegate
{
public:

    void resize(int width,int height);

    void update();
    /**
    * @brief   クラスのインスタンス(シングルトン)を返す。<br>
    *           インスタンスが生成されていない場合は内部でインスタンを生成する。
    *
    * @return  クラスのインスタンス
    */
    static LAppDelegate* GetInstance();

    /**
    * @brief   クラスのインスタンス(シングルトン)を解放する。
    *
    */
    static void ReleaseInstance();

    /**
    * @brief   APPに必要なものを初期化する。
    */
    // bool Initialize();
    bool Initialize(MyOpenGL* window);

    /**
    * @brief   解放する。
    */
    void Release();

    /**
    * @brief   実行処理。
    */
    void Run();

    /**
    * @brief   OpenGL用 glfwSetMouseButtonCallback用関数。
    *
    * @param[in]       window            コールバックを呼んだWindow情報
    * @param[in]       button            ボタン種類
    * @param[in]       action            実行結果
    * @param[in]       modify
    */
    void OnMouseCallBack(GLFWwindow* window, int button, int action, int modify);

    /**
    * @brief   OpenGL用 glfwSetCursorPosCallback用関数。
    *
    * @param[in]       window            コールバックを呼んだWindow情報
    * @param[in]       x                 x座標
    * @param[in]       y                 x座標
    */
    void OnMouseCallBack(GLFWwindow* window, double x, double y);

    /**
    * @brief シェーダーを登録する。
    */
    GLuint CreateShader();

    /**
    * @brief   Window情報を取得する。
    */
    //GLFWwindow* GetWindow() { return _window; }
    MyOpenGL* GetWindow() { return _window; }
    /**
    * @brief   View情報を取得する。
    */
    LAppView* GetView() { return _view; }

    /**
    * @brief   アプリケーションを終了するかどうか。
    */
    bool GetIsEnd() { return _isEnd; }

    /**
    * @brief   アプリケーションを終了する。
    */
    void AppEnd() { _isEnd = true; }

    LAppTextureManager* GetTextureManager() { return _textureManager; }

private:
    /**
    * @brief   コンストラクタ
    */
    LAppDelegate();

    /**
    * @brief   デストラクタ
    */
    ~LAppDelegate();

    /**
    * @brief   Cubism SDK の初期化
    */
    void InitializeCubism();

    /**
     * @brief   CreateShader内部関数 エラーチェック
     */
    bool CheckShader(GLuint shaderId);

    LAppAllocator _cubismAllocator;              ///< Cubism SDK Allocator
    Csm::CubismFramework::Option _cubismOption;  ///< Cubism SDK Option
    //GLFWwindow* _window;                         ///< OpenGL ウィンドウ
    MyOpenGL*_window;
    LAppView* _view;                             ///< View情報
    bool _captured;                              ///< クリックしているか
    float _mouseX;                               ///< マウスX座標
    float _mouseY;                               ///< マウスY座標
    bool _isEnd;                                 ///< APP終了しているか
    LAppTextureManager* _textureManager;         ///< テクスチャマネージャー

    int _windowWidth;                            ///< Initialize関数で設定したウィンドウ幅
    int _windowHeight;                           ///< Initialize関数で設定したウィンドウ高さ
};

class EventHandler
{
public:
    /**
    * @brief   glfwSetMouseButtonCallback用コールバック関数。
    */
    static void OnMouseCallBack(GLFWwindow* window, int button, int action, int modify)
    {
        LAppDelegate::GetInstance()->OnMouseCallBack(window, button, action, modify);
    }

    /**
    * @brief   glfwSetCursorPosCallback用コールバック関数。
    */
    static void OnMouseCallBack(GLFWwindow* window, double x, double y)
    {
        LAppDelegate::GetInstance()->OnMouseCallBack(window, x, y);
    }

};

5.2、修改源文件

LAppDelegate.cpp 对应调整 Initialize,将 window 初始化逻辑修改为自己的 MyOpenGL 实例,


bool LAppDelegate::Initialize(MyOpenGL*window)
{
    if (DebugLogEnable)
    {
        LAppPal::PrintLog("START");
    }

    // GLFWの初期化
    if (glfwInit() == GL_FALSE)
    {
        if (DebugLogEnable)
        {
            LAppPal::PrintLog("Can't initilize GLFW");
        }
        return GL_FALSE;
    }

    // Windowの生成_
    //_window = glfwCreateWindow(RenderTargetWidth, RenderTargetHeight, "SAMPLE", NULL, NULL);
    _window = window;
    if (_window == NULL)
    {
        if (DebugLogEnable)
        {
            LAppPal::PrintLog("Can't create GLFW window.");
        }
        glfwTerminate();
        return GL_FALSE;
    }

    // Windowのコンテキストをカレントに設定
    //glfwMakeContextCurrent(_window);
    _window->makeCurrent();
    glfwSwapInterval(1);

    if (glewInit() != GLEW_OK) {
        if (DebugLogEnable)
        {
            LAppPal::PrintLog("Can't initilize glew.");
        }
        glfwTerminate();
        return GL_FALSE;
    }

    //テクスチャサンプリング設定
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    //透過設定
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    //コールバック関数の登録
    // glfwSetMouseButtonCallback(_window, EventHandler::OnMouseCallBack);
    // glfwSetCursorPosCallback(_window, EventHandler::OnMouseCallBack);

    // ウィンドウサイズ記憶
    // int width, height;
    // glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &width, &height);
    _windowWidth = _window->width();
    _windowHeight = _window->height();

    //AppViewの初期化
    _view->Initialize();

    // Cubism SDK の初期化
    InitializeCubism();

    return GL_TRUE;
}

修改Release函数,注释 glfwDestroyWindow 逻辑,

void LAppDelegate::Release()
{
    // Windowの削除
    //glfwDestroyWindow(_window);

    glfwTerminate();

    delete _textureManager;
    delete _view;

    // リソースを解放
    LAppLive2DManager::ReleaseInstance();

    //Cubism SDK の解放
    CubismFramework::Dispose();
}

修改 Run 函数,将其拆分为两个函数 resize 跟 update,


void LAppDelegate::Run()
{
    //メインループ
    // while (glfwWindowShouldClose(_window) == GL_FALSE && !_isEnd)
    // {
    //     int width, height;
    //     glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &width, &height);
    //     if( (_windowWidth!=width || _windowHeight!=height) && width>0 && height>0)
    //     {
    //         //AppViewの初期化
    //         _view->Initialize();
    //         // スプライトサイズを再設定
    //         _view->ResizeSprite();
    //         // サイズを保存しておく
    //         _windowWidth = width;
    //         _windowHeight = height;

    //         // ビューポート変更
    //         glViewport(0, 0, width, height);
    //     }

    //     // 時間更新
    //     LAppPal::UpdateTime();

    //     // 画面の初期化
    //     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    //     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    //     glClearDepth(1.0);

    //     //描画更新
    //     _view->Render();

    //     // バッファの入れ替え
    //     glfwSwapBuffers(_window);

    //     // Poll for and process events
    //     glfwPollEvents();
    // }

    // Release();

    // LAppDelegate::ReleaseInstance();
}
void LAppDelegate::resize(int width,int height){
    if( (_windowWidth!=width || _windowHeight!=height) && width>0 && height>0)
    {
        //AppViewの初期化
        _view->Initialize();
        // スプライトサイズを再設定
        _view->ResizeSprite();
        // サイズを保存しておく
        _windowWidth = width;
        _windowHeight = height;

        // ビューポート変更
        glViewport(0, 0, width, height);
        return;
    }
    glViewport(0, 0, width, height);
}

void LAppDelegate::update(){

    // 時間更新
    LAppPal::UpdateTime();

    // 画面の初期化
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearDepth(1.0);

    //描画更新
    _view->Render();
}

6、修改 width 和 height 的获取方式

官方源代码中获取渲染窗口的宽和高是通过glfwGetWindowSize() 函数,但我们调整为获取 OpenGL 窗口的宽高,因此所有用到 glfwGetWindowSize 函数的地方都要修改为自定义逻辑,

7、MyOpenGL 类对应调整

#include "live2d/LAppDelegate.hpp"
#include "live2d/LAppView.hpp"
#include "live2d/LAppPal.hpp"
#include "live2d/LAppLive2DManager.hpp"
#include "live2d/LAppDefine.hpp"

#include "myopengl.h"

MyOpenGL::MyOpenGL(QWidget *parent)
    :QOpenGLWidget{parent} {

}

void MyOpenGL::initializeGL(){
    LAppDelegate::GetInstance()->Initialize(this);
}

void MyOpenGL::resizeGL(int w, int h){
    LAppDelegate::GetInstance()->resize(w,h);
}

void  MyOpenGL::paintGL(){
    LAppDelegate::GetInstance()->update();
}

8、添加模型资源

将 SDK 示例中的整个Resources 文件夹放到 exe 程序的上一级目录,因为在 Qt Creator 运行目录默认是 exe 程序的上一级目录,

9、运行效果

添加资源之后就可以运行了,可以看到 Live2d SDK 已经移植成功,在 QT 的窗口上正常运行。

三、QOpenGLWindow

除了使用 QOpenGLWidget 控件,我们也可以使用 QOpenGLWindow 渲染模型,QOpenGLWindow 是 Qt 框架中的一个类,用于创建基于 OpenGL 的窗口应用程序。

它具有以下优势:

  1. 简化了 OpenGL 窗口的创建和管理:QOpenGLWindow 封装了底层的 OpenGL 上下文管理和窗口创建过程,使得开发者可以更方便地创建和管理 OpenGL 窗口应用程序。

  2. 跨平台支持:Qt 框架本身就是跨平台的,因此使用 QOpenGLWindow 创建的窗口应用程序可以在多个操作系统上运行,包括 Windows、macOS 和 Linux。

  3. 高性能:QOpenGLWindow 提供了与底层 OpenGL 直接交互的能力,可以充分利用硬件加速来实现高性能的图形渲染。

  4. 内置的事件处理机制:QOpenGLWindow 继承自 QWidget,因此可以直接使用 Qt 的事件处理机制来处理用户输入事件,如鼠标点击、键盘按键等。

  5. 支持其他 Qt 功能:由于 QOpenGLWindow 是基于 Qt 框架的,所以可以方便地与其他 Qt 功能进行集成,如绘制 2D 图形、使用 Qt Widgets 构建用户界面等。

1、准备工作

在第二步介绍的一样新建一个新的 QT 工程,然后配置 QT,唯一不同的是,要在 pro 配置中引用的是 opengl 模块,

QT       += core gui
QT += opengl
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++17

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

# add header inclue
INCLUDEPATH += $$PWD/inc
INCLUDEPATH += $$PWD/inc/Core
INCLUDEPATH += $$PWD/inc/Framework
INCLUDEPATH += $$PWD/inc/stb

# add link lib debug mode
LIBS += -l$$PWD/libs_debug/Core/lib/windows/x86_64/142/Live2DCubismCore_MT
LIBS += -l$$PWD/libs_debug/GLFW/lib/glfw3
LIBS += -l$$PWD/libs_debug/Framework/lib/Framework
LIBS += -l$$PWD/libs_debug/GL/lib/libglew32d

LIBS += -ldwmapi

# 预定义宏 => 表示在 WINDOWS 平台
DEFINES += CSM_TARGET_WIN_GL

SOURCES += \
    live2d/CubismSampleViewMatrix.cpp \
    live2d/CubismUserModelExtend.cpp \
    live2d/LAppAllocator.cpp \
    live2d/LAppDefine.cpp \
    live2d/LAppDelegate.cpp \
    live2d/LAppLive2DManager.cpp \
    live2d/LAppModel.cpp \
    live2d/LAppPal.cpp \
    live2d/LAppSprite.cpp \
    live2d/LAppTextureManager.cpp \
    live2d/LAppView.cpp \
    live2d/LAppWavFileHandler.cpp \
    live2d/MouseActionManager.cpp \
    live2d/TouchManager.cpp \
    main.cpp \
    myopenglwindow.cpp

HEADERS += \
    live2d/CubismSampleViewMatrix.hpp \
    live2d/CubismUserModelExtend.hpp \
    live2d/LAppAllocator.hpp \
    live2d/LAppDefine.hpp \
    live2d/LAppDelegate.hpp \
    live2d/LAppLive2DManager.hpp \
    live2d/LAppModel.hpp \
    live2d/LAppPal.hpp \
    live2d/LAppSprite.hpp \
    live2d/LAppTextureManager.hpp \
    live2d/LAppView.hpp \
    live2d/LAppWavFileHandler.hpp \
    live2d/MouseActionManager.hpp \
    live2d/TouchManager.hpp \
    myopenglwindow.h

FORMS +=

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

2、自定义实现 MyOpenGLWindow

新建一个类 MyOpenGLWindow,头文件如下,

#ifndef MYOPENGLWINDOW_H
#define MYOPENGLWINDOW_H

#include <QOpenGLWindow>
#include <QOpenGLFunctions_3_3_Compatibility>

#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")

class MyOpenGLWindow: public QOpenGLWindow, protected QOpenGLFunctions_3_3_Compatibility
{
public:
    MyOpenGLWindow();

protected:
    void initializeGL();
    void resizeGL(int w, int h);
    void paintGL();
};

#endif // MYOPENGLWINDOW_H

然后按照第二步中一样对照修改 LAppDelegate 类头文件、源文件,最后重写 MyOpenGLWindow 的三个方法,

#include "live2d/LAppDelegate.hpp"
#include "live2d/LAppView.hpp"
#include "live2d/LAppPal.hpp"
#include "live2d/LAppLive2DManager.hpp"
#include "live2d/LAppDefine.hpp"
#include "myopenglwindow.h"

MyOpenGLWindow::MyOpenGLWindow() {

}

void MyOpenGLWindow::initializeGL(){
    initializeOpenGLFunctions();
    LAppDelegate::GetInstance()->Initialize(this);
}

void MyOpenGLWindow::resizeGL(int w, int h){
    LAppDelegate::GetInstance()->resize(w,h);
}

void  MyOpenGLWindow::paintGL(){
    LAppDelegate::GetInstance()->update();
}

3、main.cpp 修改

使用 MyOpenGLWindow  替换 MainWindow 启动窗口,

#include "myopenglwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //MainWindow w;
    MyOpenGLWindow w;
    w.resize(800,600);
    w.show();
    return a.exec();
}

4、运行效果

可以看到,同样正常渲染模型,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

金汐脉动 | PulseTide

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值