前言
终于,到了可以看到界面的阶段了。
基于glad和GLFW来构建渲染上屏的功能。
我们熟悉的OpenGL只是一套图形标准,GL版本众多,函数的地址无法在编译时确定下来,需要运行时查询,寻址的方法和平台相关。
比如在Windows平台上申请一段buffer的操作如下,代码显的很冗余。每个GL的API调用都如此,会有大量的重复的逻辑。
c++
// 定义函数原型
typedef void (*GL_GENBUFFERS) (GLsizei, GLuint*);
// 找到正确的函数并赋值给函数指针
GL_GENBUFFERS glGenBuffers = (GL_GENBUFFERS)wglGetProcAddress("glGenBuffers");
// 现在函数可以被正常调用了
GLuint buffer;
glGenBuffers(1, &buffer);
这里引入glad库解决这个问题
引入glad
glad是一个开源库,解决OpenGL API查询的繁琐问题。
进入glad官网: glad.dav1d.de/
选择需要的版本,配置好
选择Generate a loader,点击generate,下载glad.zip,里面有头文件和cpp源码
将头文件目录copy到工程Platform下,glad.h放到Windows下
更新引擎CMake文件,Hazel/CMakeLists.txt:
bash
# 添加glad的头文件和源码
set(SRC_LIST
...
src/Hazel/glad.c
)
# 添加源码
target_include_directories(${PROJECT_NAME} PUBLIC
...
src/Hazel/Platform/include
)
注意!target_include_directories方法里要设置成PUBLIC,如果设置成PRIVATE,则引擎中include的头文件无法传递给业务方,即Sanbox访问不到这里include的头文件。
OK!glad配置好了,我们接着配置GLFW
GLFW配置
OpenGL可以看成是一套基于GPU API的数学计算工具,但是并不能和屏幕设备打交道。
需要引入专门的处理窗口的工具-GLFW,类似的工具层还有SDL、Android平台的EGL、苹果平台的EAGL。
GLFW是开源的、多平台库,支持OpenGL、GLes、Vulkan开发,用简单的API就能创建windows、contexts和surfaces,还支持输入事件的处理。
有多种GLFW的配置方法,这里讲一种我使用的,相对简单、通用的。
- 下载GLFW
进入根目录执行命令,下载GLFW到vendor目录,你也可以进入glfw官网手动下载。这里采用submodule的方式组织第三方库,将三方库从引擎工程总剥离,工程更简洁清爽。
bash
git submodule add https://github.com/TheCherno/glfw Sandbox/Hazel/vendor/GLFW
.gitmodules自动增加了一项GLFW配置
ini
[submodule "Sandbox/Hazel/vendor/GLFW"]
path = Sandbox/Hazel/vendor/GLFW
url = https://github.com/TheCherno/glfw
- CMake中更新GLFW配置
我们下载的是源码,需要参与编译
bash
# 编译子项目 GLFW
add_subdirectory(vendor/GLFW)
# 链接glfw库
target_link_libraries(${PROJECT_NAME} glfw)
# 增加glfw include地址
target_include_directories(${PROJECT_NAME} PUBLIC
...
vendor/GLFW/include
)
OK!窗口相关的环境配置好了,进入正式的编码环节了
Window抽象与实现
设计一个基类Window,抽象出窗口的基本属性和接口,再派生出Windows平台的窗口类WindowsWindow,当然还可能有其他的平台,我们暂不实现。
- WindowProps:描述窗口宽高、title等属性
- OnUpdate():每次tick的时候更新
- GetWidth()、GetHeight():获取宽高
- SetEventCallback():设置事件处理的回调,需要设置进GLFW
- SetVsync():设置强制同步
- IsVSync():查询是否强制同步
声明一个静态的创建Window的函数:
static Window* Create();
由各个具体的平台去实现,在Application层调用。
完整代码如下: Haze./src/Hazel/Core/Window.h
c++
#ifndef SANBOX_WINDOW_H
#define SANBOX_WINDOW_H
#include "../Core.h"
#include "Event.h"
namespace Hazel {
struct WindowProps {
std::string Title;
unsigned int Width;
unsigned int Height;
// 默认参数需要手动转成(std::string&)类型
WindowProps(const std::string& title = (std::string&)("Hazel Engine"),
unsigned int width = 1280,
unsigned int height = 720)
: Title(title), Width(width), Height(height) {
}
};
class Window {
public:
using EventCallbackFn = std::function<void(Event&)>;
virtual ~Window() = default;
virtual void OnUpdate() = 0;
virtual unsigned int GetWidth() const = 0;
virtual unsigned int GetHeight() const = 0;
// Window attributes
virtual void SetEventCallback(const EventCallbackFn& callback) = 0;
virtual void SetVSync(bool enabled) = 0;
virtual bool IsVSync() const = 0;
static Window* Create(const WindowProps& props = WindowProps());
};
}
#endif //SANBOX_WINDOW_H
Windows下的Window实现
这里基于OpenGL实现的,其实也是跨平台的,如果后面切换成directX,则就真的是Windows平台独有的。
在Hazel/src/Hazel/Platform/Windows目录下新建WindowsWindow.h WindowsWindow.cpp文件
这两个文件可以参考我提交的代码WindowsWindow.h、WindowsWindow.cpp
里面实现了Windows中定义的虚函数。不做过多解释,需要注意的几点:
- WindowsWindow类封装了GLFWwindow,这是真实的窗口
- glad会和GLFW冲突,需要将glad放到GLFW前面include
c++
#include "Window.h"
#include <glad/glad.h>
#include <GLFW/glfw3.h>
- glfw的使用不同的平台上有点区别,mac上需要额外加一行代码兼容
c++
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
- glad初始化要放到glfwMakeContextCurrent调用之后
c++
glfwMakeContextCurrent(m_Window);
glfwSetWindowUserPointer(m_Window, &m_Data);
- 垂直同步 SetVSync(true)调用了glfwSwapInterval(1),表示只有窗口缓冲中数据更新了才swap数据上屏,否则每次都会进行swap上屏操作。
c++
void WindowsWindow::SetVSync(bool enabled) {
if (enabled) {
glfwSwapInterval(1);
} else {
glfwSwapBuffers(0);
}
m_Data.VSync = enabled;
}
Application中增加窗口
在Application构造函数中创建窗口,在Run函数中更新窗口
别的地方不需要持有Window,我们用unique_ptr智能指针包装Window.
在Run的While循环中,调用m_Window->OnUpdate(),这里我们绘制一个空白的窗口,背景颜色设置为绿色
c++
#include "Application.h"
#include "ApplicationEvent.h"
#include "Log.h"
#include <glad/glad.h>
namespace Hazel{
Application::Application() {
m_Window = std::unique_ptr<Window>(Window::Create());
}
void Application::Run() {
...
while(m_Running) {
glClearColor(0, 1, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
m_Window->OnUpdate();
}
}
}
没问题的话,你应该能看到一个纯绿色的窗口
OK!总算迈进了一大步~~
这章节完整代码参考: 窗口抽象与实现