本文将会介绍使用OpenGL进行点,线,三角形乃至多边形的绘制,,,
下图是采用不同类型的图形效果
一、点
点”是一切的基础。 OpenGL提供了一系列函数指定一个点。它们都以
glVertex开头,后面跟一个数字和1~2个字母。
例如:
glVertex2d
glVertex2f
glVertex3f
glVertex3fv 等等。
数字表示
参数的个数:2表示有两个参数,3表示三个,4表示四个。
字母表示
参数的类型:
s表示16位整数(OpenGL中将这个类型定义为
GLshort),
i表示32位整数(OpenGL中将这个类型定义为
GLint和
GLsizei),
f 表示32位浮点数(OpenGL中将这个类型定义为
GLfloat和
GLclampf),
d表示64位浮点数(OpenGL中将这个类型定义为
GLdouble和
GLclampd)。
v表示传递的几个参数将使用指针的方式,见下面的例子。
这些函数除了参数的类型和个数不同以外,功能是相同的。例如,以下五个代码段的功能是等效的:
(一)glVertex2i(1, 3);
(二)glVertex2f(1.0f, 3.0f);
(三)glVertex3f(1.0f, 3.0f, 0.0f);
(四)glVertex4f(1.0f, 3.0f, 0.0f, 1.0f);
(五)GLfloat VertexArr3[] = {1.0f, 3.0f, 0.0f};
glVertex3fv(VertexArr3);
OpenGL的很多函数都是采用这样的形式。
OpenGL中描述一个面(线、点)的方法是
glBegin/glEnd
命令组:
glBegin(形状);
glVertex(顶点1);
glVertex(顶点
2);
……
glEnd();
void glPointSize(GLfloat size);
size必须大于0.0f,默认值为1.0f,单位为“像素”。
注意
:对于具体的OpenGL实现,点的大小都有个限度的,如果设置的size超过最大值,则设置可能会有
问题。
下面是一段示例代码:
1 //GLUT的头文件 2 //本来OpenGL程序一般还要包含<GL/gl.h>和<GL/glu.h>,但GLUT的头文件中已经自动将这两个文件包含了,不必再次包含 3 #include <glut.h> 4 5 void myDisplay(void) 6 7 { 8 //表示清除颜色 9 glClear(GL_COLOR_BUFFER_BIT); 10 11 //设置点的大小 12 glPointSize(5.0f); 13 14 glBegin(GL_POINTS); 15 glVertex2f(0.0f, 0.0f); 16 glVertex2f(0.5f, 0.5f); 17 glEnd(); 18 19 //保证前面的OpenGL命令立即执行(而不是让它们在缓冲区中等待) 20 glFlush(); 21 22 } 23 24 //带命令行参数的main函数 25 int main(int argc, char *argv[]) 26 27 { 28 glutInit(&argc, argv); 29 30 glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); 31 32 glutInitWindowPosition(100, 100); 33 34 glutInitWindowSize(400, 400); 35 36 glutCreateWindow("OpenGL画点程序"); 37 38 glutDisplayFunc(&myDisplay); 39 40 glutMainLoop(); 41 42 return 0; 43 44 }
效果如图
二、线
直线可以指定
宽度
void glLineWidth(GLfloat width);
其用法跟glPointSize类似。
与OpenGL画点函数十分类似,不同在于glBegin()中的符号常量。
使用图元常量GL_LINES可连接每一对相邻定点而得到一组直线段。
1 glBegin(GL_LINES); 2 glVertex2iv (p1); 3 glVertex2iv (p2); 4 glVertex2iv (p3); 5 glVertex2iv (p4); 6 glVertex2iv (p5); 7 glEnd();
上述代码可以画出线段p1-p2和p3-p4。
示例
:
使用
GL_LINE_STRIP
则可以获得折线,如果需要封闭的折线则需要重复某些点坐标。
1 glBegin(GL_LINE_STRIP); 2 glVertex2iv (p1); 3 glVertex2iv (p2); 4 glVertex2iv (p3); 5 glVertex2iv (p4); 6 glVertex2iv (p5); 7 glEnd();
上述代码画出折线p1-p2-p3-p4-p5。
示例
:
生成封闭折线的常量是
GL_LINE_LOOP
。
1 glBegin(GL_LINE_LOOP); 2 glVertex2iv (p1); 3 glVertex2iv (p2); 4 glVertex2iv (p3); 5 glVertex2iv (p4); 6 glVertex2iv (p5); 7 glEnd();
上述代码画出封闭折线p1-p2-p3-p4-p5-p1。
-
示例代码
1 #include <GL/glut.h> 2 3 void init(){ 4 glClearColor (0.0, 0.0, 0.0, 0.0); 5 glMatrixMode (GL_PROJECTION); 6 gluOrtho2D (0.0, 200.0, 0.0, 150.0); 7 } 8 9 void display(){ 10 int p1 [] = {110, 50}; 11 int p2 [] = {20, 0}; 12 int p3 [] = {55, 80}; 13 int p4 [] = {90, 0}; 14 int p5 [] = {0, 50}; 15 16 glClear (GL_COLOR_BUFFER_BIT); 17 18 19 glBegin(GL_LINE_LOOP); 20 glVertex2iv(p1); 21 glVertex2iv(p2); 22 glVertex2iv(p3); 23 glVertex2iv(p4); 24 glVertex2iv(p5); 25 glEnd(); 26 27 glFlush(); 28 } 29 30 void main(int argc, char *argv[]) 31 { 32 glutInit(&argc, argv); 33 glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); 34 glutInitWindowSize(400, 300); 35 glutCreateWindow("Five-Pointed Star"); 36 init(); 37 glutDisplayFunc(display); 38 glutMainLoop(); 39 }
运行结果如下:
大家可自行比较三张截图了解不同。
画虚线
:
首先,使用
glEnable(GL_LINE_STIPPLE)
;来启动虚线模式(使用
glDisable(GL_LINE_STIPPLE)
可以关闭之)。
然后,使用
glLineStipple
来设置虚线的样式。
void glLineStipple(GLint factor, GLushort pattern);
pattern是由1和0组成的长度为16的序列,从最低位开始看,如果为1,则直线上接下来应该画的factor个点将被画为实的;如果为0,则直线上接下来应该画的factor个点将被画为虚的。
示例代码:
1 void myDisplay(void) 2 3 { 4 5 glClear(GL_COLOR_BUFFER_BIT); 6 7 glEnable(GL_LINE_STIPPLE); 8 9 glLineStipple(2, 0x0F0F); 10 11 glLineWidth(10.0f); 12 13 glBegin(GL_LINES); 14 15 glVertex2f(0.0f, 0.0f); 16 17 glVertex2f(0.5f, 0.5f); 18 19 glEnd(); 20 21 glFlush(); 22 23 }
效果:
三、几何物体
1.三角形
画三角形以不同顶点的连接有三种方式,但都是
内部填充
的方式
-
GL_TRIANGLES如同GL_LINES一样,第一个三角形的点是V0,V1,V2,第二个则是V3,V4,V5,即是一个3的倍数。不然最后的一个或两个点不显示。
-
GL_TRIANGLE_STRIP的填充方式犹如放弃前一个顶点,如第一个三角形V0,V1,V2,第二个则是V1,V2,V3(舍弃V0)
-
GL_TRIANGLE_FAN的填充方式将永远以V0为起始点,如第一个三角形为V0,V1,V2,第二个则是V0,V2,V3
这里就不自己画了,上面的例子已经可以充分表达了
2.四边形
有两种方式
其区别与画三角形相同,只不过STRIP是隔了2个顶点
3.多边形
GL_POLYGON用于画多边形
多边形无法绘制非凸多边形,如下图
但可以用glPolygonMode函数改变多边形绘制的模式,绘制其轮廓
下面从四个方面详细介绍下多边形的绘制问题:
(1)多边形的两面以及绘制方式。
从三维的角度来看,一个多边形具有两个面。每一个面都可以设置不同的绘制方式:填充、只绘制边缘轮廓线、只绘制顶点,其中“填充”是默认的方式。可以为两个面分别设置不同的方式。
glPolygonMode(GL_FRONT, GL_FILL); // 设置正面为填充方式
glPolygonMode(GL_BACK, GL_LINE); // 设置反面为边缘绘制方式
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); // 设置两面均为顶点绘制方式
(2)反转
一般约定为“顶点以逆时针顺序出现在屏幕上的面”为“正面”,另一个面即成为“反面”。生活中 常见的物体表面,通常都可以用这样的“正面”和“反面”,“合理的”被表现出来(请找一个比较透明的矿泉水瓶子,在正对你的一面沿逆时针画一个圆,并标明 画的方向,然后将背面转为正面,画一个类似的圆,体会一下“正面”和“反面”。你会发现正对你的方向,瓶的外侧是正面,而背对你的方向,瓶的内侧才是正 面。正对你的内侧和背对你的外侧则是反面。这样一来,同样属于“瓶的外侧”这个表面,但某些地方算是正面,某些地方却算是反面了)。
但也有一些表面比较特殊。例如“麦比乌斯带”(请自己Google一下),可以全部使用“正面”或全部使用“背面”来表示。
可以通过glFrontFace函数来交换“正面”和“反面”的概念。
glFrontFace(GL_CCW); // 设置CCW方向为“正面”,CCW即CounterClockWise,逆时针
glFrontFace(GL_CW); // 设置CW方向为“正面”,CW即ClockWise,顺时针
下面是一个示例程序,用它替换myDisplay函数,并将glFrontFace(GL_CCW)修改为glFrontFace(GL_CW),并观察结果的变化。
1 void myDisplay(void) 2 3 { 4 5 glClear(GL_COLOR_BUFFER_BIT); 6 7 glPolygonMode(GL_FRONT, GL_FILL); // 设置正面为填充模式 8 9 glPolygonMode(GL_BACK, GL_LINE); // 设置反面为线形模式 10 11 glFrontFace(GL_CCW); // 设置逆时针方向为正面 12 13 glBegin(GL_POLYGON); // 按逆时针绘制一个正方形,在左下方 14 15 glVertex2f(-0.5f, -0.5f); 16 17 glVertex2f(0.0f, -0.5f); 18 19 glVertex2f(0.0f, 0.0f); 20 21 glVertex2f(-0.5f, 0.0f); 22 23 glEnd(); 24 25 glBegin(GL_POLYGON); // 按顺时针绘制一个正方形,在右上方 26 27 glVertex2f(0.0f, 0.0f); 28 29 glVertex2f(0.0f, 0.5f); 30 31 glVertex2f(0.5f, 0.5f); 32 33 glVertex2f(0.5f, 0.0f); 34 35 glEnd(); 36 37 glFlush(); 38 39 }
效果:
改为
glFrontFace(GL_CW)
时效果如下:
(3)剔除多边形表面
在三维空间中,一个多边形虽然有两个面,但我们无法看见背面的那些多边形,而一些多边形虽然是正面的,但被其他多边形所遮挡。如果将无法看见的多边形和可见的多边形同等对待,无疑会降低我们处理图形的效率。在这种时候,可以将不必要的面剔除。
首先,使用
glEnable(GL_CULL_FACE)
;来启动剔除功能(使用
glDisable(GL_CULL_FACE)
可以关闭之)
然后,使用
glCullFace
来进行剔除。
glCullFace的参数可以是GL_FRONT,GL_BACK或者GL_FRONT_AND_BACK,分别表示剔除正面、剔除反面、剔除正反两面的多边形。
注意:剔除功能只影响多边形,而对点和直线无影响。例如,使用
glCullFace(GL_FRONT_AND_BACK)
后,所有的多边形都将被剔除,所以看见的就只有点和直线。
(4)镂空多边形
直线可以被画成虚线,而多边形则可以进行镂空。
首先,使用
glEnable(GL_POLYGON_STIPPLE)
;来启动镂空模式(使用
glDisable(GL_POLYGON_STIPPLE)
可以关闭之)。
然后,使用
glPolygonStipple
来设置镂空的样式。
void glPolygonStipple(const GLubyte *mask);
其中的参数mask指向一个长度为128字节的空间,它表示了一个32*32的矩形应该如何镂 空。其中:第一个字节表示了最左下方的从左到右(也可以是从右到左,这个可以修改)8个像素是否镂空(1表示不镂空,显示该像素;0表示镂空,显示其后面 的颜色),最后一个字节表示了最右上方的8个像素是否镂空。
但是,如果我们直接定义这个mask数组,像这样:
1 static GLubyte Mask[128] = 2 3 { 4 5 0x00, 0x00, 0x00, 0x00, // 这是最下面的一行 6 7 0x00, 0x00, 0x00, 0x00, 8 9 0x03, 0x80, 0x01, 0xC0, // 麻 10 11 0x06, 0xC0, 0x03, 0x60, // 烦 12 13 0x04, 0x60, 0x06, 0x20, // 的 14 15 0x04, 0x30, 0x0C, 0x20, // 初 16 17 0x04, 0x18, 0x18, 0x20, // 始 18 19 0x04, 0x0C, 0x30, 0x20, // 化 20 21 0x04, 0x06, 0x60, 0x20, // , 22 23 0x44, 0x03, 0xC0, 0x22, // 不 24 25 0x44, 0x01, 0x80, 0x22, // 建 26 27 0x44, 0x01, 0x80, 0x22, // 议 28 29 0x44, 0x01, 0x80, 0x22, // 使 30 31 0x44, 0x01, 0x80, 0x22, // 用 32 33 0x44, 0x01, 0x80, 0x22, 34 35 0x44, 0x01, 0x80, 0x22, 36 37 0x66, 0x01, 0x80, 0x66, 38 39 0x33, 0x01, 0x80, 0xCC, 40 41 0x19, 0x81, 0x81, 0x98, 42 43 0x0C, 0xC1, 0x83, 0x30, 44 45 0x07, 0xE1, 0x87, 0xE0, 46 47 0x03, 0x3F, 0xFC, 0xC0, 48 49 0x03, 0x31, 0x8C, 0xC0, 50 51 0x03, 0x3F, 0xFC, 0xC0, 52 53 0x06, 0x64, 0x26, 0x60, 54 55 0x0C, 0xCC, 0x33, 0x30, 56 57 0x18, 0xCC, 0x33, 0x18, 58 59 0x10, 0xC4, 0x23, 0x08, 60 61 0x10, 0x63, 0xC6, 0x08, 62 63 0x10, 0x30, 0x0C, 0x08, 64 65 0x10, 0x18, 0x18, 0x08, 66 67 0x10, 0x00, 0x00, 0x08 // 这是最上面的一行 68 69 };
这样一堆数据非常缺乏直观性,我们需要很费劲的去分析,才会发现它表示的竟然是一只苍蝇。
如果将这样的数据保存成图片,并用专门的工具进行编辑,显然会方便很多。下面介绍如何做到这一点。
首先,用Windows自带的画笔程序新建一副图片,取名为mask.bmp,注意保存时,应该选择“单色位图”。在“图象”->“属性”对话框中,设置图片的高度和宽度均为32。
用放大镜观察图片,并编辑之。黑色对应二进制零(镂空),白色对应二进制一(不镂空),编辑完毕后保存。
然后,就可以使用以下代码来获得这个Mask数组了。
1 static GLubyte Mask[128]; 2 3 FILE *fp; 4 5 fp = fopen("mask.bmp", "rb"); 6 7 if( !fp ) 8 9 exit(0); 10 11 // 移动文件指针到这个位置,使得再读sizeof(Mask)个字节就会遇到文件结束 12 13 // 注意-(int)sizeof(Mask)虽然不是什么好的写法,但这里它确实是正确有效的 14 15 // 如果直接写-sizeof(Mask)的话,因为sizeof取得的是一个无符号数,取负号会有问题 16 17 if( fseek(fp, -(int)sizeof(Mask), SEEK_END) ) 18 19 exit(0); 20 21 // 读取sizeof(Mask)个字节到Mask 22 23 if( !fread(Mask, sizeof(Mask), 1, fp) ) 24 25 exit(0); 26 27 fclose(fp);
好的,现在请自己编辑一个图片作为mask,并用上述方法取得Mask数组,运行后观察效果。
说明:绘制虚线时可以设置factor因子,但多边形的镂空无法设置factor因子。请用鼠标改变窗口的大小,观察镂空效果的变化情况。
1 #include <stdio.h> 2 3 #include <stdlib.h> 4 5 void myDisplay(void) 6 7 { 8 9 static GLubyte Mask[128]; 10 11 FILE *fp; 12 13 fp = fopen("mask.bmp", "rb"); 14 15 if( !fp ) 16 17 exit(0); 18 19 if( fseek(fp, -(int)sizeof(Mask), SEEK_END) ) 20 21 exit(0); 22 23 if( !fread(Mask, sizeof(Mask), 1, fp) ) 24 25 exit(0); 26 27 fclose(fp); 28 29 glClear(GL_COLOR_BUFFER_BIT); 30 31 glEnable(GL_POLYGON_STIPPLE); 32 33 glPolygonStipple(Mask); 34 35 glRectf(-0.5f, -0.5f, 0.0f, 0.0f); // 在左下方绘制一个有镂空效果的正方形 36 37 glDisable(GL_POLYGON_STIPPLE); 38 39 glRectf(0.0f, 0.0f, 0.5f, 0.5f); // 在右上方绘制一个无镂空效果的正方形 40 41 glFlush(); 42 43 }
效果:
转载于:https://www.cnblogs.com/tjulym/p/5018911.html