706 words
4 minutes
EchoEngine开发笔记-GraphicWidget

对于渲染引擎来说,UI中的图形显示组件最为重要,因为它是显示渲染结果和与用户进行交互的窗口,所以编写一篇笔记来记录图形显示组件的实现过程是很有必要的。

GraphicWidget#

设计思路#

  • 在EchoEngine中,我设计了一个名为GraphicWidget类,此类是用于在EchoEditor中显示图形渲染结果的组件。在一开始时,我是以QOpenGLWidget为基类来设计的,但考虑到未来该项目将支持多个图形API,如OpenGL、DirectX、Vulkan等,所以最终我决定使用QWidget作为基类。
  • 基于目前开发计划是围绕OpenGL来设计的,因此我打算使用GLFW来进行图形上下文管理。

实现过程#

  • 重写两个重要函数:paintEventresizeEvent,在paintEvent中绘制图形,在resizeEvent中更新窗口大小。
  • 重写paintEngine函数,返回nullptr,表示禁用QPainter绘制组件,完全使用渲染引擎来绘制。
  • 在构造函数中设置setAttribute(Qt::WA_PaintOnScreen);, 用于控制Qt在控件上直接进行绘制操作,而不通过缓冲区(例如双缓冲)进行中间绘制。
  • paintEvent中,获取当前OpenGL上下文,并使用OpenGL来绘制图形,最后交换缓冲区将渲染结果显示到屏幕上。
// GraphicWidget.h #pragma once struct GLFWwindow; namespace EchoEditor { /// @brief 图形显示组件 class GraphicWidget : public QWidget { Q_OBJECT public: GraphicWidget(uint32_t nWidth, uint32_t nHeight, QWidget* parent = nullptr); virtual ~GraphicWidget(); public: virtual void paintEvent(QPaintEvent* event) override; virtual void resizeEvent(QResizeEvent* event) override; /// @brief 获取绘制引擎 /// @return 返回nullptr:表示禁用QPainter绘制组件,完全使用渲染引擎来绘制 virtual QPaintEngine* paintEngine() const override { return nullptr; } private: /// @brief 初始化 void Initialize(); private: GLFWwindow* m_pWindow = nullptr; }; } // GraphicWidget.cpp #include "pch.h" #include "GraphicWidget.h" #include <GLFW/glfw3.h> #include <glad/glad.h> namespace EchoEditor { static bool s_bGLFWInitialiazed = false; GraphicWidget::GraphicWidget(uint32_t nWidth, uint32_t nHeight, QWidget* parent) : QWidget(parent) { // 设置直接绘制在屏幕上 setAttribute(Qt::WA_PaintOnScreen); // 设置大小 resize((int)nWidth, (int)nHeight); // 初始化 Initialize(); } GraphicWidget::~GraphicWidget() { glfwDestroyWindow(m_pWindow); } void GraphicWidget::paintEvent(QPaintEvent* event) { //设置当前上下文 glfwMakeContextCurrent(m_pWindow); // OpenGL rendering here glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); // 交换缓冲区 glfwSwapBuffers(m_pWindow); } void GraphicWidget::resizeEvent(QResizeEvent* event) { } void GraphicWidget::Initialize() { if (!s_bGLFWInitialiazed) { // glfwTerminate on system shutdown int success = glfwInit(); ECHO_CORE_ASSERT(success, "Could not intialiaz GLFW!"); s_bGLFWInitialiazed = true; } glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); m_pWindow = glfwCreateWindow(width(), height(), "EchoEngine", nullptr, nullptr); glfwMakeContextCurrent(m_pWindow); //初始化GLAD int iStatus = gladLoadGLLoader((GLADloadproc)glfwGetProcAddress); ECHO_CORE_ASSERT(iStatus, "Failed to initiazlize Glad!"); //设置垂直同步 glfwSwapInterval(1); #if defined(_WIN32)// Windows: Use glfwGetWin32Window QWindow* pGLFWWindow = QWindow::fromWinId((WId)glfwGetWin32Window(m_pWindow)); #else ECHO_CORE_ASSERT(false, "Unsupported platform for embedding GLFW window."); #endif //将GLFW嵌入到GraphicWidget中 QVBoxLayout* layout = new QVBoxLayout(); QWidget* container = QWidget::createWindowContainer(pGLFWWindow, this); layout->addWidget(container); setLayout(layout); } }

测试效果#

GraphicWidget

由图中可以看到,图形显示组件已经成功将组件的背景色设置为深灰色!


Reference#

EchoEngine开发笔记-GraphicWidget
https://jerryym.github.io/posts/echoengine/echoengine开发笔记-graphicwidget/
Author
_进击のJerry_
Published at
2025-01-01