本节是OpenGL学习的第二个课时,下面介绍如何用点和线来绘制图形:
(1)用点的坐标来绘制矩形:
#include <GL/glut.h> void display(void) { // clear all pixels glClear(GL_COLOR_BUFFER_BIT); // draw yellow polygon (rectangle) with corners at glColor3f(1.0, 1.0, 0.0); glBegin(GL_POLYGON); //绘制开始前必须调用glBegin以通知绘制图形的类型,比如还可以绘制点,线等。 glVertex3f(0.20, 0.20, 0.0); glVertex3f(0.80, 0.20, 0.0); glVertex3f(0.80, 0.80, 0.0); glVertex3f(0.20, 0.80, 0.0); glEnd(); //结束之后则要调用glEnd函数 glFlush(); } void init(void) { // select clearing color: blue glClearColor(0.0, 1.0, 0.0, 0.0); // initialize viewing values glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(300, 300); glutInitWindowPosition(400, 300); glutCreateWindow("polyon"); init(); glutDisplayFunc(display); glutMainLoop(); return 0; }
代码解释:
1)glClear(GLbitfield mask)
glClear sets the bitplane area of the window to values previously selected by glClearColor, glClearDepth, and glClearStencil. Multiple color buffers can be cleared simultaneously by selecting more than one buffer at a time using glDrawBuffer.
The pixel ownership test, the scissor test, dithering, and the buffer writemasks affect the operation of glClear. The scissor box bounds the cleared region. Alpha function, blend function, logical operation, stenciling, texture mapping, and depth-buffering are ignored by glClear.
glClear takes a single argument that is the bitwise OR of several values indicating which buffer is to be cleared.The values are as follows:
GL_COLOR_BUFFER_BIT Indicates the buffers currently enabled for color writing.
GL_DEPTH_BUFFER_BIT Indicates the depth buffer.
GL_STENCIL_BUFFER_BIT Indicates the stencil buffer.
The value to which each buffer is cleared depends on the setting of the clear value for that buffer.
2)glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
glClearColor specifies the red, green, blue, and alpha values used by glClear to clear the color buffers. Values specified by glClearColor are clamped to the range [0,1].
3)glMatrixMode(GLenum mode)
学习OpenGL时,对矩阵的操作是核心。glMatrixMode告诉我们这个当前矩阵是什么矩阵。
4)glColor3f()
对颜色进行设定。OpenGl绘制图形形状时,并不绘制颜色,而是在绘制形状之前指定好颜色
5)glLoadIdentity()
恢复初始坐标系,重置当前指定的矩阵为单位矩阵。
6)glOrtho(left, right, bottom, top, near, far)
glOrtho(投影变换函数)创建一个正交平行的视景体,一般用于"物体不会因为离屏幕的远近而产生大小的变换"的情况。
OpenGL中有两个比较重要的投影变换函数,glViewport和glOrtho。
(2)演示投影变换函数对画出图形位置的影响:
1).示例1:
#include <GL/glut.h> void display(void) { glClear(GL_COLOR_BUFFER_BIT); glColor3f(1.0, 1.0, 0.0); glBegin(GL_POLYGON); glVertex3f(0.20, 0.20, 0.0); glVertex3f(0.80, 0.20, 0.0); glVertex3f(0.80, 0.80, 0.0); glVertex3f(0.20, 0.80, 0.0); glEnd(); glFlush(); } void init(void) { glClearColor(0.0, 1.0, 0.0, 0.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(300, 300); glutInitWindowPosition(400, 300); glutCreateWindow("polyon"); init(); glutDisplayFunc(display); glutMainLoop(); return 0; }
2).示例2:
#include <GL/glut.h> void display(void) { glClear(GL_COLOR_BUFFER_BIT); glColor3f(1.0, 1.0, 0.0); glBegin(GL_POLYGON); glVertex3f(-0.80, 0.80, 0.0); glVertex3f(-0.80, -0.80, 0.0); glVertex3f(0.80, -0.80, 0.0); glVertex3f(0.80, 0.80, 0.0); glEnd(); glFlush(); } void init(void) { glClearColor(0.0, 1.0, 0.0, 0.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(300, 300); glutInitWindowPosition(400, 300); glutCreateWindow("polyon"); init(); glutDisplayFunc(display); glutMainLoop(); return 0; }
3)结论:
代码中红色标志了他们的对应关系。
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0)函数实际上让当前窗体显示的区域对应到一个笛卡尔坐标系xy平面的矩形上(仅2D,所以只看前4个参数),原点是窗体显示区域的中心。
在第一个例子中的点仅存在于第一象限,因此虽然窗体包含有4个坐标系,它仅仅在第一个区域显示图形。在第二个例子中,4个点存在4个象限中,因此显示的矩形横跨4个象限。
而在开始的例子中,窗体的显示区域仅仅对应了第一象限。
(3)详细介绍:
1).在openGL中编程,经常用到glColor3f()函数进行颜色设定,现对参数与颜色的对应关系整理如下:
glColor3f(0.0, 0.0, 0.0); --> 黑色 glColor3f(1.0, 0.0, 0.0); --> 红色 glColor3f(0.0, 1.0, 0.0); --> 绿色 glColor3f(0.0, 0.0, 1.0); --> 蓝色 glColor3f(1.0, 1.0, 0.0); --> 黄色 glColor3f(1.0, 0.0, 1.0); --> 品红色 glColor3f(0.0, 1.0, 1.0); --> 青色 glColor3f(1.0, 1.0, 1.0); --> 白色
2).void glMatrixMode(GLenum mode)
mode告诉计算机将什么矩阵设置为当前矩阵,可选值有: GL_MODELVIEW、GL_PROJECTION、GL_TEXTURE。
GL_MODELVIEW: 应用这个参数后,表示接下来的矩阵操作都是针对模型视景矩阵堆栈 。 直到下一次调用这个函数并更改参数为止。 GL_PROJECTION:应用这个参数后,表示接下来的矩阵操作都是针对投影矩阵堆栈 。 直到下一次调用这个函数并更改参数为止。 GL_TEXTURE : 应用这个参数后,表示接下来的矩阵操作都是针对纹理矩阵堆栈 。 直到下一次调用这个函数并更改参数为止。
由于对不同的矩阵有不同的操作,置了当前的矩阵后,我们接下来所调用的所有openGL库函数的功能必须确定是针对我们设定的这个当前矩阵的,不能张冠李戴。
glMatrixMode(GL_MODELVIEW );//设置当前矩阵为模型视景矩阵 gluPerspective(45.0f,(GLfloat)cx/(GLfloat)cy,0.1f,100.0f);//对图像进行透视投影,以将三维物体显示在二维平面上 这样调用是错误的,结果将没有图像显示。这是因为,我们设置了当前矩阵为模型视景矩阵,而gluPerspective()是要对投影矩阵进行操作,那么计算机就会把模型矩阵当做投影矩阵,来与 gluPerspective()指定的矩阵进行乘法运算,最终就会导致错误。
3).void glLoadIdentity(void)(http://blog.sina.com.cn/s/blog_957b9fdb0100zez9.html)
glLoadIdentity是非常简单的恢复初始坐标系的手段,用一个4×4的单位矩阵来替换当前矩阵。
当您调用glLoadIdentity()之后,您实际上将当前点移到了屏幕中心:类似于一个复位操作 1.X坐标轴从左至右,Y坐标轴从下至上,Z坐标轴从里至外。 2.OpenGL屏幕中心的坐标值是X和Y轴上的0.0f点。 3.中心左面的坐标值是负值,右面是正值。移向屏幕顶端是正值,移向屏幕底端是负值。移入屏幕深处是负值,移出屏幕则是正值。
由于某些原因可能使得当前矩阵中的元素有一些不确定的值,这将导致程序对图形对象进行几何变形时得到一个非预期的结果。因此有必要将当前矩阵初始成一个单位矩阵,即对图形对象不做任何变换。这就是为什么在调用过glMatrixMode()命令后,总是要调用该命令的原因。
用单位矩阵替换当前矩阵并不改变当前矩阵模式。
4).glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far)
创建一个平行视景体(就是一个长方体空间区域,也相当于对立体空间进行3D裁剪)。这种投影意味着离观察者较远的对象看上去不会变小(与透视投影相反)。实际上这个函数的操作是创建一个正射投影矩阵,并且用这个矩阵乘以当前矩阵。
六个参数, 前两个是x轴最小坐标和最大坐标,中间两个是y轴,最后两个是z轴值。其中近裁剪平面是一个矩形,矩形左下角点三维空间坐标是(left,bottom,-near),右上角点是(right,top,-near);远裁剪平面也是一个矩形,左下角点空间坐标是(left,bottom,-far),右上角点是(right,top,-far)。
注意,所有的near和far值同时为正或同时为负, 值不能相同。如果没有其他变换,正射投影的方向平行于Z轴,且视点朝向Z负轴。这意味着物体在视点前面时far和near都为负值,物体在视点后面时far和near都为正值。只有在视景体里的物体才能显示出来。如果最后两个值是(0,0),也就是near和far值相同了,视景体深度没有了,整个视景体都被压成个平面了,就会显示不正确。
5).glBegin()和glEnd()
绘制图像的开始或结束的标志。
它们之间可以执行的函数:
glVertex*() 设置顶点坐标 glColor*() 设置当前颜色 glIndex*() 设置当前颜色表 glNormal*() 设置法向坐标 glCoord*() 产生坐标 glCallList(),glCallLists() 执行显示列表 glTexCoord*() 设置纹理坐标 glEdgeFlag*() 控制边界绘制 glMaterial*() 设置材质
可以绘制的图形的类型:
GL_POINTS 单个顶点集 GL_LINES 多组双顶点线段 GL_POLYGON 单个简单填充凸多边形 GL_TRAINGLES 多组独立填充三角形 GL_QUADS 多组独立填充四边形 GL_LINE_STRIP 不闭合折线 GL_LINE_LOOP 闭合折线 GL_TRAINGLE_STRIP 线型连续填充三角形串 GL_TRAINGLE_FAN 扇形连续填充三角形串 GL_QUAD_STRIP 连续填充四边形串
6)3D编程的基础知识:
1、 渲染:对一个三维物体进行几何描述,并且把它转换为屏幕上的一幅图像,这个过程就叫渲染。 2、 纹理贴图:通过一副图像向一个多边形提供额外细节的技巧称为纹理贴图。我们所提供的图像称为纹理,纹理中每个单独的元素称为纹理单元(或纹理像素,texel)。 3、 过滤(filtering):在一个物体的表面上拉伸或压缩纹理单元(纹理像素)的过程称为过滤。 4、 混合(blending):指屏幕上颜色或物体的组合。混合可以用于多种目的,如制作透明效果、反射效果等。 5、 裁剪区域:窗口(即屏幕)是以像素为单位进行度量的。裁剪区域指的是占据窗口的笛卡尔坐标空间中的区域。也可以解释为填充窗口的笛卡尔坐标空间中的区域。注意,裁剪区域使用的是笛卡尔坐标系统。 6、 视口:因为裁剪区域的宽度和高度很少正好与窗口的宽度和高度(以像素为单位)相匹配,所以需要把坐标系统从逻辑笛卡尔坐标空间映射到物理屏幕像素坐标空间。视口就是窗口中用于绘制裁剪区域的客户区域。这里,要注意窗口与视口的区别,视口在窗口中指定,我们可以使用视口来缩小或者放大窗口中的图像。//类似4所说的东西 7. 管线:它用于描述一种过程,该过程可能涉及两个或更多个独特的阶段或步骤。 8、 OpenGL命令缓冲区:当应用程序进行OpenGL API函数调用时,这些命令被放置在一个命令缓冲区中。最终,这个缓冲区中会填满API 调用命令、顶点数据、纹理数据之类的东西。当缓冲区被刷新时,命令和数据就会被传递给管线的下一个阶段。 9、 变换和光照(T&L):这是一个数学计算密集型的阶段。在变换阶段,描述物体几何形状的顶点被重新计算(经历多次不同坐标空间的变换),以确定这个物体的位置和朝向。同时进行的光照计算阶段(世界坐标空间中)将确定每个顶点该具有的颜色和亮度。 10、 光栅化:该阶段根据几何图形、颜色和纹理数据实际创建彩色图像。然后,图像被放入到帧缓冲区之中。 11、 帧缓冲区:即图形显示设备的内存。图像放入到帧缓冲区意味着将会在屏幕上显示(刷新帧缓冲区时)。
(4)绘制不同图形的实例:
#include <GL/glut.h> #include <math.h> const int n = 1000; const GLfloat R = 0.40f; const GLfloat Pi = 3.1415926536f; const GLfloat factor = 0.1f; void paint(void) { //在下半部分画一个正弦图像,上半部分画一个圆和一个五角形 glClear(GL_COLOR_BUFFER_BIT); //1.圆形 int i; glColor3f(1.0,0.0,0.0);//红色的圆 glBegin(GL_POLYGON);//有顶点就必须要有glBegin() glEnd(); for(i=0; i<n; ++i) glVertex2f(R*cos(2*Pi/n*i)-0.50f, R*sin(2*Pi/n*i)+0.5f);//将圆形的中点向左上方平移 glEnd(); glFlush(); //2.五角形 glColor3f(0.0f,0.0f,0.0f);//黑色的五角形 GLfloat a = 1/ (4 - 4 * cos(72 * Pi / 180)); GLfloat bx = a * cos(18 * Pi / 180); GLfloat by = a * sin(18 * Pi / 180); GLfloat cy = -a * cos(18 * Pi / 180); GLfloat PointA[2] = { 0+0.50, a+0.50 }, PointB[2] = { bx+0.5, by+0.50 }, PointC[2] = { 0.25+0.50, cy+0.50 }, PointD[2] = { -0.25+0.50, cy+0.50 }, PointE[2] = { -bx+0.50, by+0.50 }; //将整体向右上方平移 // 按照A->C->E->B->D->A的顺序,将五角星画出 glBegin(GL_LINE_LOOP);//闭合折线 glVertex2fv(PointA); glVertex2fv(PointC); glVertex2fv(PointE); glVertex2fv(PointB); glVertex2fv(PointD); glEnd(); glFlush(); //3.正弦图像: //黑色的正弦图像 GLfloat x; glBegin(GL_LINE_STRIP); for(x=-1.0f/factor; x<1.0f/factor; x+=0.01f) { glVertex2f(x*factor, sin(x)*factor-0.50); } glEnd(); glFlush(); //4.分割线: glColor3f(0.5f,0.5f,0.0f);//黄色的分割线 glBegin(GL_LINES); glVertex2f(-1.0f, 0.0f); glVertex2f(1.0f, 0.0f); // 以上两个点可以画x轴 glVertex2f(0.0f, 0.0f); glVertex2f(0.0f, 1.0f); // 以上两个点可以画y轴 glEnd(); glFlush(); } void init(void) { glClearColor(0.0,1.0,1.0,0.0);//设背景为青色 glMatrixMode(GL_PROJECTION);//创建投影矩阵堆栈 glLoadIdentity();//将当前矩阵设为单位矩阵,恢复初始坐标系 gluOrtho2D(-1.0f,1.0f,-1.0f,1.0f);//窗口左上角坐标(-2,1),右下角坐标(2,-1); } int main(int argc,char *argv[]) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); glutInitWindowSize(600,600); glutInitWindowPosition(300, 100); glutCreateWindow("圆形五角形和正弦图像"); init(); glutDisplayFunc(paint); glutMainLoop(); return 0; }