【转】我的opengl编程学习(一)(简介、绘制图像、三维观

来源:互联网 发布:启动oracle服务 linux 编辑:程序博客网 时间:2024/05/12 13:31

这是我第二次学习OPENGL,第一次学习是在大二的计算机图形学课堂上,那是对opengl只是走马观花,现在过了两年,我打算把opengl进行新一编完整而系统的学习,有三个目的:1.熟练掌握opengl编程,2从opegl的体系中加深对计算机图形渲染管线的整个体系的了解,作为深入学习GPU编程的进阶,3.制作好看的CG作品。

这里是我在学习《opengl programming guide fifth edition》过程的从头到尾的整个的学习笔记,放在这里,给自己做个以后的参考,也想与大家交流。有很多错别字,请原谅~~

最前面:OpenGL是一个开放的2D/3D图形绘制的工业标准,正因为只是一个标准,所以OPENGL的管理者并没有一个所谓的SDK,所有开发者可以使用的SDK都是Khronos Group(OpenGl标准的管理者)的成员应用这个标准自己编写的(它的成员可以得到这个标准),例如常见在Microsoft的Visual Studio下的opengl是由微软根据OpenGl1.1而自己编写的。
Opengl的更高版本的更多特性都叫做,OPENGL扩展,不同的显卡支持这些扩展中的不同扩展函数,所以使用更高级的特性时你必须拥有这些扩展库,并且你的显卡支持你用的特性。

GLEW就是一个非常好的扩展库,它包括了最近的OPENGL核心库,和大量的扩展库,并且可以根据硬件自动识别哪些扩展可用。并且NVIDIA会有最新的OPENGL_SDK_Guide,这个guide其实就是以各种DEMO的形式来向开发者展示他们的显卡都支持到的最新的核心库和哪些扩展库,他们的DEMO基本上也是用GLEW来写的。

NVIDIA为了支持OPENGL的更高版本和新的扩展也会经常发布新的硬件驱动,来支持OPENGL的发展,可随时关注NVIDIA的开发者网站。
0. 配置开发环境:
在VS2005下一般选用WIN32控制台程序,然后还要加入<window.h>
1. glut程序的一般组成:
glutInit
glutInitDisplayMode
glutInitWindowPosition
glutInitWindowSize
glutCreateWindow
直到调用glutMainLoop()窗口才真的会显现
每当窗口内容需要重绘时都会调用glutDisplayFunc(void (*func)(void)),当你再改变某些内容想强制重绘时可调用glutPostRedisplay(void)发送一个信号
2 glut的四个时间响应回掉函数
glutReshapeFunc(void (*func)(int w, int h))
glutKeyboardFunc(void (*func)(unsigned char key, int x, int y))
glutMouseFunc(void (*func)(int button, int state, int x, int y))
glutMotionFunc(void (*func)(int x, int y))
3.双缓存,一个显示,另一个准备下一帧的绘制,当下一帧绘制好来,切换到另一个缓存,如此交替,可以时人看不到绘制当过程,只有绘制好当结果才会被看到,就不会出现黑的屏幕(闪)。
在写运动的程序中,除来初始化时用glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB)外,在绘制函数的最后一般要加上glutSwapBuffers(),因为打开双缓存后,程序不会自动交换要显示的那个缓存,而是一直显示一个缓存的内容,这样当一帧结束后这一缓存会清屏并进行重绘,而这以过程也被显示出来,通常回看到程序像卡调,因为大部分时间这一个缓存在进行绘制运算,真正显现出当绘制的结果只是一瞬,所以要调用glutSwapBuffers()马上显示另一个缓存已完成的结果。
4.opengl绘制的三个最基本操作:clearing the window, drawing a geometric object, drawing a raster object(指一些二维对象)
clearing the window:为什么要先CLEAR,因为缓存中的数据通常是上次保留的,要清掉
图形硬件有好多个缓存,glClear后面括号中指的是要清理的是那个缓存,有Color buffer Depth buffer Accumulation buffer Stencil buffer四种。
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glClear(GL_COLOR_BUFFER_BIT);
glClear(GL_DEPTH_BUFFER_BIT)要快
绘制的基本原语:点 线 三角形 四边形 多边形 ,此外还有一个特殊点glRect函数绘制矩形 (在先进点GPU上,用一个向量做参数来制定一个点比用四个位置做参数性能会更好)
 
画虚线: 先glLineStipple(1, 0x3F07)设置类型,再glEnable(GL_LINE_STIPPLE)打开虚线开关,这样以后调用等画线语句都画虚线.
多边形;
glPolygonMode(GLenum face, GLenum mode)用设置多边形的外面和里面的填充样式和线性
  多边行的内外,默认时按顶点逆时针方向的那个面为外面(可见的),另外一个为里,可以用glFrontFace(GLenum mode)来定义哪种时针方向的为外面,通常多边形组成空间体时,里面的里.
  glCullFace可以用来删除外面或侧面,比如永远不可能看到侧面的情况,此处需要打开删除开关.
  Stippling Polygons,可以用一个位图数据填充的方式来绘制多边形,只要先glEnable (GL_POLYGON_STIPPLE)打开开关,再glPolygonStipple ()载入这个位图数据,然后绘制出的多边形就都是用该位图填充的.
Add:
Glflush()保证前面所有的绘制命令强制被执行,并且是在优先时间内,因为有事由于一些优化等原因,绘制代码并不会马上就执行绘制,调用此函数保证马上进行绘制
Glfinish ()也是强行绘制,但是与glflush不同是它被调用后进程被暂时阻塞直到绘制完成,比如一些用户交互,当绘制完成后,你的键盘鼠标输入才会正常响应.
绘制多边形;只能绘制简单的凸包多边形


1glEdgeFlag(GLboolean flag);来指定后面的点的一个性质,如果为假,则这个点不会引导绘制出一条线,默认为真,这个函数为了绘制一些不需要内部线条的形状或不是凸包的多边形
2设置一个点的发向量的方法:glNormal3fv(n0); glVertex3fv(v0);
glEnable(GL_NORMALIZE)为打开自动归一化发向量的开关(使成为单位向量)通常用在一些不常用的几何操作(如乘以某个矩阵)时.因为默认OPENGL在计算时上是不会自动单位化这个法向量的.
4.数组数据,
glArrayElemernt
当绘制图形时使用大量的顶点\发向量\颜色等时,可定义数组数据减少代码,
static GLint vertices[] = {25, 25,
  100, 325,
  175, 25,
  175, 325,
  250, 25,
  325, 325};
glEnableClientState (GL_VERTEX_ARRAY);打开顶点数组开关
glVertexPointer (2, GL_INT, 4*sizeof(GLint), vertices);//指定结构,表2个元素为一组,并选用其前4个;
glInterleavedArrays (GL_C3F_V3F, 0, intertwined);可以同时存储多种数据的数组(如顶点和发向,要在数组中先定义顶点再定义发向)

然后用
glBegin(GL_TRIANGLES);
glArrayElement (2)来访问其中的第三个元素
glend()
而且可以同时定义发向量等其他数组,可以在调用上句时同时调用
glDrawElements
用来进行一组绘制
static GLubyte allIndices = {4, 5, 6, 7, 1, 2, 6, 5,
 0, 1, 5, 4, 0, 3, 2, 1,
 0, 4, 7, 3, 2, 3, 7, 6};

glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, allIndices);用24个点绘制正方形(也就是绘制6个)
这个函数不能用在glbegin和glend之间

glDrawArrays(GLenum mode, GLint first, GLsizei count);
用first到first+count-1之间的数做为值来绘制MODE(一个几何体)

5.状态管理和查询,大多数状态默认下是关闭的,以便达到根高的渲染速度,通过void glEnable(GLenum cap)和void glDisable(GLenum cap)来开或闭,
查询当前状态可以用glIsEnabled(GLenum capability)询问某个状态的关闭,或者更强大的void glGetBooleanv(GLenum pname, GLboolean *params);
void glGetIntegerv(GLenum pname, GLint *params);
void glGetFloatv(GLenum pname, GLfloat *params);
void glGetDoublev(GLenum pname, GLdouble *params);
void glGetPointerv(GLenum pname, GLvoid **params);这些可以查询基本所有的数据(如当前的颜色等);
6.变换矩阵
viewing and modeling transformations-从物体坐标系转换到视域坐标系(比如物体的一个顶点p在它的自身坐标系下坐标为(1,1,1),经过平移的MODEL矩阵后变为(1,2,1),而人是在原点向斜前方四五度观察度,所以再乘以view矩阵变换到人的视域坐标系后,改点再以人眼为原点,观察方向为z负方向的坐标系中就又不是(1,2,1)了,而变掉了,
projection matrix-从你的视域坐标系中,根据已定义的视锥体剪除调再视锥体外的部分,并且,将XYZ除以W以归一化,还要确定投影的形式,是透视变换还是正交变换
viewport transformation-将三维的位置信息变换到二维屏幕的位置

glLoadIdentity()再OPENGL中,所有的转换都是将转换矩阵乘以当前矩阵,然后吧结果再作伪当前矩阵,所以再做一些变换前可能要调用此函数来清空当前矩阵
可以与他相乘的有gllookat glscale(rotate..)<modelview matrix>
  glFrustum<projection matrix>等
glLoadMatrix*()将load一个你指定的矩阵作伪当前矩阵
opengl中矩阵的存储形式为 ,可以定义一个16元素的叔祖来代表这个矩阵;

透视投影用来生成真是世界,正交投影用来生成需要严格考虑真是尺寸等情形

Opengl中同时存在一个MODELVIEW矩阵和一个PROJECTION矩阵,当你希望改变的是PROJECTION矩阵时,要用glMatrixMode (GL_PROJECTION)来切换,繁殖亦然,这样切换后,当你再调用glFrustum等就是再PROJECTION矩阵上相乘,而不改变MODELVIEW矩阵.
一般MODELVIEW等变换都写在绘制函数中,VIEW变换语句永远要写在MODEL变换前面,而PROJECTION和VIEWPORT变换写在RESHAPE函数中(因为投影和viewport要随着窗口尺寸而变)
变换矩阵的相乘顺序,矩阵的出现顺序于实际变换顺序时相反的,如先ROTATION再TRANSLATION的代码应该是
glLoadIdentity();
glMultMatrixf(T); /* translation */
glMultMatrixf(R); /* rotation */
draw_the_object()
glscale的参数如果是负值,则表示朝向相反方向,如果想做关于Xz平面的镜像,只要GLSCALE(1.-1.1),GLSCALE会降低性能
两个透视投影函数gluPerspective glfrustum
Viewport的宽高比要和projection的宽高闭相同图像才不会歪曲
Glpushmatrix() glpopmatrix(),把转换的代码写在中间可以在结束后将矩阵再重置为push之前,但是OPENGL的矩阵堆栈是又限制的,可以用glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, GLint *params).查询当前还可以放多少矩阵
7自定义剪裁平面,除了用GLPERSPECTIVE剪裁四个边界外,还可已自定义任意平面对可见视锥体进行剪裁,可以定义多大6个平面,用glClipPlane定义剪裁面并进行剪裁,定以后要用glEnable打开开关才行
8.反转矩阵变换,即从屏幕对二维信息转换到物体对建模时对三维空间,等于将整个渲染管线反转,用gluUnProject(GLdouble winx, GLdouble winy, GLdouble winz, const GLdouble modelMatrix[16], const GLdouble projMatrix[16], const GLint viewport[4], GLdouble *objx, GLdouble *objy, GLdouble *objz)函数
其中WINZ是指深度缓存,默认为0-1,从0-1的深度缓存将对应出物体建模空间的一条线出来,其中还要制定渲染管线过程中的各个矩阵
gluProject()则是它的相反,模仿正常的几何渲染管线得到三维对应二维的数据
9.颜色缓存由比特平面构成,每个比特平面为像素提供以为存储,由几个平面就有几位存储
dithering(递色),用合理的棋盘格叠加创造更多硬件不支持的颜色,通常在比特面少的情况下,另外在使用双缓存时,由于比特平面减少了一半,有时也可能会使用,用glEnable(GL_DITHER)开启

指定颜色时用glcolor3ub即可使用uchar类型(0-255)

用颜色索引表模式时设置清屏颜色用glClearIndex(),设置颜色用glIndex()。用glutSetColor()设置颜色表

用glShadeModel设置颜色的渲染模型,只有GL_SMOOTH和GL_FLAT. smooth为默认
10.opengl中使用光照的步骤:
为每一个定点定义其法向量(其中glutSolidSphere()等这样等函数已经定义好了默认的法向量)
i创建光源(用glLight()来设定光源的各种属性,位置/属性/参与打各种类型光的颜色,默认下环境光为0.0.0即不为环境的光做贡献,第一个光源的反射光都为1.1.1,白光,其他的都无光)
ii创建光照模型(默认情况下认为人和物体无限远,这样就不用考虑人和物体的连线和光刀物体的联线的夹角,默认还认为只计算物体外面的光照)
定义关照模型的函数为glLightModel,它有三种参数,为
GL_LIGHT_MODEL_AMBIENT,它用来定义一个基本的环境光,这个环境光时不是由任何实际光源所贡献的环境光
GL_LIGHT_MODEL_LOCAL_VIEWER(GL_TURE OR FALE),用来定义视点时否离物体无限远,也就是说如果为真,那么光源在每个顶点处不用考虑角度的因素,各个点是一样的,如果为错则更真实一点,默认为无限远
GL_LIGHT_MODEL_TWO_SIDE定义是否双面光照
iii定义物体的表面光照材质(用glMaterialfv()来定义各种反射的属性)
glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission)可以定义一个模拟的光源
注意,设置glMaterialfv后这个属性回一直影响到后面所有绘制物体的状态
一种简化设置光照材质属性的方法,用glColorMaterial()和GLCOLOR联合,当定义了
glColorMaterial()后,并打开glEnable(GL_COLOR_MATERIAL)开关,则以后每改变GLCOLOR,则改变glColorMaterial()中所定义当那种材质属性的颜色
还要用glEnable(GL_LIGHTING)打开关照功能,用glEnable(GL_LIGHT0)打开相应的光源
光源的名称为GL_LIGHT0, GL_LIGHT1, GL_LIGHT2, GL_LIGHT3。。。
11.光源类型,OPENGL由方向光源(太阳,只有方向无位置)和位置光源(只有位置),当制定参数GL_POSITION时,如果矩阵当W为0,则为方向光源,XYZ代表方向,如果W非零,则为位置光源,XYZ为其次的位置坐标

其中位置光源可以设置光强衰减(模仿自然光),可以设置其衰减的参数用glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 2.0);
glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 1.0);
glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.5);
因为实际光强= ×光强,默认KC=1,其余为0;

所有位置光源可以摄制成聚光灯,用glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 45.0);定义夹角,用glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction);定义方向

光源的位置也会受到MODELVIEW矩阵的影响,所以有时要用POP PUSH堆栈

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/leonwei/archive/2009/03/27/4029268.aspx