OpenGL中创建一个窗口

来源:互联网 发布:淘宝美工学费多少 编辑:程序博客网 时间:2024/04/28 13:45

近日需要把一副全景图映射到一个立方体上,在立方体中间架一台摄像机,在摄像机中间看到的任何一个方向都是很真实的效果;这中间直观来说有两个步骤,第一个就是把全景图映射到一个球上,之后通过建一个立方体,这个立方体的中心通过球心,那么这立方体六个面上的每一个面是怎样的图像,我们主要的目的是实现这个。
网上有很多学习OpenGL的教程,下面推荐一个我一直在用的:LearnOpenGL。之后的学习OpenGL大都参考这个链接。


相关文件的配置

有很多针对OpenGL的C语言库,GLFW是其中一个;GLFW定义渲染物体所需的最低限的借口,允许用户创建OpenGL上下文,定义窗口参数以及处理用户输入。可以从其强调内容强调内容强调内容下载页上面获取,页面上有已经编译好的windows系统的文件,也有源文件,我们这里下载已经编译好的windows上面的文件。与其它库文件一样,我们需要把dll的文件拷贝到C:\Windows\System与C:\Windows\SysWOW64中,需要把lib与.h的头文件拷贝到vs安装目录下的lib文件夹与include文件夹中,比如我的路径是E:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\lib与E:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include;最好在包含头文件的include文件夹中新建一个文件夹,方便管理。
在添加上述的dll、lib以及头文件之后,还需要在新建的工程中添加有关的lib文件;添加的路径是在新建的项目的属性中的配置属性中的链接器的输入中的附加依赖项添加相应的lib文件,比如这里我们添加opengl32.lib与glfw3.lib;这样的话,GLFW在编译的时候就会被链接进来了,上面opengl32.lib已经包含在Microsoft的SDK中。这里写图片描述
另外还需要一个GLEW(OpenGL Extension Wrangle Library)库,这个库能够很有效的解决在调用一个函数必须对其进行声明的问题,可以从这里下载,同样的,按照上述步骤配置相应的dll以及lib、h文件;同样的,要在新建项目的属相中添加相应的lib文件。


设置窗口以及相应的尺寸

包含相应的头文件:

// GLEW#define GLEW_STATIC#include <GL/glew.h>// GLFW#include <GLFW/glfw3.h>

创建一个main函数,并且实例化GLFW窗口

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_RESIZABLE, GL_FALSE);    return 0;}

首先调用glfwInit函数初始化GLFW,之后就可以使用glfwWindowHint函数来配置GLFW;glfwWindowHint函数内的第一个输入参数代表一个选项的名称,有很多这样的名称,第二个参数用来接受对应这个选项的值。比如这一句话:

glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);

第一个参数定义了我们的=使用Opengl主版本号(MAJOR),第二个参数说明主版本号是3.3;同样的能够理解下一句是定义Opengl的次版本号是3;同样告诉opengl是核心模式(core-profile),并且不允许调整窗口大小。上面的设置主版本以及次版本号是3.3,对系统的要求是必须支持Opengl3.3或者更高的版本,这主要与显卡有关。
接下来创建一个窗口对象,这个窗口对象存放了所有与窗口有关的数据,而且会被GLFW的其它函数频繁的用到。

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

glfwCreateWindow函数需要宽和高作为它的前两个参数,第三个参数是名称。就这样创建了一个窗口。
之前提到过,在使用任何函数之前都需要初始化GLEW:

    glewExperimental = GL_TRUE;    if (glewInit() != GLEW_OK)    {        std::cout << "Failed to initialize GLEW" << std::endl;        return-1;    }    glfwMakeContextCurrent(window);//不知道这一句的作用是什么

函数的意思能够从很显然能够从字面上理解
只是创建了窗口是不行的,必须要告诉OpenGL渲染窗口尺寸的大小,通过调用glViewport函数来设置窗口的维度:

    int width, height;    glfwGetFramebufferSize(window, &width, &height);    glViewport(0, 0,width, height);//测试渲染窗口的尺寸大小

在这里读者可能会问,为什么不直接设置为800*600,是为了在更高分辨率的电脑上也能够正常工作。


游戏循环、按键控制以及渲染

运行上述程序后不难发现,运行完程序之后立即退出并关闭窗口,我们希望程序在我们明确关闭之前不断绘制图像,即在未接收外界指令或者信号之前是不断地刷新屏幕。我们需要在程序这种添加一个while循环,之后会看到,我们在while循环中能够做很多事情,比如添加时间变量,就能够自动的改变显示的图像等。

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

glfwWindowShouldClose函数在每一次循环开始前检车GLFW是否要求被退出,如果是true的话,那么循环结束,刷新结束,就可以直接结束程序了。glfwPollEvents函数检查有没有触发什么事件(鼠标移动,键盘输入等),然后调用对应的回调函数,一般在游戏循环的开始调用事件处理函数,这大概类似于单片机中的中断,在我看来,当然中断执行完之后还需要再返回到断点处;glfwSwapBuffers会交换颜色缓冲区,即在显示上一帧的时候已经开始渲染下一帧;这样能够消除抖动的问题。
在游戏结束之后,我们需要清理所有的资源并正确的退出,记得之前说过glfw创建一个窗口对象的时候已经分配了内存,所以:

    glfwTerminate();    return 0;

最终的输出结果如下图所示:
运行程序显示的结果图

像其它的库一样,GLFW也能够实现键盘的控制,就像刚才在游戏循环中检查有没有事件发生一样,可以通过GLFW的回调函数来完成;回调函数事实上是一个函数指针,我们设置好之后会在合适的时候调用它;按键回调是众多的回调函数中的一种,设置了按键回调之后,GLFW会在有键盘交互的时候调用它。函数原型如下所示:

void key_callback(GLFWwindow*window, int key, int scancode, int action, int mode);

第一个参数指定了要操作的对象,而且我们按下按键的时候必须在这份窗口上,即用鼠标点击一下这个窗口;第二个参数制定了按下的按键,比如按下的是ESC按键,对应的key即为GLFW_KEY_ESCAPE,GLFW已经在内部对这些按键进行宏定义,第三个参数不知道是什么,第四个参数表示是按下还是释放,第五个参数来表示是否有ctrl、alt以及shift等的操作;在此我们设置按下ESC按键之后就退出窗口,具体函数如下:

void key_callback(GLFWwindow*window, int key, int scancode, int action, int mode){    if (key == GLFW_KEY_ESCAPE&&action == GLFW_PRESS)        glfwSetWindowShouldClose(window, GL_TRUE);}

上述函数的意思是当按下esc按键时候,setwindowshouldclose为true,这样的话如果再检查游戏循环的话,那么就直接退出了,所以要在游戏循环中设置调用这个回调函数:

glfwSetKeyCallback(window, key_callback); 

亲测该回调函数在glfwPollEvents()前或者后都行。其实也可以注册其它的回调函数,而不一定是按键,比如鼠标的动作,在之后将会看到通过鼠标的点击以及滚轮以及鼠标的移动能够做很多有意思的事情,而且也不一定在游戏循环中调用回调函数,在循环之前调用回调函数也可以,那么就可以做很多有意思的事情比如我们可以根据一些按键来创建一个游戏。

所有的渲染都要放在游戏循环之中,因为每一次循环都是刷新一次窗口显示的内容,先要窗口显示不同的内容,必须在每一次刷新的时候执行这些操作,所以游戏循环的内部的结构大概是类似于这样:

//游戏循环    while (!glfwWindowShouldClose(window))    {         //检查事件        glfwPollEvents();        ......         //渲染指令        ......         //交换缓冲        glfwSwapBuffers(window);    }

所以游戏循环中要做的事情大概就是先检查事件,之后渲染指令,之后交换缓冲,之后的着色器中也属于渲染指令,也在渲染指令这个位置。
在每一个新的渲染迭代开始的时候我们总希望清屏,否则仍然能够看到上一帧的渲染结果,通过glClear函数来清空屏幕,同时还调用glClearColor函数来设置清空屏幕所用的颜色:

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

glClearColor内的参数应该分别是RGB颜色值以及alpha通道,意思就是清空颜色所用的颜色值;glClear函数内的参数亦即清空颜色值,它们两个一般需要配合使用。
运行的结果如下:这里写图片描述
整个程序如下:

#include<iostream>#include<GL/glew.h>#include<gl/glfw3.h>//声明函数原型void key_callback(GLFWwindow*window, int key, int scancode, int action, int mode);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_RESIZABLE, GL_FALSE);//测试可以伸缩的尺寸    GLFWwindow*window = glfwCreateWindow(1000, 600, "LearnOpenGL", nullptr, nullptr);    if (window == nullptr)    {        std::cout << "Failed to creat GLFW window" << std::endl;        glfwTerminate();        return -1;    }    glfwMakeContextCurrent(window);//不知道这一句的作用是什么    glewExperimental = GL_TRUE;    if (glewInit() != GLEW_OK)    {        std::cout << "Failed to initialize GLEW" << std::endl;        return-1;    }    int width, height;    glfwGetFramebufferSize(window, &width, &height);    glViewport(0, 0,width, height);//测试渲染窗口的尺寸大小    while (!glfwWindowShouldClose(window))    {        //glfwSetKeyCallback(window, key_callback);        glfwPollEvents();        glfwSetKeyCallback(window, key_callback);        //渲染        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);        glClear(GL_COLOR_BUFFER_BIT);        //交换缓冲        glfwSwapBuffers(window);    }    glfwTerminate();    return 0;}void key_callback(GLFWwindow*window, int key, int scancode, int action, int mode){    if (key == GLFW_KEY_ESCAPE&&action == GLFW_PRESS)        glfwSetWindowShouldClose(window, GL_TRUE);}
0 0
原创粉丝点击