Horse3D 游戏引擎研发笔记(一):基于独立的渲染线程绘制彩色三角形 Horse3D 游戏引擎研发笔记一基于独立的渲染线程绘制彩色三角形一、为什么要独立渲染线程二、Qt 6.4 官方的多线程渲染建议三、项目结构四、核心实现4.1 渲染线程 RenderThread4.2 显示端 IScreen4.3 CMake 与部署五、踩坑记录5.1 界面闪烁5.2 三角形消失 / 黑屏5.3 RenderThread::metaObject 链接错误5.4 VS 里运行找不到 Qt DLL六、当前成果七、下一步目标在 Qt 6.4 OpenGL Core 环境下把渲染逻辑放到独立线程执行GUI 线程只负责把渲染结果呈现出来并最终以动态库形式组织渲染模块。一、为什么要独立渲染线程在传统的QOpenGLWidget用法里渲染、事件处理、UI 布局全部跑在 GUI 线程。当场景复杂之后一帧的paintGL可能直接阻塞消息循环导致窗口卡顿、输入延迟。把渲染拆到独立线程的好处很直接GUI 线程只干 “显示” 这一件事帧率稳定。渲染线程可以充分利用多核 CPU/GPU做更重的几何、材质、后处理计算。后期更容易扩展成命令队列、多 Pass、异步资源加载等引擎常见结构。但 Qt 的 OpenGL 上下文对线程敏感直接跨线程访问 FBO、VAO 会触发驱动层访问冲突。因此需要先理解 Qt 官方推荐的多线程模型。二、Qt 6.4 官方的多线程渲染建议Qt 6.4 文档在QOpenGLWidget的 Threading 段落里给出了两条路工作线程做离屏渲染主线程采样纹理工作线程持有与 GUI 上下文共享的第二个QOpenGLContext渲染到自己的 FBO然后把纹理 ID 交给 GUI 线程显示。直接把QOpenGLWidget的上下文移到工作线程需要重载paintEvent为空并小心地通过moveToThread切换上下文亲和性渲染完再切回 GUI 线程触发合成。第一条路更符合引擎思路渲染管线自己管理 target显示端只负责 present。我们项目最终采用的就是这条路。三、项目结构e:WorkSpacesoftwarer-horse ├── CMakeLists.txt # 顶层统一输出目录、add_subdirectory ├── Dragon # 渲染核心动态库 │ ├── CMakeLists.txt │ ├── dragon_global.h # DLL 导出宏 │ ├── RenderThread.h / .cpp # 独立渲染线程 │ ├── Screen.h / .cpp # IScreen基于 QOpenGLWidget 的显示端 ├── softwarer-horse # 可执行程序 │ ├── CMakeLists.txt │ ├── main.cpp │ ├── softwarerhorse.h / .cpp # 主窗口Dragon是 SHARED 库softwarer-horse.exe链接Dragon.lib运行时依赖Dragon.dll。四、核心实现4.1 渲染线程 RenderThreadRenderThread继承QThread负责持有与 GUI 共享的QOpenGLContext。在QOffscreenSurface上渲染三角形到 FBO。维护双缓冲 FBO每帧渲染完成后交换并暴露 front texture ID。通过QWaitCondition实现 “按需渲染”GUI 请求一帧线程渲染一帧渲染完成发frameReady()信号。关键代码片段voidRenderThread::run(){if(!m_context-makeCurrent(m_surface)){qWarning(RenderThread: makeCurrent failed);return;}initializeOpenGLFunctions();initRenderResources();while(!m_stop){m_requestMutex.lock();m_waitCondition.wait(m_requestMutex);m_requestMutex.unlock();if(m_stop)break;renderFrame();glFinish();emitframeReady();}m_context-doneCurrent();}voidRenderThread::renderNext(){QMutexLockerlocker(m_requestMutex);m_waitCondition.wakeOne();}使用glFinish()是为了确保 GPU 真正完成绘制避免 GUI 线程拿到还没写满的纹理。4.2 显示端 IScreenIScreen继承QOpenGLWidget和QOpenGLFunctions_3_3_Core职责只有三个在 GUI 线程创建QOffscreenSurface。创建共享上下文并移交给RenderThread。在paintGL()里把RenderThread的 front texture 用全屏四边形画出来。共享上下文创建是重点。Windows 上wglShareLists要求 GUI 线程不能持有当前 OpenGL 上下文所以必须先doneCurrent()voidIScreen::initializeGL(){initializeOpenGLFunctions();initDisplayResources();m_offscreenSurfacenewQOpenGLSurface();m_offscreenSurface-setFormat(context()-format());m_offscreenSurface-create();doneCurrent();// 释放 GUI 上下文否则 wglShareLists 会失败QOpenGLContext*workerContextnewQOpenGLContext();workerContext-setShareContext(context());workerContext-setFormat(context()-format());if(!workerContext-create()){qWarning(IScreen: failed to create worker context);deleteworkerContext;return;}m_renderThreadnewRenderThread(workerContext,this);workerContext-moveToThread(m_renderThread);connect(m_renderThread,RenderThread::frameReady,this,QOverload::of(IScreen::update));m_renderThread-start();m_renderThread-renderNext();}paintGL()里采样纹理voidIScreen::paintGL(){glClear(GL_COLOR_BUFFER_BIT);GLuint texm_renderThread-frontTexture();if(tex0)return;m_program-bind();glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D,tex);m_program-setUniformValue(tex,0);glBindVertexArray(m_vao);glDrawArrays(GL_TRIANGLES,0,6);m_renderThread-renderNext();}注意paintGL()显示完才会请求下一帧形成 “显示 → 请求 → 渲染 → 显示” 的节奏避免读写竞争导致的闪烁或黑屏。4.3 CMake 与部署顶层CMakeLists.txt统一输出目录set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Library) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Library) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Runtime)这样Dragon.dll和softwarer-horse.exe会在同一目录避免运行时找不到 DLL。为了让 VS 里按 F5 也能正确加载 Qt DLL给可执行目标加了调试器工作目录属性set_target_properties(${PROJECT_NAME} PROPERTIES WIN32_EXECUTABLE TRUE VS_DEBUGGER_WORKING_DIRECTORY $TARGET_FILE_DIR:${PROJECT_NAME} )同时用windeployqt在 post-build 阶段自动把 Qt 运行时依赖复制到 exe 目录if(WIN32) get_target_property(_qt_core_location Qt${QT_VERSION_MAJOR}::Core IMPORTED_LOCATION_RELEASE) get_filename_component(_qt_bin_dir ${_qt_core_location} DIRECTORY) find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS ${_qt_bin_dir} NO_DEFAULT_PATH) if(WINDEPLOYQT_EXECUTABLE) add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${WINDEPLOYQT_EXECUTABLE} --dir $TARGET_FILE_DIR:${PROJECT_NAME} $TARGET_FILE:${PROJECT_NAME} ) endif() endif()五、踩坑记录5.1 界面闪烁最初RenderThread::run()里是无条件循环渲染主线程显示节奏和渲染节奏不同步导致读到未完成帧。解决办法是改成按需渲染主线程显示完请求下一帧线程渲染完通过信号通知更新。5.2 三角形消失 / 黑屏共享上下文创建失败。根本原因是 Windows 上wglShareLists要求 GUI 线程先释放当前上下文所以必须在创建 worker context 前调用doneCurrent()。5.3RenderThread::metaObject链接错误RenderThread头文件写了Q_OBJECT但Dragon库目标没有开AUTOMOC导致 moc 生成的元对象代码没有编译进库。解决在Dragon/CMakeLists.txt里set(CMAKE_AUTOMOC ON)。5.4 VS 里运行找不到 Qt DLLwindeployqt已经把 DLL 复制到Runtime/目录但 VS 默认调试工作目录不是 exe 所在目录。解决设置VS_DEBUGGER_WORKING_DIRECTORY为$TARGET_FILE_DIR:${PROJECT_NAME}。六、当前成果现在项目已经能稳定运行独立渲染线程在离屏 FBO 上绘制彩色三角形。GUI 线程通过共享纹理采样并显示。渲染模块打包为Dragon.dll主程序通过导入库链接。VS 直接 F5 运行无额外配置。七、下一步把顶点、材质、 uniform、相机等概念抽象成引擎资源类。引入渲染命令队列让主线程以 “提交命令” 的方式驱动渲染线程。支持 Dear ImGui 或 Qt Widget 叠加调试 UI。尝试把渲染线程渲染目标升级为多采样 FBO 后处理链。本系列记录 Horse3D 游戏引擎从零开始的研发过程欢迎交流。

相关新闻

最新新闻

毕业论文降AI率实测攻略:AI率高也能救回来

毕业论文降AI率实测攻略:AI率高也能救回来

马上要交论文了,最近真的被论文ai率折磨的够呛。 明明查重都没问题了,但是ai率就是居高不下,崩溃了,明明都是我自己写的,天杀的,明明都是我亲生的啊 改来改去,终于给我搞出一套完美的降ai方案…

2026/8/28 23:55:53
element-plus下拉列表修改背景样式、级联选择器修改背景样式如何操作详细过程

element-plus下拉列表修改背景样式、级联选择器修改背景样式如何操作详细过程

最好单独找个css或者less文件来做element-plus的样式修改&#xff0c;本文在main.less下做element-plus的修改 在main.js中引入import ‘./assets/main.less’ 样式 在App.vue中给下拉标签添加popper-class"my_select"这个属性, 完整的内容如下&#xff1a; <temp…

2026/8/28 23:55:53
HUD:为AI编程CLI打造可视化终端面板

HUD:为AI编程CLI打造可视化终端面板

HUD 是什么&#xff1f;一句话&#xff1a;一个开源的极简终端 UI&#xff0c;专门给 ClaudeCode、Codex、OpenCode 这三个 AI 编程 CLI 工具做信息面板。你在终端里跑过这几个工具就知道&#xff0c;原生的交互已经不错&#xff0c;但对话过程、工具调用步骤、上下文和 token …

2026/8/28 23:55:53
基于Hugo静态站点生成器构建低成本SEO站群实现单页关键词排名

基于Hugo静态站点生成器构建低成本SEO站群实现单页关键词排名

简介&#xff1a;站群策略是搜索引擎优化&#xff08;SEO&#xff09;中提升网站权重与排名的常见技术手段&#xff0c;其核心原理是通过构建多个独立但主题相关的网站&#xff0c;形成内部链接网络&#xff0c;从而集中传递权重至目标页面。在技术实现上&#xff0c;静态站点生…

2026/8/28 23:55:53
AI安全防护实战:构建从输入检测到审计追踪的全链路方案

AI安全防护实战:构建从输入检测到审计追踪的全链路方案

朋友们好&#xff0c;这篇内容想聊的其实是“AI 安全计划中&#xff0c;哪些技术能力与工程方向会成为真正的赢家”。过去一段时间&#xff0c;AI 应用井喷式增长&#xff0c;但伴随而来的安全事件也越来越多&#xff0c;比如提示注入、模型幻觉、敏感信息泄露、越权重定向等。…

2026/8/28 23:55:53
2026SRM选型必看:高适配供应商协同平台盘点

2026SRM选型必看:高适配供应商协同平台盘点

2025年我国企业物资采购总额近200万亿元&#xff0c;数字化采购渗透率仅为11.8%。超九成采购仍在线下运行&#xff0c;这意味着SRM系统不仅要解决流程线上化问题&#xff0c;更要深度适配不同行业的业务逻辑。供应链管理的本质在于**打通企业内部管理与外部产业链协同。**作为数…

2026/8/28 23:50:52