OpenGL一直是事实上的计算机图形学标准,截止2016年06月,OpenGL版本已经更新到4.5。不过DirectX发展迅速,大有OpenGL落后的态势,知乎上的该话题的讨论:https://www.zhihu.com/question/23241456。所以OpenGL为了追赶DirectX,推出 了glNext(Vulkan API),相关讨论https://www.zhihu.com/question/28039310。
图形学的经典书籍是《Computer Graphics with OpenGL》,即《计算机图形学》,以OpenGL描述,在介绍图形学基础理论的同时,也提供了OpengGL的相关API和实例程序。目前(2016年6月)本书已经更新到第4版。网上第3版的PDF文档比较多,尚未发现第4版的PDF文档,不过第3版和第4版差别很小,第4版重要的更新是提供了GLSL的一个章节。
《计算机图形学》的重要性在于全面的理论知识,不过它基本不涉及Shader编程,仍然沿用固定管程的教学内容,然而当前的OpengGL都是Shader编程,读完本书你会发现对Shader了解很少。
下面三本书都是Shader编程。
《OpenGL超级宝典》和《OpenGL编程指南》是两本实用的OpengGL学习书籍,区别在于前者有更多实例,更容易按步骤进行操作,而后者着重介绍API,最好先阅读前者。
《OpenGL 4.0 Shading Language Cookbook.pdf》主要介绍Shader编程。
《OpenGL 4.0 Shading Language Cookbook.pdf》下载地址:http://download.csdn.net/detail/brillianteagle/9541959
《计算机图形学》详细介绍了3个类库,总结起来就是
glu库:函数glu开头,设置视景和投影矩阵、用线和多边形近似法来描述对象、用线性近似法描述quadric和B-spline,处理表面绘制操作。
glut库:跨平台套件,包括了窗口操作、输入管理。
注意,包括GLUT头文件就不必包括GL和GLU头文件。
配置好这三个库就可以运行《计算机图形学》中所有的实例,但是不能进行Shader编程。
GLU库获得是比较陈旧的OpenGL API版本,可以从glew获得最新的OpenGL API版本。freeglut则是用来取代glut库的。
所以,配置好glew和freeglut两个库,完全取代glu和glut库,同时可以获得最新版本的API,可以进行Shader编程。
GLTools是《OpenGL超级宝典》作者提供的工具包,在运行本书的实例时要用到。
GLM (OpenGL Mathematics) 也是一个工具包,在运行《OpenGL 4.0 Shading Language Cookbook.pdf》的程序时会用到。
一般配置都有两种方式,要么相关文件放到VC目录下,这样不必每次都配置;要么相关文件放到工程目录下,并包含头文件目录和lib文件目录,这样程序在在其他电脑上也可以打开。各有好处。这里介绍第二种方式。
glew官网:http://glew.sourceforge.net/
glew官方GitHub主页:https://github.com/nigels-com/glew
freeglut官网:https://sourceforge.net/projects/freeglut/
下载glew的最新Binaries版本(截止2016年6月9日的)为glew-1.13.0-win32.zip,freeglut最新版本为3.0版本freeglut-MSVC-3.0.0-2.mp.zip。
在VS2015中新建VC控制台程序OpenGL-Demo,在【工程目录】下新建GL文件夹和lib文件夹,然后执行下面四个步骤:
(1) 将glew-1.13.0-win32\glew-1.13.0\include\GL目录下的头文件和freeglut-MSVC-3.0.0-2.mp\freeglut\include\GL目录下的头文件都放到【工程目录】下新建GL文件夹中;
(2) 将glew-1.13.0-win32\glew-1.13.0\lib\Release\Win32目录下的glew32.lib文件和freeglut-MSVC-3.0.0-2.mp\freeglut\lib目录下的freeglut.lib放到【工程目录】下新建lib文件夹中,这里要注意,我使用的是32位版本的lib包,即便我的电脑是64位的。因为我们建立的VC工程默认是32位的。
(3) 将glew-1.13.0-win32\glew-1.13.0\bin\Release\Win32目录下的glew32.dll和freeglut-MSVC-3.0.0-2.mp\freeglut\bin目录下的freeglut.dll放到工程目录下。
(4) 在项目上右键菜单中打开配置,设置附加头文件目录和附加lib文件目录,如图。注意,$(ProjectDir)就是当前目录的意思。
可以用以下代码测试一下,绘制一个白色背景的窗口:
// OpenGL-Demo.cpp : Defines the entry point for the console application.
//
#pragma comment(lib,"glew32.lib")
#include
#include
#include
using namespace std;
/* GLUT callback Handlers */
static void resize(GLint width, GLint height) {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, (GLdouble)width, 0.0, (GLdouble)height);
glClear(GL_COLOR_BUFFER_BIT);
}
/**绘制 函数,这里是空的*/
static void display() {
}
/**更换屏幕颜色设定矩阵模式
选择正摄投影的范围*/
static void init() {
glClearColor(1.0, 1.0, 1.0, 1.0);
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0.0, 200.0, 0.0, 150.0);//窗口的左下角是原点
}
/* Program entry point */
int main(int argc, char *argv[]) {
glutInit(&argc, argv);
glutInitWindowSize(640, 480);
glutInitWindowPosition(100, 100);//相对屏幕的左上角
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);//正在使用单个帧缓存,颜色模式//为RGB,这是默认的颜色模式
glutCreateWindow("Draw Curves");
init();
if (glewInit() == GLEW_OK) {
cout << "glew初始化成功!" << endl;
}
glutDisplayFunc(display);
glutReshapeFunc(resize);
glutMainLoop();
}
这个库由《OpenGL超级宝典》的作者提供,但是需要自己进行编译使用。这里我将本书源码进行了上传。
part1下载地址:http://download.csdn.net/detail/brillianteagle/9545333
part2下载地址:http://download.csdn.net/detail/brillianteagle/9545335
只有两个都下载后才能解压。解压后SB5\Src目录下就有GLTools文件夹。该文件夹的目录下有include和src两个文件夹。由于GLTools的编译依赖于glew,可以看到SB5\Src\GLTools\include\GL目录下就是某个版本的glew头文件,而SB5\Src\GLTools\src目录下有glew.c源文件。
由于我们已经采用了glew的1.13版本,所以这里要替换SB5\Src\GLTools\include\GL目录下的所有头文件,并采用1.13版本glew.c源文件。在glew的官网http://glew.sourceforge.net/下载Source版本,替换SB5\Src\GLTools\src目录下有glew.c源文件。
在VS2015中新建VC静态库程序GLTools,然后按下面步骤配置工程:
(1)将 SB5\Src\GLTools的include文件夹放到【工程目录】下,将SB5\Src\GLTools\src目录下的源文件放到【工程目录】下;
(2)将SB5\Src\GLTools\src目录下的所有源文件放到【工程目录】下;
(3)在工程上点击右键菜单中的属性,添加头文件。
然后,就可以在relseas模式下编译该工程,结束后GLTools\Release目录下生成GLTools.lib。
如何把GLTools集成到自己的工程呢?
在之前的OpenGL-Demo【项目目录】下新建GLTools文件夹。
(1) 将更新过glew最新版本的SB5\Src\GLTools的include文件夹下的所有文件拷贝到GLTools文件夹,并删除【项目目录】的GL文件夹,因为此时GL文件夹已经存在于GLTools目录下。
(2) 将GLTools.lib添加到lib文件夹。
(3) 更改附加头文件目录 ( P r o j e c t D i r ) G L 到 (ProjectDir)GL到 (ProjectDir)GL到(ProjectDir) GLTools
值得注意的是,GLTools依赖于glew,所有,在添加GLTools到自己的工程时要注意依赖关系。这样,引入GLTools,也就引入了glew。
测试一下《OpengGL超级宝典(第5版)》的第一个例子,蓝色背景下绘制红色三角形。
// OpenGL-Demo.cpp : Defines the entry point for the console application.
//2016-06-09
//#pragma comment(lib,"glew32.lib")//不用
#pragma comment(lib,"GLTools.lib")
//#include //不用
#include
#include
#include
GLBatch triangleBatch;
GLShaderManager shaderManager;
///////////////////////////////////////////////////////////////////////////////
// Window has changed size, or has just been created. In either case, we need
// to use the window dimensions to set the viewport and the projection matrix.
void ChangeSize(int w, int h)
{
glViewport(0, 0, w, h);
}
///////////////////////////////////////////////////////////////////////////////
// This function does any needed initialization on the rendering context.
// This is the first opportunity to do any OpenGL related tasks.
void SetupRC()
{
// Blue background
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
shaderManager.InitializeStockShaders();
// Load up a triangle
GLfloat vVerts[] = { -0.5f, 0.0f, 0.0f,
0.5f, 0.0f, 0.0f,
0.0f, 0.5f, 0.0f };
triangleBatch.Begin(GL_TRIANGLES, 3);
triangleBatch.CopyVertexData3f(vVerts);
triangleBatch.End();
}
///////////////////////////////////////////////////////////////////////////////
// Called to draw scene
void RenderScene(void)
{
// Clear the window with current clearing color
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
triangleBatch.Draw();
// Perform the buffer swap to display back buffer
glutSwapBuffers();
}
///////////////////////////////////////////////////////////////////////////////
// Main entry point for GLUT based programs
int main(int argc, char* argv[])
{
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
glutInitWindowSize(800, 600);
glutCreateWindow("Triangle");
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
return 0;
}
GLM只有头文件,只需要在官网下载后解压,glm-master\glm-master目录下的glm放到OpenGL-Demo【工程目录】下,并添加$(ProjectDir)glm即可。
glm官网:https://sourceforge.net/projects/ogl-math/
下面是《OpenGL 4.0 Shading Language Cookbook.pdf》中第一个例子,也是绘制三角形。
#pragma comment(lib,"glew32.lib")//这里要加载该静态包
#include
#include
#include //斜杠和反斜杠都可以
#include
/*
完整的shader过程。
初始化→编译→链接到程序
*/
using namespace std;
/*传入shader的引用,和GLSL字符串,对shader进行编译*/
void compileShader(GLint& shader, const GLchar* shaderCode) {
const GLchar* codeArray[] = { shaderCode };
glShaderSource(shader, 1, codeArray, NULL);
glCompileShader(shader);//编译shader
/*判断编译后的结果*/
GLint result;
glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE) {
cout << "shader编译错误!" << endl;
GLint logLen;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLen);
if (logLen>0) {
char* log = (char*)malloc(logLen);
GLsizei written;
glGetShaderInfoLog(shader, logLen, &written, log);
cout << log << endl;
free(log);
}
exit(1);
}
else
{
cout << "shader编译成功!" << endl;
}
}
void render(GLuint programHandle) {
float positionData[] = {
-0.8f, -0.8f, 0.0f,
0.8f, -0.8f, 0.0f,
0.0f, 0.8f, 0.0f
};
float colorData[] = {
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f
};
GLuint vboHandle[2];
glGenBuffers(2, vboHandle);
GLuint positionbufferHandle = vboHandle[0];
GLuint colorBufferHandle = vboHandle[1];
glBindBuffer(GL_ARRAY_BUFFER, positionbufferHandle);
glBufferData(GL_ARRAY_BUFFER, 9 * sizeof(float), positionData, GL_STATIC_DRAW);//绑定顶点数据
glBindBuffer(GL_ARRAY_BUFFER, colorBufferHandle);
glBufferData(GL_ARRAY_BUFFER, 9 * sizeof(float), colorData, GL_STATIC_DRAW);//绑定颜色数据
GLuint vaoHandle;
glGenVertexArrays(1, &vaoHandle);
glBindVertexArray(vaoHandle);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, positionbufferHandle);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLubyte*)NULL);
glBindBuffer(GL_ARRAY_BUFFER, colorBufferHandle);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (GLubyte*)NULL);
glBindVertexArray(vaoHandle);
// 绘制三角形
glDrawArrays(GL_TRIANGLES, 0, 3);
glFlush();
}
void link() {
//判断glew是否能初始化
if (glewInit() != GLEW_OK) {
cout << "glewInit()失败!" << endl;
exit(1);
}
/*编译vertexShader*/
const GLchar* shaderCode = "#version 400 \n in vec3 VertexPosition;in vec3 VertexColor;out vec3 Color;void main() {Color = VertexColor;gl_Position = vec4(VertexPosition, 1.0);}";
GLint vertexShader = glCreateShader(GL_VERTEX_SHADER);
if (vertexShader == 0) {
cout << "vertexShader无法获取!" << endl;
exit(1);
}
compileShader(vertexShader, shaderCode);
/*编译fragmentShader*/
const GLchar* fragmentCode = "#version 400 \n in vec3 Color;out vec4 FragColor;void main() {FragColor=vec4(Color,1.0);}";
GLint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
if (fragmentShader == 0) {
cout << "fragmentShader无法获取!" << endl;
exit(1);
}
compileShader(fragmentShader, fragmentCode);
/*获得程序programHandle*/
GLuint programHandle = glCreateProgram();
if (programHandle == 0)
{
cout << "Program无法获取!" << endl;
exit(1);
}
/*将shader链接到programHandle*/
glAttachShader(programHandle, vertexShader);
glAttachShader(programHandle, fragmentShader);
glBindAttribLocation(programHandle, 0, "VertexPosition");
glBindAttribLocation(programHandle, 1, "VertexColor");
glLinkProgram(programHandle);
/*判断链接状态*/
GLint status;
glGetProgramiv(programHandle, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
cout << "link失败!" << endl;
GLint logLen;
glGetProgramiv(programHandle, GL_INFO_LOG_LENGTH, &logLen);
if (logLen>0) {
char* log = (char*)malloc(logLen);
GLsizei written;
glGetProgramInfoLog(programHandle, logLen, &written, log);
cout << log << endl;
free(log);
}
exit(1);
}
else {
cout << "link成功!" << endl;
glUseProgram(programHandle);//使用该程序
render(programHandle);
}
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);;
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowPosition(0, 0);
glutInitWindowSize(900, 600);
glutCreateWindow("First GLUT Sample");
/*link函数是GLSL编写的主体*/
glutDisplayFunc(link);
glutMainLoop();
return 0;
}
如果觉得写得不错,可以扫描我的微信二维码请我喝咖啡哦~
或者点击 打赏地址 请我喝杯茶~