OpenGL(三)之基础绘制篇

本文将会介绍使用OpenGL进行点,线,三角形乃至多边形的绘制,,,

 

下图是采用不同类型的图形效果

 http://images.cnblogs.com/cnblogs_com/Clingingboy/WindowsLiveWriter/OpenGL3_C7AD/image_4.png

 

一、点

点”是一切的基础。 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.三角形

 

画三角形以不同顶点的连接有三种方式,但都是内部填充的方式

 

image

 

  1. GL_TRIANGLES如同GL_LINES一样,第一个三角形的点是V0,V1,V2,第二个则是V3,V4,V5,即是一个3的倍数。不然最后的一个或两个点不显示。
  2. GL_TRIANGLE_STRIP的填充方式犹如放弃前一个顶点,如第一个三角形V0,V1,V2,第二个则是V1,V2,V3(舍弃V0)
  3. GL_TRIANGLE_FAN的填充方式将永远以V0为起始点,如第一个三角形为V0,V1,V2,第二个则是V0,V2,V3

这里就不自己画了,上面的例子已经可以充分表达了

 

2.四边形

 

有两种方式

 

image

 

其区别与画三角形相同,只不过STRIP是隔了2个顶点

 

3.多边形

 

image

 

GL_POLYGON用于画多边形

 

多边形无法绘制非凸多边形,如下图

 

image

 

但可以用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

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值