且举世誉之而不加劝,举世非之而不加沮,定乎内外之分,辩乎荣辱之境,斯已矣。
——《逍遥游·北冥有鱼》庄周
零、文件清单(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 的窗口应用程序。
它具有以下优势:
-
简化了 OpenGL 窗口的创建和管理:QOpenGLWindow 封装了底层的 OpenGL 上下文管理和窗口创建过程,使得开发者可以更方便地创建和管理 OpenGL 窗口应用程序。
-
跨平台支持:Qt 框架本身就是跨平台的,因此使用 QOpenGLWindow 创建的窗口应用程序可以在多个操作系统上运行,包括 Windows、macOS 和 Linux。
-
高性能:QOpenGLWindow 提供了与底层 OpenGL 直接交互的能力,可以充分利用硬件加速来实现高性能的图形渲染。
-
内置的事件处理机制:QOpenGLWindow 继承自 QWidget,因此可以直接使用 Qt 的事件处理机制来处理用户输入事件,如鼠标点击、键盘按键等。
-
支持其他 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、运行效果
可以看到,同样正常渲染模型,