萌梦动作编辑器设计支持多渲染器的思想



  • 截止现在,萌梦动作编辑器只能支持OpenGL作为默认的渲染器进行渲染。我在和同行的交流过程中了解到,目前他们也在积极地将自己的软件向新型渲染器vulkan靠拢,对于我们做萌梦动作编辑器这款产品来说,支持vulkan的确会带来很多好处的,比如说咱们目前渲染的过程中还会出现一些卡顿,这个也有可能和OpenGL架构相关。如果换做vulkan,那么指令会得到进一步精简,运行速度可能会更快。但是作为这项研究的第一步,我们必须将现有OpenGL的调用从咱们骨骼动画等业务逻辑中剥离出来。形成独立的模块,这样在不同平台上可以选用不同类型的渲染器以便达到更好的效果。

    此外,萌梦动作编辑器面向的用户群是使用MikuMikuDance、MikuMikuMoving、nanoem等等的喜欢三维动画编辑器的初级用户,我们需要做的是积极地对接这部分的用户,满足他们的需求。而其中有一个点就是他们有较大一部分使用的是MikuMikuEffect,也就是制作渲染特效的插件。这个插件呢,是使用微软的HLSL编写的,而HLSL就需要Direct3D(通常是Direct3D 9),这就要求我们软件需要支持Direct3D进行渲染了。这也是我们此次支持多渲染器的目的之一。

    为了达成目的,我们设计了这样一个结构:我们定义的所有三维对象的基类名称叫做Entity,那么Entity采用了PIMPL设计模式(私有实现设计模式),在私有的实现里,我们将其数据的部分和渲染的部分剥离开来。渲染的部分单独写一个基类。这个基类叫做EntityRenderer,这里只负责搜集Entity实例的个数以及做抽象渲染流程。具体的实现呢,我们采用的是Qt的插件思想,插件里与OpenGL相关的类聚合起来,写成一个插件。具体来说,假设我使用OpenGL要渲染Entity,那么在插件里,我会写这样一个类:

    class OpenGLEntityRenderer: public EntityRenderer, protected QOpenGLFunctions
    {
    public:
        virtual void render( void ) { ... }
    };
    

    这样写的好处显而易见:将OpenGL相关的指令隔离到插件中,渲染模块完全不用依赖任何与OpenGL相关的内容。并且通过C++多态性,依然可以采用OpenGL渲染出和以前一样的渲染效果。

    实际操作中,我们还需要让EntityRenderer继承自Renderer接口来获得更抽象一层的方法,在插件中我们使用分布式抽象工厂方法来让渲染器根据需求获得——即传入具体渲染类名称字符串,即可返回相应的渲染器对象实例。有关分布式工厂方法,如果有需要的话,我会单独抽出时间写一篇长文给大家讲解。

    目前这一套方案已经成功地在我写的一个单元测试案例中得以实现。接下来就是推广之,将以前的渲染器都替换代码隔离并且成可以切换的实现方案。
    :face_with_stuck-out_tongue_closed_eyes: :face_with_stuck-out_tongue_closed_eyes: :face_with_stuck-out_tongue_closed_eyes: :face_with_stuck-out_tongue_closed_eyes: :face_with_stuck-out_tongue_closed_eyes:


Log in to reply
 

走马观花

最近的回复

  • C

    Qt for MCU需要商业授权的

    read more
  • Qt for MCUs

    搭建Qt for MCUs PC端开发环境。qt for mcus提供了一个完整的图形框架和工具包,包含了在MCUs上设计、开发和部署gui所需的一切。它允许您在裸机或实时操作系统上运行应用程序。

    先决条件

    开发主机环境支持仅限于Windows 10

    MSVC compiler v19.16 (Visual Studio 2017 15.9.9 or newer) x64

    CMake v3.13 or newer (you can install it using the Qt Online installer) x64

    使用Qt联机安装程序安装Qt for MCUs,该安装程序可通过Qt帐户下载

    安装Qt 5.14和Qt Creator 4.11 or higher

    安装链接

    › Qt: https://account.qt.io/downloads
    › CMake: https://cmake.org/download/
    › Python 2.7 32-bit: https://www.python.org/downloads/release/python-2716/
    › Arm GCC: https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnutoolchain/gnu-rm/downloads
    › J-Link Software Pack: https://www.segger.com/downloads/jlink/JLink_Windows.exe
    › J-Link OpenSDA Firmware: https://www.segger.com/downloads/jlink/OpenSDA_MIMXRT1050-EVKHyperflash
    › STM32CubeProgrammer: https://www.st.com/en/development-tools/stm32cubeprog.html
    › STM32 ST-LINK Utility: https://www.st.com/en/development-tools/stsw-link004.html​​​​​​​

    Qt Creator设置 启用Qt Creator插件 选择“帮助>关于插件”,然后从列表中选择“MCU支持(实验性)”插件,重新启动Qt Creator以应用更改
    替代文字 为MCU创建Qt工具包

    选择工具>选项>设备>MCU

    选择Qt for MCUs-Desktop 32bpp作为目标

    如果尚未设置,请提供Qt for MCUs安装目录的路径。

    单击Apply应用。

    替代文字

    替代文字
    替代文字

    注意:

    编译器要选X64,Qt版本要选64bit,CMake Tool选x64

    打开恒温器项目demo

    选择文件>打开文件或项目。。。

    打开CMakefiles.txt文件来自thermo文件夹的文件。

    选择Qt作为MCU-桌面32bpp套件。

    单击“配置项目”以完成。

    替代文字

    问题

    开发主机环境支持仅限于Windows 10

    C++编译失败,文本大字体.pixelSize.

    文本类型无法正确呈现需要复杂文本布局的unicode序列。对复杂文本使用StaticText

    read more
  • H

    hi 有问题请教你,方便加个联系方式吗

    read more
  • boost.asio是一个很棒的网络库,这回儿我也开始系统地学习起来了。想想当年接触boost,也有八年多了。这次开始接触boost,觉得既熟悉又陌生。熟悉的是小写字母+下划线的命名方式、晦涩的模板、很慢的编译速度以及较大的程序体积,陌生的是asio的各种概念:io服务、接收器、套接字等等:我之前对网络编程不是非常了解。

    于是根据我的理解,参考《Boost.Asio C++网络编程》实现了这样一个简单的客户端和服务端通信的例子,例子非常简单,还不完善,但是幸运的是,可以在本机上互通了。
    下面是客户端的代码:

    #include <iostream> #include <boost/asio.hpp> #include <boost/proto/detail/ignore_unused.hpp> using namespace std; using namespace boost::asio; using namespace boost::system; using namespace boost::proto::detail;// 提供ignore_unused方法 void writeHandler( const boost::system::error_code& ec, size_t bytesTransferred ) { if ( ec ) { cout << "Write data error, code: " << ec.value( ) << "transferred: " << bytesTransferred << endl; } else { cout << "OK! " << bytesTransferred << "bytes written. " << endl; } } int main(int argc, char *argv[]) { ignore_unused( argc ); ignore_unused( argv ); io_service service; ip::tcp::socket sock( service ); ip::tcp::endpoint ep( ip::address::from_string( "127.0.0.1" ), 6545 ); boost::system::error_code ec; sock.connect( ep, ec ); if ( ec ) { cout << "Connect error, code: " << ec.value( ) << ", We will exit." << endl; return ec.value( ); } else { char buf[1024] = "Hello world!"; sock.async_write_some( buffer( buf ), writeHandler ); sock.close( ); } return service.run( ); }

    下面是服务端的代码:

    #include <iostream> #include <boost/asio.hpp> #include <boost/proto/detail/ignore_unused.hpp> using namespace std; using namespace boost::asio; using namespace boost::system; using namespace boost::proto::detail;// 提供ignore_unused方法 void acceptHandle( const boost::system::error_code& code ) { cout << "Accepted." << endl; } int main(int argc, char *argv[]) { ignore_unused( argc ); ignore_unused( argv ); io_service service; ip::tcp::endpoint ep( ip::address::from_string( "127.0.0.1" ), 6545 ); boost::system::error_code ec; ip::tcp::socket sock( service ); ip::tcp::acceptor acceptor( service, ep ); acceptor.async_accept( sock, acceptHandle ); if ( ec ) { cout << "There is an error in server. code: " << ec.value( ) << endl; } return service.run( );// 阻塞运行 }

    运行结果是这样的:
    78448d7b-b3ae-42fc-9e2e-4dd2fbdac2c2-image.png

    我对boost.asio中几个概念的理解:

    io_service,这就是一个类似事件循环的东西,它为io设备提供服务,故名。不管是套接字、文件还是串口设备,都要使用它的服务。它的run()函数相当于启动了一个事件循环。一旦有消息了,即进行响应。这也是实现异步编程的重要基础。 socket,这个类则是套接字,可以处理TCP或者是UDP请求。有同步以及异步的处理方式,也有带异常以及不带异常的处理方式。 acceptor,接收器,仅仅是服务端使用。相当于其余框架中的listener,作接收用的。

    比较浅显,如果有不当之处,敬请指正。

    read more

关注我们

微博
QQ群