文章转自:http://blog.donews.com/atombomb/archive/2005/11/16/628498.aspx
OpenGl 是一套3D 绘图函数库, 在三维空间绘图是他的功能,可惜人和电脑的沟通方式还不够发达,不然是不是艺术家可以通过一些程序用意识凭空在空间里作画,是不是很酷(科幻电影看多了)而现在我们仍然只能通过传统的笛卡尔坐标系这种一板一眼的方式 将我的脑中的图形量化,和数字化。在这一点上OpenGl的对空间事物的体现方式无疑是完善的,在opengl中,也有一个内建的坐标系,就如我们的视野有限一样,opengl同样无法提供无限空间里的所有信息,但是我们可以把opengl提供的空间理解成一个盒子而我们正通过盒子唯一的开口向内观察,这一开口就是我们电脑屏幕,如果把这个盒子看成是一个 (-1,-1,-1),(1,1,1)的立方体的话,屏幕就是在z=-1 上从(1,1)到(-1,-1)的这片区域,事实上opengl也是这样做的 ,它在所提供的空间中圈定的也是这样一个坐标系 初始情况下在屏幕上 水平向左的为X轴正方向 竖直向上的为Y轴正方向 而Z轴则垂直于屏幕向外与屏幕平面的焦点为屏幕中心,坐标则为(0,0,-1). 而我们在屏幕上看到的图形是显示器上显示的就是从z为负无穷处射来的平行光在Z = -1处的屏幕上所成的像。
理解了OpenGL的这个坐标体系我们就可以开始里面作画了,在opengl中画点和线是由同一组函数完成的 基本结构是这样的
glBegin(mode_parameter);
glColor3f(r,g,b);
glVertex2f(x1,y2);
glVertex3f(x2,y2,z2);
glEnd();
这段代码的作用是在空间中以模式mode_parameter 和颜色(r g b) 在坐标(x1,y1,0.0)和(x2,y2,z2)处画两个点,当然你最终看到的是不是点还要取决于你的mode_parameter。在glbegin()与glend()之间是通过glvertex的一系列函数来定义一组顶点然后根据mode_parameter将这些点连接画线,或是以孤立点的形式画出,mode_parameter有以下几种:
GL_POINTS ,GL_LINES ,GL_LINE_STRIP ,GL_LINE_LOOP ,GL_TRIANGLES ,GL_TRIANGLE_STRIP ,GL_TRIANGLE_FAN ,GL_QUADS ,GL_QUAD_STRIP,GL_POLYGON
比较常用的是 GL_POINTS 画点 也就是说程序中以glvertex定义的点将被画在屏幕上
GL_LINES 画线 定义的每两个点将被连接起来变成一条直线,共N/2条
GL_LINE_STRIP 将所有的点连接变成一条折线
GL_LINE_LOOP 将GL_LINE_STRIP画成的折线头尾相连,形成闭合图形
GL_TRIANGLES 定义的每三个点将被连接起来涂成一个三角形,共N/3个
GL_QUADS 定义的每四个点将被连接起来涂成一个四边形,共N/4个
在这些模式中到底是哪几个点变成一组形成直线,或多边形取决于glvertex定义点的次序
至此 我们已经能够画出绝大多数直线形,但是曲线呢,数学上曲线是被定义为 N个点连成的折线 当N趋向无穷大的情况。而在计算机中我们只要画出N足够大的折线就可以认为是曲线了
还记得小时候学logo是 要画一个园 不就是画一个正几百边形吗,这样我们就可以画出绝大多数简单的函数图形了,理论说完了来实践一下吧 :
void display()
{
glClearColor(1,1,1,1); //设置刷新背景色
glClear(GL_COLOR_BUFFER_BIT); //刷新背景
glBegin(GL_LINES); //画个十字充当坐标系
glColor3f(0,0,0); //设置当前颜色
glVertex3f(-1,0,0);
glVertex3f(1,0,0);
glVertex3f(0,-1,0);
glVertex3f(0,1,0);
glEnd();
glBegin(GL_LINE_STRIP); //画个y=sin(4x) 的函数图像
glColor3f(1,0,0);
for (float x=-1.00;x<=1.00;x+=0.01)
glVertex2f(x,sin(x*4*pi));
glEnd();
glBegin(GL_LINE_LOOP); //画个蓝色的单位圆
glColor3f(0,0,1);
for (float x=-1.00;x<=1.00;x+=0.01)
glVertex2f(x,sqrt(1-x*x));
for (float x=1.00;x>=-1.00;x-=0.01)
glVertex2f(x,-sqrt(1-x*x));
glEnd();
glFlush(); //更新窗口
}
这里glvertex2f(x,y) 等价与 glvertex3f(x,y,0)
用这个display函数替换原来我们用来换背景的那个会看到一个单位圆,一个坐标系,和sin(4x)的图像
四.坐标变换 平移,缩放与旋转
前面我们知道OpenGL有内建的坐标系,事实上OpenGl有两套坐标系,一个坐标系被称为眼睛坐标(eye coordinate system) 简称ECS ,上章讲的就是这个坐标系。 OpenGL还有一套坐标,被称为(object coordinate system) 简称OCS ,而这个才是更为重要的,其实我们用来绘图的正是OCS。
两个坐标系中ECS 可以看成是一个现实存在的 基本不变的全局坐标系,而OCS则可以看成是用户自定义的坐标系,我们可以将这个坐标系任意的平移与缩放,在初始情况下他和ECS是重合的,也可以通过glLoadIdentity()强制复位,这样可以给我们的绘图带来极大的方便。这里有一点是要值得注意的是在使用一个函数时需要弄清它是使用什么坐标系的,刚刚我们用到的glVertex系列函数都是用的OCS
下面是一个平移的例子:
void makecross(float *color) //在当前OCS的中心画一个十字
{
glBegin(GL_LINES);
glColor3fv(color); //设置当前颜色
glVertex3f(-1,0,0);
glVertex3f(1,0,0);
glVertex3f(0,-1,0);
glVertex3f(0,1,0);
glEnd();
}
void display()
{
float red[3]={1,0,0};
float blue[3]={0,1,0};
float green[3]={0,0,1};
float yellow[3]={1,1,0};
glClearColor(1,1,1,1);
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
makecross(red);
glTranslatef(-0.5,-0.5,0);
makecross(blue);
glTranslatef(1,0.25,0);
makecross(green);
glTranslatef(-0.75,0.75,0);
glScalef(0.5,0.5,1); //在x,y 上缩小一半
makecross(yellow);
glFlush(); //更新窗口
}
这两个函数中makecross的作用是在坐标中心画一个十字,前面我们知道glVertex使用的是OCS 所以makecross 的作用便是在当前OCS的中心画一个十字,以观察OCS的位置,
glColor3fv(color)的功能于之前我们看到的glColor3f(r,g,b)是一样的,只不过一个是使用一个数组作为参数
void glLoadIdentity(void)
在display中 glLoadIdentity 的作用是使OCS 与ECS 重合,在这里我们用来初始化OCS
void glTranslate{fd}(TYPEx, TYPE y, TYPEz);
glTranslatef 是将OCS平移至x,y,z 出,也就是在(x,y,z)处建立新的OCS,这里要注意这里的参数X,Y,Z也是OCS坐标
void glScale{fd}(TYPEx, TYPE y, TYPEz);
glScalef则是当前OCS的缩放,x,y,z 分别指在三个方向上的放大倍数
说完了缩放和平移,我们来看看旋转,opengl 里的旋转是通过glrotate来实现的,他的本质是将当前矩阵在乘于旋转矩阵,就是将当前的OCS 旋转一个指定的角度
void glRotate{fd}(TYPE angle, TYPE x, TYPE y, TYPE z);
glrotate 是将当前OCS绕向量(x,y,z)逆时针旋转angle度 例如我们要讲上例中的图形旋转绕Z轴旋转45度则可以通过glrotatef(45,0,0,1)来实现。