OpenGL入门(二)

133 阅读5分钟

背景

上一篇文章我们已经初步介绍了OpenGL的基本概念,准确来说OpenGL只是一个规范,基于这个规范提供了库去实现图像编程。OpenGL的很多操作都是基于一个窗口,今天我们就来实现创建一个简单的窗口,talk is cheap,show me your code。

环境配置

工欲善其事, 必先利其器,在正式开发之前还需要搭建一下环境,这里仅仅以个人的环境为例,不同的平台可以去google一下,有很多相关的文章,我自己使用的是Mac,为了方便编辑器使用的是VS Code。
step1:
在正式开始之前需要先安装GLFW,GLFW是一个C语言写的库,符合OpenGLd的标准,使用GLFW可以创建一个OpenGL的context,创建并定义一个窗口的参数以及处理用户的输入,安装后其安装位置要记住,之后需要使用。

brew install glfw

step2:

下载glad,可以去下载页面下载,使用一下配置即可:

glad配置

然后点击下面的生成按钮并且下载。然后新建一个文件夹将解压后的include和src文件夹复制到新建的文件夹。 这个glad是一个开源库,正如之前我们所提到的OpenGL只是一个标准,具体的实现是有各个显卡厂商完成的, 所以显卡驱动的版本有很多种类,虽然都是基于OpenGL标准,实现的函数和最终的效果是一样的,但是函数的位置在编译期是无法知道的,且需要在运行时检索这些函数,然后将这些函数的位置存储在函数指针中以便之后使用,而这个检索函数的过程又是与操作系统高度相关的,而glad就是用来帮助开发者完成这些繁琐的过程。

step3:

新建CMakeList.txt文件,其内容如下,记得替换GLFW_H和GLFW_LINK的地址,在安装GLFW完成时会提示安装的位置。

cmake_minimum_required(VERSION 3.0.0)
project(HelloGL VERSION 0.1.0)

# 使用 C++ 11 标准
set(CMAKE_CXX_STANDARD 11)

# 添加头文件
set(GLAD_H ${PROJECT_SOURCE_DIR}/include)
set(GLFW_H /usr/local/include)
include_directories(${GLAD_H} ${GLFW_H})

# 添加目标链接
set(GLFW_LINK /usr/local/lib/libglfw.3.dylib)
link_libraries(${GLFW_LINK})

# 执行编译命令
set(SOURCES glad.c main.cpp)
add_executable(HelloGL ${SOURCES})

# 链接系统的 OpenGL 框架
if (APPLE)
    target_link_libraries(HelloGL "-framework OpenGL")
endif()

include(CTest)
enable_testing()

set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)

step4:

然后在src目录下新建一个main.cpp,内容如下:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>

void framebuffer_size_callback(GLFWwindow *window, int width, int height);
void processInput(GLFWwindow *window);

int main(int argc, const char *argv[])
{
    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); 
    // uncomment this statement to fix compilation on OS X
#endif

    GLFWwindow *window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "fail to create window" << std::endl;
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    glViewport(0, 0, 800, 600);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    while (!glfwWindowShouldClose(window))
    {

        processInput(window);
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}

void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
    glViewport(0, 0, width, height);
}

void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
    {
        glfwSetWindowShouldClose(window, true);
    }
}

然后在主目录下新建一个build目录

mkdir build

然后进入build目录

cd build

使用以下命令

cmake ..

然后使用make编译

make

如果编译成功,会在build目录下生成HelloGL的二进制文件,使用以下命令启动程序

./HelloGL

如果此时出现一个窗口,这就是你绘制的第一个窗口。

窗口的由来

经过之前的配置我们已经成功创建了一个窗口了,那么这个窗口是如何被创建的呢,接下来我们通过代码仔细分析这个创建的过程。 step1:

首先我们需要引入需要使用的库,也就是之前提到的glfw和glad

# include <glad/glad.h>
# include <GLFW/glfw3.h>

其中需要注意的是glad的引入要在GLFW之前,因为GLFW需要使用glad其中的内容

然后需要创建一个主函数,然后在主函数中对glfw进行初始化和各种配置,以便之后使用,其代码如下, 首先需要使用glfwInit函数对glfw进行初始化,然后使用glfwWindowHint对glfw进行配置,glfwWindowHint函数主要有两个参数,第一个参数是需要配置的项,第二个参数是配置给该项的值。 下面代码通过GLFW_CONTEXT_VERSION_MAJOR配置OpenGL的主版本号,GLFW_CONTEXT_VERSION_MINOR是次版本号,GLFW_OPENGL_PROFILE是配置OpenGL的渲染模式,正如上篇文章所提到的,我嘛使用核心渲染模式,最后由于我是苹果平台,所以需要配置GLFW_OPENGL_FORWARD_COMPAT,保证程序正常执行。

int main() {
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    return 0;
}

step2:

在初始化glfw后我们则需要创建一个窗口,并将其与当前线程绑定,其代码如下:创建一个窗口需要使用glfwCreateWindow函数,其第一个参数是指定窗口的宽度,第二个参数是窗口的高度,第三个参数是窗口的名字,最后两个参数暂时可以不管设置为空即可,然后使用glfwMakeContextCurrent函数将窗口绑定在当前线程。

 GLFWwindow *window = glfwCreateWindow(800, 600, "QStackOpenGL", NULL, NULL);
    if (window == NULL) {
        std::cout<<"Fail to create a glfw window"<<std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);

step3:

正如之前所提到的,glad将为我们管理一些函数的指针,所以在使用OpenGL的一些函数前,我们需要初始化glad,其代码如下:

if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cout<<"fail to init glad"<<std::endl;
        return -1;
    }

在正式渲染之前还需要设置一下视口(viewport),其前两个参数是窗口左上角的坐标,后两个参数是窗口的宽度和高度,设置与GLFW的窗口一致即可。

glViewport(0, 0, 800, 600);

step4: 当用户改变窗口大小的时候,窗口的大小也要随之改变,需要先声明一个函数如下:

void framebuffer_size_callback(GLFWwindow *window, int width, int height);

其具体实现如下:

 void framebuffer_size_callback(GLFWwindow *window, int width, int height) {
        glViewport(0, 0, width, height);
    }

然后需要注册该函数,每次窗口大小改变时调用该函数,实现如下:

 glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

step5: 我们不希望窗口出现一下就消失,而是只要我们不取消就一直存在,那么则需要一个循环一直去绘制窗口,其代码如下:

 while (!glfwWindowShouldClose(window))
    {
      glfwSwapBuffers(window);
      glfwPollEvents();
    }

glfwWindowShouldClose(window)是用来判断GLFW是否接受到关闭指令,如果没有则是true,glfwSwapBuffers(window)是交换窗口的buffer,其实在渲染过程中是有两块buffer的,一块是屏幕上展示的,另一块是在计算下一桢每一个像素的颜色等,如果使用一个buffer,就会显示出渲染的过程,体验不好,且性能也不好,glfwPollEvents()则是接受一个外界的信号如鼠标和键盘输入等。

step6:

当程序运行结束,我们则要关闭GLFW,代码如下:

 glfwTerminate();

最后

这篇文章简要介绍了环境配置以及如何创建一个窗口,更多文章可以关注公众号QStack。