前语

终于,到了能够看到界面的阶段了。

根据glad和GLFW来构建渲染上屏的功用。

咱们了解的OpenGL只是一套图形规范,GL版别很多,函数的地址无法在编译时确定下来,需求运行时查询,寻址的办法和渠道相关。

比如在Windows渠道上请求一段buffer的操作如下,代码显的很冗余。每个GL的API调用都如此,会有大量的重复的逻辑。

// 界说函数原型
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/

挑选需求的版别,装备好

游戏引擎从零开始(5)-窗口&显示

挑选Generate a loader,点击generate,下载glad.zip,里边有头文件和cpp源码

游戏引擎从零开始(5)-窗口&显示

将头文件目录copy到工程Platform下,glad.h放到Windows下

游戏引擎从零开始(5)-窗口&显示

更新引擎CMake文件,Hazel/CMakeLists.txt:

# 添加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,还支撑输入事情的处理。

游戏引擎从零开始(5)-窗口&显示

有多种GLFW的装备办法,这儿讲一种我运用的,相对简略、通用的。

  1. 下载GLFW

进入根目录执行命令,下载GLFW到vendor目录,你也能够进入glfw官网手动下载。这儿采用submodule的方式安排第三方库,将三方库从引擎工程总剥离,工程更简练清爽。

git submodule add https://github.com/TheCherno/glfw Sandbox/Hazel/vendor/GLFW

游戏引擎从零开始(5)-窗口&显示

.gitmodules主动添加了一项GLFW装备

[submodule "Sandbox/Hazel/vendor/GLFW"]
	path = Sandbox/Hazel/vendor/GLFW
	url = https://github.com/TheCherno/glfw

2. CMake中更新GLFW装备

咱们下载的是源码,需求参与编译

# 编译子项目 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层调用。

游戏引擎从零开始(5)-窗口&显示

完好代码如下: Haze./src/Hazel/Core/Window.h


#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中界说的虚函数。不做过多解说,需求留意的几点:

  1. WindowsWindow类封装了GLFWwindow,这是实在的窗口
  2. glad会和GLFW冲突,需求将glad放到GLFW前面include
#include "Window.h"
#include <glad/glad.h>
#include <GLFW/glfw3.h>
  1. glfw的运用不同的渠道上有点差异,mac上需求额外加一行代码兼容
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
  1. glad初始化要放到glfwMakeContextCurrent调用之后
glfwMakeContextCurrent(m_Window);
glfwSetWindowUserPointer(m_Window, &m_Data);
  1. 笔直同步 SetVSync(true)调用了glfwSwapInterval(1),表明只有窗口缓冲中数据更新了才swap数据上屏,否则每次都会进行swap上屏操作。
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(),这儿咱们制作一个空白的窗口,背景颜色设置为绿色

#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();
        }
    }
}

没问题的话,你应该能看到一个纯绿色的窗口

游戏引擎从零开始(5)-窗口&显示

OK!总算迈进了一大步~~

这章节完好代码参考: 窗口笼统与完成