写在前面
若是你正在阅读本文,那么你和我同样可能喜欢图形学或者游戏编程,想尝试编写本身的图形App :)。图形学的初学者每每很是着急,但愿能当即编写出丰富生动、逼真又富有交互性的3D应用,至少我是这样。css
笔者翻看了图形学教材,红宝书,3D数学基础等大部头书后,感受到根据教材或者网络tutorial编写简单的应用程序并不困难,难点在于可以保持足够热情,坚持学习,使本身对图形学原理有更深更完整理解,培养自主学习更高级话题的能力和解决实际问题的能力。
贪多嚼不烂,若是过于急躁,学习教材或者网络tutorial就会感受要么太简单,要么太难,难以锲而不舍。所以我写下这一系列博客,目的在于保持本身的热情,保持本身的节凑,以OpenGL编程为切入点,扎实学习图形学原理,从而加强图形编程能力。html
在学习OpenGL编程的过程当中,当前感触比较深的是:宁愿暂时跳过一些复杂的数学背景知识,也不要所以损害到热情,兴趣很是重要。所以和我同样对图形学有热情的同路人,若是遇到难点必定不要轻易放弃,若是一次不能彻底理解,能够标记下来,回过头来再作研究。
本系列将使用 windows & visual studio 2013 & C++ 做为开发平台,笔者建议初学者也是如此,尽可能不要把精力耗费在linux下库文件编译和显卡程序安装,编程语言细节(例如C++11新特性,java语言绑定版本)等琐碎事情上,更多地关注图形原理和实现。java
话不絮烦,下面开始本节内容。linux
经过本节,能够了解到:ios
OpenGL是一个跨语言和跨平台的图形编程接口,这个API用来与GPU交互,实现基于硬件加速的图形渲染。OpenGL其实是一个抽象的图形绘制规范,尽管能够由软件来彻底实现,但它被设计为大部分或者所有由硬件来实现。OpenGL是一个绘图规范,不一样的显卡厂商负责具体实现,所以实现的具体程序也不尽相同。OpenGL是跨平台的,支持PC端,嵌入式设备,手机设备的渲染;同时也是跨语言的,它底层使用C语言实现,支持java语言、Python、javaScript绑定。
OpenGL包括旧的版本和现代版本 旧版本的绘图功能大多数在GPU内完成,某些功能没法实现,现代版本使用着色器程序提供了更大的灵活性。旧式版本是指1.x和2.x版本,现代版本是指3.x以上版本。二者函数的一个区别以下:git
当3.2规范发布后,OpenGL支持两种profile:github
一个值得注意的问题是,为了保持跨平台,OpenGL规范中并未涉及到OpenGL context的建立和管理部分,这部分的实现依赖于不一样系统的窗口管理系统。所以在编写OpenGL程序时须要借助第三方库完成与窗口系统的交互,建立绘制上下文对象(render context)。web
上面已经提到OpenGL的具体实现由显卡驱动厂商负责,那么不一样的厂商实现的库可能有所不一样,有的函数可能没有实现,有的包含拓展的实现,并且厂商的实现通常经过动态连接库来提供。通常地使用一个函数前来查询函数是否实现了,并且要关心版本问题,这些细节比较繁琐,能够交给第三方库GLEW来实现。
同时与操做系统的窗口系统交互,建立绘制上下文对象,也是与具体操做系统相关的,能够交给第三方库GLFW来实现(固然还有其余选择,包括 freeglut,SDL,GLFW目前是比较推荐的)。
总结为咱们须要下载和编译两个第三方库:编程
下载并编译后,获取到两个库的lib和dll文件,咱们就能够开始编写第一个程序了。编译源文件的过程,能够选择对应的visual studio版本,而后编译为默认的调试版本便可。若是以为下载和编译源码费劲,我也为你提供了Windows下编译好的库文件和必要的头文件,点击从github下载库文件。windows
利用上面编译的两个第三方库,咱们能够实现本身的第一个程序,建立用于渲染图形的上下文对象。
通常而言,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库(想了解更多请参考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拷贝至新建项目的同级目录下。
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