很久没碰OpenGL了,因为之前看过《交互式计算机图形学 基于OpenGL着色器的自顶向下方法(第6版) 》(后面简称《图》),觉得自己已经掌握了OpenGL基础,而且红宝书第8版写得太文绉绉了,又厚,像本工具书,当时也没有急用OpenGL编程,因此也没想看下去。
到了现在,我遇到了某些项目,发觉自己的OpenGL基础还是太贫乏了,用压箱底的知识貌似也无法写一些有用的程序。而且看到现在网上有些有用的程序还是用的OpenGL旧管线,我竟然看不懂。其实本来我是想对比一下新旧管线之间的差异,然后把网上的旧管线程序移植到新管线程序中的,可是苦于当时找不到相同功能下新旧管线的代码,因此就搁置了很久。而且我也不想看旧的红宝书,还那么厚。
以前听说网上有一个OpenGL的入门教程,简单易懂,叫nehe,于是我就搜了一下。但是发现nehe的教程是2000年的古老教程,也用的旧管线,一看,纳闷了,竟然用的Windows窗口编程。我是学过MFC的,知道MFC非常恶心,于是没有看下去的欲望了。
而现在,我随意翻了翻,发现了原来nehe更新了教程
http://nehe.gamedev.net/news/new_opengl_tutorial_site/88001/
http://www.opengl-tutorial.org/download/
而且用的新的管线,好了,那就精彩了,可以把nehe的新旧例子做一个对比,就能知道怎么移植了。然而非常坑爹的是,nehe不知为什么很不喜欢glut,在新旧例子里面,宁愿把代码复杂化也不用glut,我也是呵呵了。
Nehe的旧例子用Windows窗口,新例子用glfw,就是不用glut。明明glut最简洁,明明红宝书新旧版和《图》用的也是glut,业界普遍认同的常用工具啊,为什么就不用呢?我也是醉了。
Nehe的新旧例子代码可以从我这里下载
链接:http://pan.baidu.com/s/1o8ySMng密码:hrcy
好了,现在可以对比一下OpenGL的新旧管线究竟有什么不同(分别与glut新管线编程对比)。
Nehe的旧版例子使用Windows窗口,自创了三个函数ReSizeGLScene,InitGL,DrawGLScene。还是比较符合OpenGL的编程规范的(无论是新旧管线都有这三个老面孔函数)。
ReSizeGLScene会在处理函数WndProc中调用。
InitGL会在CreatGLWindow窗口中调用
DrawGLScene会在WinMain最后的自创死循环中调用
Windows窗口能使用OpenGL在于在CreateWindowEx函数中设置了PFD_SUPPORT_OPENGL
而新版的glut例子就非常简洁
ReSizeGLScene通常对应的是自创函数reshape,有现成的回调函数注册glutReshapeFunc《图》P73
InitGL通常对应的是自创函数init
DrawGLScene通常对应的是自创函数display,有现成的回调函数注册glutDisplayFunc《图》P58
在InitGL中,glClearColor《图》P51,glEnable(GL_DEPTH_TEST)在新版本的init都是有保留的,其余都会通过手动编写模视矩阵和着色器代码来替代。
新版本的init的处理流程都是先准备好顶点、颜色、纹理等数据,创建GPU缓存,然后把数据管线装配到GPU缓存中。
在DrawGLScene中,glClear《图》P60在新版本的display中是有保留的。在旧管线中,比较有标志性的是函数glLoadIdentity,表示模视矩阵重置为单位矩阵,这表示摄像机坐标系与世界坐标系重合。在OpenGL中,摄像机坐标系的Z轴方向是屏幕朝外的,因此我们将会看到重置后是一个XOY坐标系。而在新版本的的display中,模视矩阵是要自己编写的,因此glLoadIdentity也不复存在了。
在DrawGLScene中,比较多见的还有
glTranslatef(…);
glRotatef(…);
glBegin(…);
glColor3f(…);
glVertex3f(…);
…
glEnd();
操作大概是,先移动和旋转屏幕,然后在屏幕坐标系绘制图形。
第一步其实修改了视图矩阵,第二步定义了对象在摄像机坐标系下的坐标并绘图。
其实就是创建了一个模视矩阵,顶点、颜色数据,并绘图。在新版的display中,通常是自己编写模视矩阵,然后调用glDrawArrays《图》P41绘图。顶点,颜色数据在init中就已经准备好并发送到GPU了。
在旧的管线编程中,还会用
glMatrixMode(GL_PROJECTION);
gluPespective(…);
进入投影矩阵设置模式并具体设置投影矩阵
glMatrixMode(GL_MODELVIEW)
LookAt(…);
进入模视矩阵设置模式并具体设置模视矩阵
这些函数都是OpenGL旧管线自动提供的。而新版OpenGL管线的投影矩阵和模式矩阵都是自己编写,并调用glUniformMatrix4fv函数发送到着色器的自创矩阵变量中,因为都是纯手动,所以就不用进入具体的矩阵设置模式了。这些操作通常会在display函数中进行。
下面对比一下glut和glfw
我也不知道nehe新版本为什么要用glfw,感觉还是比glut麻烦多了。而且nehe也没有对流程进行函数分块。
然而一个意外的收获是,nehe让我知道了glm这个工具,这个工具可以自动创建模视矩阵和投影矩阵(就像固定管线一样),就方便多了,不用自己写矩阵了。
Glut在main函数中会调用glutInitDisplayMode设置显示模式《图》P56,但glfw没有。
Glut的glutInit对应glfw的glfwInit
Glut的glutInitWindowSize和CreateWindow对应glfw的glfwCreateWindow
Glut的glutInitContextVersion(3,3)对应glfw的
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,3);
glut的glutInitContextProfile(GLUT_CORE_PROFILE )对应glfw的
glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);
然后都调用glewInit
然后就是初始化流程,和进行循环绘制了。
Glut的循环绘制调用glutMainLoop就可以了,glfw还要自己手动创建循环。比较费解的是,nehe还要在循环中调用glUseProgram和启用顶点数组对象并发送数据(这个通常在init中进行的),绘制完还要取消顶点数组对象。
最后手动回收缓存、顶点数组对象和程序ID,这个在glut都不用做。
总结
不过总的来说,有nehe这些例子还是难能可贵的,例子的简单易懂是它最重要的价值。有某些功能我都不知道在OpenGL怎么实现,我也不想查红宝书(不知道在哪一页查),那么看nehe怎么实现那就快速得多了,遇到不懂的函数就查红宝书就行了。
下面讲一下纹理
在新版OpenGL管线中,流程是
glGenTexture 创建纹理对象
glBindTexture 绑定纹理对象
glTexImage2D 和纹理图像(颜色矩阵)数据连接
glTexParameterf 设置纹理参数
glActiveTexture(GL_TEXTURE0) 设置纹理对应采样器为0号采样器
glBindTexture 绑定纹理对象
创建顶点坐标数据和顶点对应的纹理坐标数据
在片元着色器中创建采样器
使用glUniform1i把0号采样器赋给片元着色器中的采样器
片元着色器中调用texture2D(texture,texCoord),使采样器获得纹理坐标下纹理的颜色
在nehe中,有自创的loadDDS函数加载DDS图片,该函数把图片转换成unsignedchar* 缓存数组buffer,并调用glCompressedTexImage2D,使纹理对象和压缩纹理图像数据连接
并调用glGenTexture 和glBindTexture
返回纹理对象
然后创建顶点坐标数据和顶点对应的纹理坐标数据
在片元着色器中创建采样器
glActiveTexture(GL_TEXTURE0)设置纹理对应采样器为0号采样器
glBindTexture 绑定纹理对象
glUniform1i把0号采样器赋给片元着色器中的采样器
片元着色器中调用texture2D(texture,texCoord),使采样器获得纹理坐标下纹理的颜色
texture(myTextureSampler, UV ).rgb使采样器获得纹理坐标下纹理的颜色
在旧版OpenGL管线中,流程是
LoadBMP把图片转换成AUX_RGBImageRec类型的数组
glGenTexture 创建纹理对象
glBindTexture 绑定纹理对象
glTexImage2D 和纹理图像(颜色矩阵)数据连接
glTexParameteri绑定纹理对象
glEnable(GL_TEXTURE_2D)开启纹理映射
在绘制循环中
glBegin(…);
glTexCoord2f(…);
glVertex3f(…);
…
glEnd();
创建纹理坐标和对应的顶点坐标
最后推荐一下OpenGL可编程管线的拾取程序,因为我在红宝书找不到例子
http://www.lighthouse3d.com/tutorials/opengl-selection-tutorial/
我作了一下修改(按http://blog.csdn.net/outtt/article/details/51277481的配置可运行)
链接:http://pan.baidu.com/s/1nvE3Xw9密码:s6w2
现在更新一下网上流传的OpenGL中文教程网站,看起来比红宝书和蓝宝书好
opengl-tutorial中文教程,也就是nehe的新教程,用的glfw+glew
http://www.opengl-tutorial.org/cn/beginners-tutorials/
learnopengl中文教程,用的glfw+glad
https://learnopengl-cn.github.io/
不过这两个教程其实我都不喜欢,还是喜欢
《交互式计算机图形学 基于OpenGL着色器的自顶向下方法 (第六版)》
用的最传统的glut+glew,深得我心。
不过初学者的话,如果追求最新最酷炫的学习体验的话,还是看网上的教程算了,如果是学术型的,想再理论性地学习一下计算机图形学的话,那么这本书也是非常好的学术性基础书。