zoukankan      html  css  js  c++  java
  • OpenGL教程(2)——第一个窗口

    注:本文可转载,转载请注明出处:http://www.cnblogs.com/collectionne/p/6618419.html

    OpenGL环境终于配置好了,现在我们可以开始学习OpenGL了。

    首先,创建一个.cpp文件,然后打上几行#include指令:

    #include <iostream>
    using std::cout;
    using std::endl;
    
    #include <GL/glew.h>
    #include <GLFW/glfw3.h>

    先从<iostream>说起。其实<iostream>不是必须的,这里包含它,是因为后面可能需要在控制台上打印信息,提醒用户。glew.h和glfw3.h是我们上一篇教程里所下载的头文件,注意一定要先包含glew.h,再包含glfw3.h(因为glfw3.h会包含gl.h,而这是glew.h不允许的)。最后,如果你使用的是静态版的GLEW,需要在包含glew.h前加上下面的#define指令:

    #define GLEW_STATIC

    然后我们进入main()函数:

    int main()
    {

    GLFW

    首先我们需要初始化GLFW:

    if (!glfwInit())
    {
        cout << "Failed to initialize GLFW!
    ";     
        return -1;
    }

    glfwInit()函数用于初始化GLFW,在调用大部分其它GLFW函数前,都需要初始化GLFW。glfwInit()如果成功,将会返回GLFW_TRUE,否则返回GLFW_FALSE(GLFW_TRUE和GLFW_FALSE是GLFW定义的常量,被定义为于1和0)。也就是说,如果返回GLFW_FALSE,就说明初始化失败,这时我们将打印一条信息告知用户GLFW初始化失败,并且返回(注意,你可能无法看到打印的信息,这时可以在return -1前加上一句cin.get()或system("pause")暂停程序)。不过,检查返回值也不是必须的。

    然后我们需要使用glfwWindowHint()函数设置hint:

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

    Hint直接翻译成中文是“线索”,这里大致指“选项”。使用glfwWindowHint()可以设置一些关于窗口的选项。glfwWindowHint()接受两个参数,第一个是我们要设置的hint的名字,使用GLFW常量(以GLFW_开头)指定;第二个是我们要把该hint设置成的值,该值随要设置的hint而异。

    Hint也有很多种,这里我们只设置了4种,分别是OpenGL主版本号(GLFW_CONTEXT_VERSION_MAJOR)、OpenGL副版本号(GLFW_CONTEXT_VERSION_MINOR)、OpenGL模式(GLFW_OPENGL_PROFILE)、窗口是否可调整大小(GLFW_RESIZABLE)。这4个hint,我们分别设置为3、3、GLFW_CORE_PROFILE、GLFW_FALSE。

    因为我们要使用OpenGL 3.3,所以我们把GLFW_CONTEXT_VERSION_MAJOR和GLFW_CONTEXT_VERSION_MINOR对应的hint都设置为3。因为我们要使用OpenGL核心模式(这个后面会提到更多),所以我们把GLFW_OPENGL_PROFILE对应的hint设置为GLFW_OPENGL_CORE_PROFILE,表示使用OpenGL核心模式。最后,把GLFW_RESIZABLE对应的hint设置为GLFW_FALSE,表示窗口不允许用户调整大小。之所以这样做是因为如果允许用户调整大小,大小发生变化后,窗口的绘制区域默认不变(依然是原来窗口的区域),也就是说窗口上绘制的图像的大小、位置不会发生改变。为了避免这种现象发生,我们就简单地不让用户调整窗口大小(当然也有更好的方法,就是用GLFW设置一个窗口大小的回调函数,但这样比较简单)。

    注意,如果你使用Mac OS X,你需要再加上下面这句调用:

    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);

    然后就该创建窗口了:

    GLFWwindow * window = glfwCreateWindow(800, 600, "First window", nullptr, nullptr);
    if (window == nullptr)
    {
        cout << "Failed to create window using GLFW!
    ";
        return -1;
    }

    glfwCreateWindow()接受5个参数。第一个、第二个是窗口的宽和高,以像素为单位,这里分别是800和600;第三个是窗口标题,这里是"First window";第四个和第五个参数可以忽略,直接传入nullptr。

    glfwCreateWindow()将会使用前面glfwWindowHint()所设置的hint创建窗口,返回一个GLFWwindow指针,我们把这个指针叫做窗口句柄(window handle)。简单说来,就是从此以后我们都用它来代表我们的窗口,后面会需要对窗口进行设置、询问一些关于窗口的信息,就需要传入这个指针。如果创建窗口出现问题,将返回NULL(C++里就是nullptr),因此如果window为nullptr,说明创建窗口失败,打印错误信息,返回-1。

    创建完毕之后,需要让当前窗口的环境在当前线程上成为当前环境(说法有点复杂,不太严谨地说,就是接下来的画图都会画在我们刚刚创建的窗口上):

    glfwMakeContextCurrent(window);

    GLEW

    GLFW设置完毕后,还需要初始化GLEW:

    glewExperimental = GL_TRUE;
    if (glewInit())
    {
        cout << "Failed to init GLEW!
    ";
        return -1;
    }

    首先我们把glewExperimental设置为GL_TRUE,这样GLEW就会使用更加新的方法管理OpenGL,减少错误。然后调用glewInit()初始化GLEW。注意glewInit()成功返回0,失败返回1,与GLFW相反。如果失败则输出错误信息,并返回-1。

    游戏循环

    和控制台程序不同,我们希望这个程序可以一直运行,直到用户关闭窗口。这样我们就需要创建一个循环,叫做游戏循环(game loop)。

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

    glfwWindowShouldClose()检查窗口是否需要关闭。如果是,游戏循环就结束了,接下来我们将会清理资源,结束程序。

    glfwSwapBuffers()用来交换窗口的两个颜色缓冲(color buffer)。这个概念叫做双缓冲(double buffer)。如果不使用双缓冲,就可能会出现闪屏现象,因为绘制一般不是一下子就绘制完毕的,而是从左到右、从上到下地绘制。为了避免这个问题,一般会使用双缓冲,前缓冲(front buffer)是最终的图像,而程序会在后缓冲(back buffer)上绘制。后缓冲绘制完毕后,就交换两个缓冲,这样就不会有闪屏的问题了。

    glfwPollEvents()用来检查是否有事件被触发,例如点击关闭按钮、点击鼠标、按下键盘,等等。如果有,GLFW将会对这些事件进行处理。更严谨地说(以键盘、鼠标键为例),GLFW自己会记录每个键、鼠标键的状态(按下/没有按下),但当某个按键松开或被按下时,GLFW不会自动更新状态,必须调用glfwPollEvents()才能更新。调用glfwPollEvents()时会检查状态是否有变化(如按下的是否松开,没有按下的是否被按下),如果有就会更新该状态。如果设置了回调函数(这个将在以后讲),还会调用相应的回调函数。如果不调用这个函数,不仅无法检测输入(后文会需要这样),我们在点击窗口右上角的X时,GLFW也不会知道需要关闭窗口。所以必须在每一轮游戏循环中调用这个函数。

    最后,当循环执行完毕后,我们需要释放前面所申请的资源:

    glfwTerminate();
    return 0;
    }

    glfwTerminate()将会释放所有GLFW资源,关闭当前窗口。然后程序就成功退出了。

    现在运行你的程序,如果你得到了一个黑色窗口,恭喜你,成功了!如果没有,请参考结尾的代码,对照自己的代码。

    额外的东西

    现在我们已经成功创建窗口,并且在用户点击右上角的X时退出。但是还不够——你可能会问,可不可以让用户按ESC键时退出,或者换一个窗口背景颜色?这里我们就来实现这两项。

    关于第一个功能,可以这样写(在游戏循环里):

    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GLFW_TRUE);

    glfwGetKey()用来判断一个键是否按下。第一个参数是GLFW窗口句柄,第二个参数是一个GLFW常量,代表一个键。GLFW_KEY_ESCAPE表示Esc键。如果Esc键按下了,glfwGetKey将返回GLFW_PRESS(值为1),否则返回GLFW_RELEASE(值为0)。

    如果Esc被按下了,我们就会调用glfwSetWindowShouldClose()函数,为窗口设置关闭标志。第一个参数是窗口句柄,第二个参数表示是否关闭,这里为GLFW_TRUE,表示关闭该窗口。注意,这时窗口不会立即被关闭,但是glfwWindowShouldClose()将返回GLFW_TRUE,到了glfwTerminate()就会关闭窗口。

    关于第二个功能,可以在进入游戏循环之前,加一句:

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

    与其它glfw开头的函数(这些函数由GLFW提供)不同,这个函数是以gl开头的,是OpenGL的函数。glClearColor用来设置窗口被清除时的颜色,也就是背景颜色。前3个参数分别是背景颜色的R、G、B分量,范围是0~1;第4个是alpha值表示透明度,这里只是设为1.0f,表示不透明。(0.2, 0.3, 0.3)这一颜色大致是较暗的蓝绿色。

    然后在游戏循环中加一句:

    glClear(GL_COLOR_BUFFER_BIT);

    这将会清除当前窗口,把所有像素的颜色都设置为前面所设置的清除颜色。GL_COLOR_BUFFER_BIT是OpenGL定义的常量,表示清除颜色缓存。后面会学到,还有GL_DEPTH_BUFFER_BIT和GL_STENCIL_BUFFER_BIT。

    现在运行程序,你应该会得到下面的画面,并且按Esc时会关闭窗口:

    最终代码

    #include <iostream>
    using std::cout;
    using std::endl;
    
    #include <GL/glew.h>
    #include <GLFW/glfw3.h>
    
    int main()
    { 
        // init GLFW
        if (!glfwInit())
        {
            cout << "Failed to init GLFW!
    ";
            return -1;
        }
    
        // set window hints
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
    // create the window GLFWwindow * window = glfwCreateWindow(800, 600, "First window", nullptr, nullptr); if (window == nullptr) { cout << "Failed to create window using GLFW! "; return -1; } glfwMakeContextCurrent(window); // init GLEW glewExperimental = GL_TRUE; if (glewInit()) { cout << "Failed to init GLEW! "; return -1; } // set clear color glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // game loop while (!glfwWindowShouldClose(window)) { // if Esc is pressed, close the window if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, GLFW_TRUE); // clear the window glClear(GL_COLOR_BUFFER_BIT); // poll and swap glfwSwapBuffers(window);
    glfwPollEvents(); }
    // cleanup glfwTerminate(); return 0; }

    好啦,你已经学会了创建窗口的基本技巧,下一课,我们将会学习如何画我们的第一个三角形!

  • 相关阅读:
    单例模式
    Curator Zookeeper分布式锁
    LruCache算法原理及实现
    lombok 简化java代码注解
    Oracle客户端工具出现“Cannot access NLS data files or invalid environment specified”错误的解决办法
    解决mysql Table ‘xxx’ is marked as crashed and should be repaired的问题。
    Redis 3.0 Cluster集群配置
    分布式锁的三种实现方式
    maven发布项目到私服-snapshot快照库和release发布库的区别和作用及maven常用命令
    How to Use Convolutional Neural Networks for Time Series Classification
  • 原文地址:https://www.cnblogs.com/collectionne/p/6618419.html
Copyright © 2011-2022 走看看