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
License
CC BY-NC-SA 4.0