OpenGL学习脚印: 环境搭建

写在前面
若是你正在阅读本文,那么你和我同样可能喜欢图形学或者游戏编程,想尝试编写本身的图形App :)。图形学的初学者每每很是着急,但愿能当即编写出丰富生动、逼真又富有交互性的3D应用,至少我是这样。css

笔者翻看了图形学教材,红宝书,3D数学基础等大部头书后,感受到根据教材或者网络tutorial编写简单的应用程序并不困难,难点在于可以保持足够热情,坚持学习,使本身对图形学原理有更深更完整理解,培养自主学习更高级话题的能力和解决实际问题的能力。
贪多嚼不烂,若是过于急躁,学习教材或者网络tutorial就会感受要么太简单,要么太难,难以锲而不舍。所以我写下这一系列博客,目的在于保持本身的热情,保持本身的节凑,以OpenGL编程为切入点,扎实学习图形学原理,从而加强图形编程能力。html

在学习OpenGL编程的过程当中,当前感触比较深的是:宁愿暂时跳过一些复杂的数学背景知识,也不要所以损害到热情,兴趣很是重要。所以和我同样对图形学有热情的同路人,若是遇到难点必定不要轻易放弃,若是一次不能彻底理解,能够标记下来,回过头来再作研究。
本系列将使用 windows & visual studio 2013 & C++ 做为开发平台,笔者建议初学者也是如此,尽可能不要把精力耗费在linux下库文件编译和显卡程序安装,编程语言细节(例如C++11新特性,java语言绑定版本)等琐碎事情上,更多地关注图形原理和实现。java

话不絮烦,下面开始本节内容。linux

经过本节,能够了解到:ios

  • OpenGL基本特色
  • Windows下OpenGL开发环境搭建
  • 第一个OpenGL程序-准备Context

OpenGL基本特色

OpenGL是一个跨语言和跨平台的图形编程接口,这个API用来与GPU交互,实现基于硬件加速的图形渲染。OpenGL其实是一个抽象的图形绘制规范,尽管能够由软件来彻底实现,但它被设计为大部分或者所有由硬件来实现。OpenGL是一个绘图规范,不一样的显卡厂商负责具体实现,所以实现的具体程序也不尽相同。OpenGL是跨平台的,支持PC端,嵌入式设备,手机设备的渲染;同时也是跨语言的,它底层使用C语言实现,支持java语言、Python、javaScript绑定。
OpenGL包括旧的版本和现代版本 旧版本的绘图功能大多数在GPU内完成,某些功能没法实现,现代版本使用着色器程序提供了更大的灵活性。旧式版本是指1.x和2.x版本,现代版本是指3.x以上版本。二者函数的一个区别以下:git

  • 旧版函数: glBegin, glVertex3f, glLightfv, glPushMatrix
  • 现代版函数:glDrawArrays, glVertexAttribPointer, glUniform, glCreateShader.

当3.2规范发布后,OpenGL支持两种profile:github

  • Core Profile, 仅支持未被废弃特性。
  • The Compatibility Profile (兼容模式) 支持全部特性
    尽管在兼容模式下,仍然能够运行那些已经被废弃的API编写的程序,但为了程序之后的兼容性,咱们将使用OpenGL3.3版本,开启core profile。
    想了解更多,关于为何不要使用旧版OpenGL,能够参考Don’t use old OpenGL,以及Learning Modern OpenGL。关于如何从旧版转移到现代版本,能够参考Transition to Core Profile.

一个值得注意的问题是,为了保持跨平台,OpenGL规范中并未涉及到OpenGL context的建立和管理部分,这部分的实现依赖于不一样系统的窗口管理系统。所以在编写OpenGL程序时须要借助第三方库完成与窗口系统的交互,建立绘制上下文对象(render context)。web

开发环境搭建

上面已经提到OpenGL的具体实现由显卡驱动厂商负责,那么不一样的厂商实现的库可能有所不一样,有的函数可能没有实现,有的包含拓展的实现,并且厂商的实现通常经过动态连接库来提供。通常地使用一个函数前来查询函数是否实现了,并且要关心版本问题,这些细节比较繁琐,能够交给第三方库GLEW来实现。
同时与操做系统的窗口系统交互,建立绘制上下文对象,也是与具体操做系统相关的,能够交给第三方库GLFW来实现(固然还有其余选择,包括 freeglut,SDL,GLFW目前是比较推荐的)。
总结为咱们须要下载和编译两个第三方库:编程

  • GLEW 负责绘图函数的获取
  • GLFW 负责建立绘图上下文和窗口系统管理

下载并编译后,获取到两个库的lib和dll文件,咱们就能够开始编写第一个程序了。编译源文件的过程,能够选择对应的visual studio版本,而后编译为默认的调试版本便可。若是以为下载和编译源码费劲,我也为你提供了Windows下编译好的库文件和必要的头文件,点击从github下载库文件windows

第一个OpenGL程序-准备Context

利用上面编译的两个第三方库,咱们能够实现本身的第一个程序,建立用于渲染图形的上下文对象。
通常而言,OpenGL程序的流程大体都是这样(伪代码描述来自open.gl):

#include <libraryheaders>

int main()
{
    initLibraries();   // 初始化相关库
    createWindow(title, width, height); // 建立窗口
    createOpenGLContext(settings); // 建立绘制上下文

    while (windowOpen)  // 游戏主循环
    {
        while (event = newEvent())
            handleEvent(event); // 处理事件

        updateScene(); //更新场景中内容

        drawGraphics(); // 绘制场景
        presentGraphics(); // 交换缓冲区 呈现场景
    }
    freeResource();  // 资源清理
    return 0;
}

根据上述思路,咱们开始建立本节的CreateContext程序。

Step1 : 新建项目CreateContext 在项目属性中连接头文件和库文件。在visual studio里面设置便可。
以下图 设置头文件目录和库文件目录:
这里写图片描述

以下图 连接库文件:
这里写图片描述

Step2 添加createContext.cpp文件。
首先咱们要包含头文件以下:

// 引入GLEW库 定义静态连接
#define GLEW_STATIC
#include <GLEW/glew.h>
// 引入GLFW库
#include <GLFW/glfw3.h>

这里注意两点:

  • 首先包含glew,由于glew里面会包含gl.h等文件 这些文件是系统中OpenGL库文件
  • 定义GLEW_STATIC表示静态连接glew库,固然也可使用GLEW的动态连接库dll

包含头文件后,注意咱们须要首先建立绘制上下文,而后才能初始化glew库(想了解更多请参考Initializing GLEW)。另外注意GLFW和GLEW初始化的返回值,对于出错状况要作好处理。
在上文提到,咱们应该使用OpenGL core profile, 经过glfw的函数这样实现:

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

这里提示glfw开启OpenGL 3.3版本.
为了保持程序简单,不涉及放大缩小等问题,这里经过

glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

使窗口固定大小。同时在绘制场景前,使用

glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);

为窗口指定显示区域大小,关于这个参数,咱们后面会继续深刻了解。

最后是窗口事件处理,这里以键盘事件为例,当用户按下Esc键时,退出应用程序,键盘事件在主循环中由glfwPollEvents处理,这个函数处理时会调用键盘事件的回调函数,所以咱们须要声明和编写键盘回调函数以下:

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
    {
        glfwSetWindowShouldClose(window, GL_TRUE); // 关闭窗口
    }
}

想了解更多的输入事件和参数详细解释,能够参考glfw Input guide.
一个好的习惯时,程序主循环结束时,释放程序占用的资源,这里咱们经过:

glfwTerminate(); // 释放资源

释放GLFW分配的资源。当程序中分配有其余资源时,也须要作相应的回收处理, 这点要注意。
经过上述分析,完整的代码以下(点击从github下载代码):

// 引入GLEW库 定义静态连接
#define GLEW_STATIC
#include <GLEW/glew.h>
// 引入GLFW库
#include <GLFW/glfw3.h>
#include <iostream>


// 键盘回调函数原型声明
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);

// 定义程序常量
const int WINDOW_WIDTH = 800, WINDOW_HEIGHT = 600;


int main(int argc, char** argv)
{

    if (!glfwInit())    // 初始化glfw库
    {
        std::cout << "Error::GLFW could not initialize GLFW!" << std::endl;
        return -1;
    }

    // 开启OpenGL 3.3 core profile
    std::cout << "Start OpenGL core profile version 3.3" << std::endl;
    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(WINDOW_WIDTH, WINDOW_HEIGHT,
        "Demo of createContext", NULL, NULL);
    if (!window)
    {
        std::cout << "Error::GLFW could not create winddow!" << std::endl;
        glfwTerminate();
        return -1;
    }
    // 建立的窗口的context指定为当前context
    glfwMakeContextCurrent(window);

    // 注册窗口键盘事件回调函数
    glfwSetKeyCallback(window, key_callback);

    // 初始化GLEW 获取OpenGL函数
    glewExperimental = GL_TRUE; // 让glew获取全部拓展函数
    GLenum status = glewInit();
    if (status != GLEW_OK)
    {
        std::cout << "Error::GLEW glew version:" << glewGetString(GLEW_VERSION) 
            << " error string:" << glewGetErrorString(status) << std::endl;
        glfwTerminate();
        return -1;
    }

    // 设置视口参数
    glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);

    // 开始游戏主循环
    while (!glfwWindowShouldClose(window))
    {
        glfwPollEvents(); // 处理例如鼠标 键盘等事件

        // 清除颜色缓冲区 重置为指定颜色
        glClearColor(0.18f, 0.04f, 0.14f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // 这里填写场景绘制代码 本节不绘制物体

        glfwSwapBuffers(window); // 交换缓存
    }
    glfwTerminate(); // 释放资源
    return 0;
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
    {
        glfwSetWindowShouldClose(window, GL_TRUE); // 关闭窗口
    }
}

运行程序,效果以下:
这里写图片描述

这里咱们没有绘制任何内容,在下一节将会绘制咱们的第一个图形。

使用模板快速获取本节工程

有网友留言索要整个工程,由于github上面上传二进制的VS工程不太合适,这里制做了一个方便的模板供使用,能够从个人github下载。模板使用方法:

Step1: 将模板getting-started.zip拷贝值VS的项目模板目录,以下图所示:
安装模板

Step2: 使用模板新建工程,以下图:

新建工程

Step3: 将libraries拷贝至新建项目的同级目录下。
拷贝libraries

Step4: 编译运行新建工程便可。

推荐阅读:

[1]: OpenGL wikipedia
[2]: Don’t use old OpenGL
[3]: Learning Modern OpenGL
[4]: NVIDIA opengl

参考资料

[1] Creating a window in http://www.learnopengl.com/
[2] Window and OpenGL context in open.gl