当前位置:首页 > > 正文内容

OpenGL(三)

admin1年前 (2020-09-23)437

以下内容均来自此网站:https://learnopengl-cn.github.io/01%20Getting%20started/03%20Hello%20Window/

 

1、GLFW的初始化与配置

  1. 初始化GLFW,利用 glfwInit函数对GLFW进行初始化,

  2. 然后使用glfwWindowHint函数对GLFW进行配置

    函数原型是这个样子的:

    void glfwWindowHint(int hint, int value)

  3. 这个函数无返回值,有两个参数,第一个参数是一些选项,第二个参数是配合第一个参数这些选项的值。例如:

glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3) 这句中参数的意思就是设置使用的OpenGL主版本号为3,第一个参数就是上下文主版本号的意思,第二个参数说明要设置的上下文主版本号为3。 同理第二句代码设置上下文的次版本号为3,第三句设置OpenGL的模式为核心模式。

当然还可以设置的参数还有好多,如GLFW_RESIZABLE设置是否可以让用户改变窗口大小,默认值为GLFW_TRUEGLFW_MAXIMIZED设置是否窗口最大化,默认值是GLFW_FALSE,当然还可以设置的值还有很多,可以看下表:三列值分别为选项、默认值和可选的值。详细信息看这里https://www.glfw.org/docs/latest/window_guide.html#window_hints

另外说一句:可以在glfw3.h头文件中看到,Window hint 和 Supported values 这些值都是使用宏定义(#define)的,且宏定义的值是用16进制表示的。

Window hintDefault valueSupported values
GLFW_RESIZABLEGLFW_TRUEGLFW_TRUE or GLFW_FALSE
GLFW_VISIBLEGLFW_TRUEGLFW_TRUE or GLFW_FALSE
GLFW_DECORATEDGLFW_TRUEGLFW_TRUE or GLFW_FALSE
GLFW_FOCUSEDGLFW_TRUEGLFW_TRUE or GLFW_FALSE
GLFW_AUTO_ICONIFYGLFW_TRUEGLFW_TRUE or GLFW_FALSE
GLFW_FLOATINGGLFW_FALSEGLFW_TRUE or GLFW_FALSE
GLFW_MAXIMIZEDGLFW_FALSEGLFW_TRUE or GLFW_FALSE
GLFW_CENTER_CURSORGLFW_TRUEGLFW_TRUE or GLFW_FALSE
GLFW_TRANSPARENT_FRAMEBUFFERGLFW_FALSEGLFW_TRUE or GLFW_FALSE
GLFW_FOCUS_ON_SHOWGLFW_TRUEGLFW_TRUE or GLFW_FALSE
GLFW_SCALE_TO_MONITORGLFW_FALSEGLFW_TRUE or GLFW_FALSE
GLFW_RED_BITS80 to INT_MAX or GLFW_DONT_CARE
GLFW_GREEN_BITS80 to INT_MAX or GLFW_DONT_CARE
GLFW_BLUE_BITS80 to INT_MAX or GLFW_DONT_CARE
GLFW_ALPHA_BITS80 to INT_MAX or GLFW_DONT_CARE
GLFW_DEPTH_BITS240 to INT_MAX or GLFW_DONT_CARE
GLFW_STENCIL_BITS80 to INT_MAX or GLFW_DONT_CARE
GLFW_ACCUM_RED_BITS00 to INT_MAX or GLFW_DONT_CARE
GLFW_ACCUM_GREEN_BITS00 to INT_MAX or GLFW_DONT_CARE
GLFW_ACCUM_BLUE_BITS00 to INT_MAX or GLFW_DONT_CARE
GLFW_ACCUM_ALPHA_BITS00 to INT_MAX or GLFW_DONT_CARE
GLFW_AUX_BUFFERS00 to INT_MAX or GLFW_DONT_CARE
GLFW_SAMPLES00 to INT_MAX or GLFW_DONT_CARE
GLFW_REFRESH_RATEGLFW_DONT_CARE0 to INT_MAX or GLFW_DONT_CARE
GLFW_STEREOGLFW_FALSEGLFW_TRUE or GLFW_FALSE
GLFW_SRGB_CAPABLEGLFW_FALSEGLFW_TRUE or GLFW_FALSE
GLFW_DOUBLEBUFFERGLFW_TRUEGLFW_TRUE or GLFW_FALSE
GLFW_CLIENT_APIGLFW_OPENGL_APIGLFW_OPENGL_API, GLFW_OPENGL_ES_API or GLFW_NO_API
GLFW_CONTEXT_CREATION_APIGLFW_NATIVE_CONTEXT_APIGLFW_NATIVE_CONTEXT_API, GLFW_EGL_CONTEXT_API or GLFW_OSMESA_CONTEXT_API
GLFW_CONTEXT_VERSION_MAJOR1Any valid major version number of the chosen client API
GLFW_CONTEXT_VERSION_MINOR0Any valid minor version number of the chosen client API
GLFW_CONTEXT_ROBUSTNESSGLFW_NO_ROBUSTNESSGLFW_NO_ROBUSTNESS, GLFW_NO_RESET_NOTIFICATION or GLFW_LOSE_CONTEXT_ON_RESET
GLFW_CONTEXT_RELEASE_BEHAVIORGLFW_ANY_RELEASE_BEHAVIORGLFW_ANY_RELEASE_BEHAVIOR, GLFW_RELEASE_BEHAVIOR_FLUSH or GLFW_RELEASE_BEHAVIOR_NONE
GLFW_OPENGL_FORWARD_COMPATGLFW_FALSEGLFW_TRUE or GLFW_FALSE
GLFW_OPENGL_DEBUG_CONTEXTGLFW_FALSEGLFW_TRUE or GLFW_FALSE
GLFW_OPENGL_PROFILEGLFW_OPENGL_ANY_PROFILEGLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE or GLFW_OPENGL_CORE_PROFILE
GLFW_COCOA_RETINA_FRAMEBUFFERGLFW_TRUEGLFW_TRUE or GLFW_FALSE
GLFW_COCOA_FRAME_NAME""A UTF-8 encoded frame autosave name
GLFW_COCOA_GRAPHICS_SWITCHINGGLFW_FALSEGLFW_TRUE or GLFW_FALSE
GLFW_X11_CLASS_NAME""An ASCII encoded WM_CLASS class name
GLFW_X11_INSTANCE_NAME""An ASCII encoded WM_CLASS instance name

2、创建窗口对象

初始化glfw且设置完一些选项后,就可以创建窗口对象了,用的是glfwCreateWindow这个函数,函数原型如下:

GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share)

函数有4个参数,第一个参数是窗口宽度。第二个参数是窗口高度,第三个是窗口的标题,后面两个参数都是窗口对象(暂时设置为null),函数的返回值为GLFWwindow对象。

如果创建成功,则返回一个window对象,如果创建失败,则返回一个空指针(null)。可用下面的方式来判断:

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

如果返回空指针,说明创建窗口对象失败,输出一句创建失败的提示,同时需要调用一下glfwTerminate函数终止并释放所有分配的资源,最后返回 -1 。

如果没问题,接下来用glfwMakeContextCurrent函数来将我们窗口的上下文设置为当前线程的主上下文。如下:

glfwMakeContextCurrent(window);

3、初始化GLAD

之前提到,在运行时查找OpenGL的函数并存储在一个函数指针中使用是很麻烦的,所以需要借助GLAD库来简化这个过程,通过以下方式来初始化GLAD获取OpenGL函数指针:

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

4、视口大小

教程中提到了要设置视口大小,视口是什么,可以认为是实际上OpenGL渲染的窗口的大小,这个大小是小于等于窗口大小的。教程中的这个位置没有写设置饰扣大小的代码,也并未有什么影响,只是好像这样视口大小和窗口大小是一样的,这里暂且不管。呃,后面提到了,当窗口被第一次显示的时候framebuffer_size_callback(马上写到这个函数)也会被调用。

正常情况下用户可能会改变窗口大小,这样的话视口大小也是要随着改变的,所以要对窗口注册一个回调函数,每当窗口大小发生变化时调用这个函数使相应的视口调整相应的大小。

为窗口大小调整时注册回调函数的函数是:

glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

这个函数有两个参数,第一个是要注册函数的窗口,第二个参数是回调函数,其实就是一个函数指针。好,这里我们已经有了窗口对象,那么就只需要写回调函数,通过查看函数的定义,可以看到回调函数的原型(因为本质上回调函数这个参数是个函数指针,调用的函数与函数指针要匹配)是:

typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int);

该函数原型无返回值,需要三个参数,第一个参数是窗口对象,第二三个参数分别是调整后视口的宽和搞,所以如下设计回调函数:

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

函数名叫framebuffer_size_callback,参数和返回值与要求的一样,当然第一个参数这里没有用到,到是没有什么影响,暂且忽略。函数内部用到了一个OpenGL的函数glViewport,这个函数用于调整视口的大小,四个参数,第一二个参数是视口左下角的坐标,这里设置为(0,0),第三四个参数是视口的宽和高。

于是为窗口注册回调函数就这样写:

glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

5、循环渲染(绘制)

上面的几步走完后,如果运行会发现窗口一闪而过就结束了,我们需要循环绘制画面,称为渲染循环,需要一个while循环一直进行,而循环的判断条件是使用glfwWindowShouldClose这个函数,这个函数检查当前窗口是否要求被关闭,返回值是int类型,代表是否关闭的标志(flag),只有一个参数,是要检查是否关闭的窗口。每次循环该函数都检查一下是否要求关闭,是的话循环结束,程序继续后面内容然后就结束了。完整代码如下:

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

当然循环里面还有两个函数,glfwSwapBuffersglfwPollEvents,这里依次说一下:

glfwSwapBuffers是交换前后的颜色缓冲,这是因为这里用的是双缓冲模式,使用单缓冲的时候,生成的图像是从左到右从上到下依次绘制出来的,不是一下绘制出来,所以可能存在图像闪烁的问题。这里使用双缓冲(Double Buffer),前缓冲保存着最终输出的图像,后缓冲进行着绘制,当所有渲染指令完成后,交换前后缓冲,这样图像就立即显示出来,不会出现单缓冲时出现的闪烁不真实感等问题。这个函数无返回值,只有一个传入的窗口参数。

glfwPollEvents检查有没有触发什么事件(键盘输入、鼠标移动等),更新窗口状态,并调用对应的回调函数(如果不加这句,之前写的回调函数就用不到了)。函数无参无返回值。

最后如果渲染结束的话应该调用glfwTERminate函数来释放/删除之前分配的所有资源:

glfwTerminate();
return 0;

到这里整个流程就基本结束了,如果运行的话,会出现一个黑色的窗口,事实上,整个看似什么都没有的窗口一直在刷新,而且刷新速率还很高,我这UHD630的核显都能达到六七百帧的速度。

6、处理输入

我们也希望GLFW能够处理一些输入控制,可以通过glfwGetKey函数来完成,两个参数,返回值是int,参数分别是需要检测按键的窗口和需要检测按下的键,检测到返回1,没检测到返回0。

为了简洁好看,将检测过程写到函数里面,每次循环的时候调用,如下:

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

 

以下是完整代码:

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

#include <iostream>

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

// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

int main()
{
    // glfw: 初始化和配置
    // ------------------------------
    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

    // 创建glfw窗口
    // --------------------
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // glad: 加载所有的OpenGL函数指针
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }    

    // 循环渲染
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        // 处理输入
        // -----
        processInput(window);

        // glfw: 交换缓冲区和处理IO事件 (按下释放按键,鼠标移动等等)
        // -------------------------------------------------------------------------------
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // glfw: 终止,释放清空之前分配的资源
    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;
}

// 处理所有输入: 查询GLFW在当前帧是否有相关的键按下/释放,并且做出相应的反应
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
    if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

// glfw: 无论何时窗口的大小发生改变(有操作系统或者用户导致)这个回调函数都会被执行
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    // 确保视口与新窗口维度相匹配; 注意对于视网膜(Retina)显示屏,width和height都会明显比原输入值更高一点。
    glViewport(0, 0, width, height);
}


版权声明:本文由cyhu's essay发布,如需转载请注明出处。

本文链接:https://lovedm.club/?id=84

相关文章

QSRL+H410主板

QSRL+H410主板

之前想整个i5 10400t,但是性能不强且价格不是很美丽,也就没搞,但是最近发现了这玩意的es版,就入了一个玩玩,主板是昂达的H410SD4 ITX全固版,这板子有够吐槽的,后面板只有两个USB2....

内网穿透下搭建方舟服务器

内网穿透下搭建方舟服务器

 EPIC领了方舟,顺便steam上也买了,现在的问题是搭服务器没有公网ip,没法和基友们愉快玩耍,于是乎用内网穿透吧。需要准备:有公网ip的服务器一台,frp软件,搭建方舟的服务器一台。公...

矩阵乘法次数

p*q的矩阵和q*r的矩阵相乘,计算时需要的乘法的次数是p*q*r 。想想很简单,最后算出来的矩阵是p*r的,最后每个矩阵元素在计算时算了q次乘法,所以最后算出来总共是p*q*r次乘法。...

OpenGL(一)

OpenGL一般认为是一个API,包含了一系列可以操作图形、图像的函数,但是实际上仅仅是一个规范,OpenGL规范严格规定了每个函数该如何执行,以及它们的输出值。具体里面每个函数的实现由库的开发者去实...

Origin9.1绘图时加外边框

参考百度经验https://jingyan.baidu.com/article/4b07be3caf7e6448b280f36d.html...

Windows远程桌面隐藏标题栏

Windows远程桌面隐藏标题栏

远程桌面开网页时上面的标题栏总是碍事,可以设置一下关掉。点击显示选项在显示选项卡,把下面的 全屏显示时显示连接栏 选项勾掉。若想显示连接栏或退出,可如下操作:1、CTRL+ALT+Home。...