2.4 显示点、直线和多边形

本文详细描述了点、直线、多边形的属性,包括点的大小、直线的线宽和线型、多边形的正反面、剔除面、点画模型等,提供了一系列函数用于控制其显示方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这一小节主要描述点、直线、多边形的属性,通过这些属性,控制点、直线、多边形的显示方式。内容有点多。

点的细节

这里只描述了点的一个属性,点的大小,可以通过glPointSize函数来控制。然后通过glGetFloatv来进行查询。其中点的大小,还有抗锯齿和非抗锯齿之分。

void glPointSize(GLfloat size);

这个函数的参数,表示点的大小,单位是像素。size必须大于0.0,默认情况下为1.0。由于像素是最小的可显示单位,不能显示0.1个像素,所以,OpenGL对于浮点数采用了四舍五入,比如size = 1.1则只绘制一个像素乘以一个像素的方块,size=1.6则绘制两个像素乘以两个像素的方块。

如果启用了抗锯齿或多重采样,则绘制的点,不再是单纯的像素方块了。而是一个圆形的像素区域。一般情况下,位于边界的像素所使用的颜色强度较小,使边缘具有更平滑的外观。

这个点的大小,应该有一个取值范围,这个范围是多少呢?
这个可以通过函数进行查询。glGetFloatv()函数,以GL_ALIASED_POINT_RANGE为参数,可以查询在未进行抗锯齿处理的情况下的最小和最大的点,其中单词aliased表示 锯齿的。

那在抗锯齿情况下,如何查询点的大小范围呢?通过glGetFloatv()函数,参数使用GL_SMOOTH_POINT_SIZE_RANGE进行查询即可。除了可以查询范围之外,还可以查询特定抗锯齿点大小的精度。以GL_SMOOTH_POINT_SIZE_GRANULARITY为参数,调用glGetFloatv()即可。其中单词 granularity,意思是 间隔尺寸、粒度。

直线的细节

直线有两个属性,线宽 和 线型。 线宽是指线段的宽度,宽度越大,线段越粗。 线型是指这个线是实线,还是虚线,或者点线。

线宽

直线的宽度,使用glLineWidth来控制。

void glLineWidth(GLfloat width);

这个函数的参数width,也是以像素为单位。width参数也必须大于0.0,默认情况下为1.0。这个和点的控制函数glPointSize的参数是一样的。

这个线宽和点的大小在很多地方都有相同之处。这个也分抗锯齿和非抗锯齿。不使用抗锯齿,那么指定线宽为2,就是单纯的使用2个像素绘制直线。而如果使用了抗锯齿,那么,直线边界处的像素会画得淡一些。

同样,线宽范围,也可以通过函数查询。传递GL_ALIASED_LINE_WIDTH_RANGE给glGetFloatv()函数,可以查询非抗锯齿情况下,线宽范围。传递GL_SMOOTH_LINE_WITH_RANGE和GL_SMOOTH_LINE_WIDTH_GRANULARITY可以查询抗锯齿情况下,线的宽度范围和精度。

线型

书中管这个叫点画线。使用函数glLineStipple()进行控制直线的线型。stipple 这个单词,就是 点画,点刻 的意思。设置了线型以后,还需要开启直线点画功能。如下所示:

glLineStipple(1, 0x3F07);
glEnable(GL_LINE_STIPPLE);

下面glLineStipple的两个参数。第一个参数 GLint factor,表示重复因子。什么的重复呢?重复第二个参数的。那就必须先了解第二个参数。

第二个参数 GLushort pattern,是无符号,短整型的。这个数字,比如0x3F07可以表示成二进制 0011111100000111,二进制中的1表示绘制像素,0表示不绘制像素,需要从右往左解读。先绘制3个像素,因为右边有3个1,中间5个像素不绘制,再绘制6个像素,然后2个像素不绘制。这就形成了一定的线型。然后按照这个线型,重复的去绘制直线。

第一个参数factor这个重复因子,就表示第二个参数pattern的每一个二进制位,重复多少次。比如如果factor为2,那么0x3F07这个数字的二进制位,每一位重复2次,那就是先绘制6个像素,再空10个像素,再绘制12个像素,最后空4个像素。

然后书中给出了一个具体的例子,用来说明直线的点画模式。

#include <GL/glut.h>
#include <stdlib.h>

#define drawOneLine(x1,y1,x2,y2)  glBegin(GL_LINES);  \
   glVertex2f ((x1),(y1)); glVertex2f ((x2),(y2)); glEnd();

void init(void) 
{
   glClearColor (0.0, 0.0, 0.0, 0.0);
   glShadeModel (GL_FLAT);
}

void display(void)
{
   int i;

   glClear (GL_COLOR_BUFFER_BIT);

/* select white for all lines  */
   glColor3f (1.0, 1.0, 1.0);

/* in 1st row, 3 lines, each with a different stipple  */
   glEnable (GL_LINE_STIPPLE);

   glLineStipple (1, 0x0101);  /*  dotted  */
   drawOneLine (50.0, 125.0, 150.0, 125.0);
   glLineStipple (1, 0x00FF);  /*  dashed  */
   drawOneLine (150.0, 125.0, 250.0, 125.0);
   glLineStipple (1, 0x1C47);  /*  dash/dot/dash  */
   drawOneLine (250.0, 125.0, 350.0, 125.0);

/* in 2nd row, 3 wide lines, each with different stipple */
   glLineWidth (5.0);
   glLineStipple (1, 0x0101);  /*  dotted  */
   drawOneLine (50.0, 100.0, 150.0, 100.0);
   glLineStipple (1, 0x00FF);  /*  dashed  */
   drawOneLine (150.0, 100.0, 250.0, 100.0);
   glLineStipple (1, 0x1C47);  /*  dash/dot/dash  */
   drawOneLine (250.0, 100.0, 350.0, 100.0);
   glLineWidth (1.0);

/* in 3rd row, 6 lines, with dash/dot/dash stipple  */
/* as part of a single connected line strip         */
   glLineStipple (1, 0x1C47);  /*  dash/dot/dash  */
   glBegin (GL_LINE_STRIP);
   for (i = 0; i < 7; i++)
      glVertex2f (50.0 + ((GLfloat) i * 50.0), 75.0);
   glEnd ();

/* in 4th row, 6 independent lines with same stipple  */
   for (i = 0; i < 6; i++) {
      drawOneLine (50.0 + ((GLfloat) i * 50.0), 50.0,
         50.0 + ((GLfloat)(i+1) * 50.0), 50.0);
   }

/* in 5th row, 1 line, with dash/dot/dash stipple    */
/* and a stipple repeat factor of 5                  */
   glLineStipple (5, 0x1C47);  /*  dash/dot/dash  */
   drawOneLine (50.0, 25.0, 350.0, 25.0);

   glDisable (GL_LINE_STIPPLE);
   glFlush ();
}

void reshape (int w, int h)
{
   glViewport (0, 0, (GLsizei) w, (GLsizei) h);
   glMatrixMode (GL_PROJECTION);
   glLoadIdentity ();
   gluOrtho2D (0.0, (GLdouble) w, 0.0, (GLdouble) h);
}

/* ARGSUSED1 */
void keyboard(unsigned char key, int x, int y)
{
   switch (key) {
      case 27:
         exit(0);
         break;
   }
}

int main(int argc, char** argv)
{
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
   glutInitWindowSize (400, 150); 
   glutInitWindowPosition (100, 100);
   glutCreateWindow (argv[0]);
   init ();
   glutDisplayFunc(display); 
   glutReshapeFunc(reshape);
   glutKeyboardFunc(keyboard);
   glutMainLoop();
   return 0;
}

运行结果如下所示。
这里写图片描述
第一行,切换了3种线型。
第二行,比第一行的线的宽度增大了5倍,线型还是一样的。
第三行,使用一种线型,但是几何图元是GL_LINE_STRIP,而不再是GL_LINES。
第四行,同样的线型,画了6条相互连接的直线,发现和第三行的效果是一样的。
第五行,使用一种线型,绘制了一条直线,但是线型的重复因子,设置成了5。发现和第四行相比,单个模式的线,宽了5倍。原来看起来的一个点,现在像一条小短线。

多边形的细节

多边形的细节,我首先想到的是多边形的边,多边形的填充区域,它们两个的属性。
但是这个不按规则出牌啊,给我搞出了什么多边形的正反面、剔除面、点画多边形、还有点、轮廓或实心形式的多边形。

其实也还可以理解,正常的多边形,有边和填充区域。如果我不想绘制多边形的填充区域,那就是只绘制多边形轮廓。如果连轮廓也不想绘制,那就只绘制多边形的顶点。 如果我觉得多边形的填充区域太单调,就可以使用多边形的 点画模型,正如直线的点画模式那样。唯一不好理解的就是多边形的正反面 和 剔除面。

点、轮廓或实心形式的多边形

这个是通过函数glPolygonMode来实现的。

glPolygonMode(GLenum face, GLenum mode);

第一个参数,表示这个作用于哪个面,一共可以有三个可选的值,正面GL_FRONT,反面GL_BACK,正反面GL_FRONT_AND_BACK。第二个参数,表示这个面的多边形绘制模式,是只绘制顶点GL_POINT,还是绘制轮廓GL_LINE,或者绘制填充区域GL_FILL, 这三者其实就是点、线、面。

正反面

那OpenGL的正反面如何定义的呢?
我们在纸上画一个正方形,我们画画时,朝上的那一面,叫正面,朝下的那一面,叫反面。
OpenGL中的正反面,是以顶点出现在屏幕上的顺序来定义的。
例如:

glBegin(GL_POLYGON);
    glVertex2f(0.0, 0.0);//顶点A
    glVertex2f(0.0, 3.0);//B
    glVertex2f(4.0, 3.0);//C
    glVertex2f(4.0, 0.0);//D
glEnd();

上面的代码,指定了一个四边形,这个四边形位于z=0平面上。OpenGL采用的右手坐标系,当我们从z轴正半轴看时,看到ABCD四个点,成顺时针方向。当我们从Z轴负半轴看时, ABCD四个点成逆时针方向。默认情况下,顶点以逆时针方向出现在屏幕上时,这个面为“正面”。以这个四边形为例,默认情况下,Z轴负半轴为这个四边形的正面,Z轴正半轴为这个四边形的反面。不过,这个正反面可以调换过来,通过调用函数glFrontFace()。

glFrontFace(GLenum mode);

这个函数的参数,可以使GL_CCW,CCW全称 counter clockwise,逆时针。或者GL_CW,CW全称clockwise,顺时针。默认值为GL_CCW。

反转和剔除多边形表面

反转多边形表面,上面已经描述了,通过调用glFrontFace。

剔除多边形表面

为什么要剔除多边形表面?因为我们通常只能看到多边形的一面,看不到的另一面,没必要绘制,所以通过剔除多边形表面,可以减少没必要的渲染。

通过函数glCullFace来剔除。

void glCullFace(GLenum mode);

mode参数可以是GL_FRONT, GL_BACK, GL_FRONT_AND_BACK。为了使剔除生效,必须以GL_CULL_FACE为参数调用glEnable()函数来启用剔除功能。当然也可以使用glDisable()函数禁用剔除功能。

点画多边形

这个可以参照点画直线来进行。

在点画直线中,先调用glLineStipple()设置线型,再调用glEnable(GL_LINE_STIPPLE)启用直线的点画模式,然后开始画线就可以了。

在点画多边形中,也是先指定点画模式,然后启用多边形点画模式功能,最后开始画多边形即可。只是指定多边形点画模式的函数,叫glPolygonStipple()。然后启用多边形点画功能,是通过调用glEnable(GL_POLYGON_STIPPLE)。

void glPolygonStipple(const GLubyte *mask);

这个函数的参数mask,是一个指向32X32位图的指针,后者被解释为0和1的掩码。如果模式中出现的是1,那么多边形中对应的像素就被绘制,如果出现的是0,多边形中对应的像素就不会被绘制。这个和glLineStipple函数是一致的。

下面是多边形点画模式的代码:

#include <stdlib.h>
#include <GL/glut.h>

void display(void)
{
    GLubyte fly[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x80, 0x01, 0xC0, 0x06, 0xC0, 0x03, 0x60, 0x04, 0x60, 0x06, 0x20,
0x04, 0x30, 0x0C, 0x20, 0x04, 0x18, 0x18, 0x20, 0x04, 0x0C, 0x30, 0x20,
0x04, 0x06, 0x60, 0x20, 0x44, 0x03, 0xC0, 0x22, 0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22, 0x66, 0x01, 0x80, 0x66,
0x33, 0x01, 0x80, 0xCC, 0x19, 0x81, 0x81, 0x98, 0x0C, 0xC1, 0x83, 0x30,
0x07, 0xe1, 0x87, 0xe0, 0x03, 0x3f, 0xfc, 0xc0, 0x03, 0x31, 0x8c, 0xc0,
0x03, 0x33, 0xcc, 0xc0, 0x06, 0x64, 0x26, 0x60, 0x0c, 0xcc, 0x33, 0x30,
0x18, 0xcc, 0x33, 0x18, 0x10, 0xc4, 0x23, 0x08, 0x10, 0x63, 0xC6, 0x08,
0x10, 0x30, 0x0c, 0x08, 0x10, 0x18, 0x18, 0x08, 0x10, 0x00, 0x00, 0x08};

    GLubyte halftone[] = {
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA,
0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA,
0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA,
0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA,
0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA,
0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55};

    glClear (GL_COLOR_BUFFER_BIT);

/*  draw all polygons in white  */
    glColor3f (1.0, 1.0, 1.0);

/*  draw one solid, unstippled rectangle,   */  
/*  then two stippled rectangles        */
    glRectf (25.0, 25.0, 125.0, 125.0);
    glEnable (GL_POLYGON_STIPPLE);
    glPolygonStipple (fly);
    glRectf (125.0, 25.0, 225.0, 125.0);
    glPolygonStipple (halftone);
    glRectf (225.0, 25.0, 325.0, 125.0);
    glDisable (GL_POLYGON_STIPPLE);

    glFlush ();
}

void myinit (void) 
{
/*  clear background to black   */
    glClearColor (0.0, 0.0, 0.0, 0.0);
    glShadeModel (GL_FLAT);    
}

static void reshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0.0, (GLdouble)w, 0.0, (GLdouble)h, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/*  Main Loop
 *  Open window with initial window size, title bar, 
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize (350, 150);
    glutCreateWindow (argv[0]);
    myinit ();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMainLoop();
    return 0;             /* ANSI C requires main to return int. */
}

运行结果如下所示:
这里写图片描述

标记多边形的边界边

边界边,就是位于多边形边界处的边,这个和内部边对立。
标记边界边干什么?当使用GL_LINE模式绘制多边形时,为了将内部边和边界边区分开。
如何标记边界边?通过函数glEdgeFlag()函数来进行标记。

glEdgeFlag(GLboolean flag);

使用起来比较简便,在glBegin和glEnd函数之间调用。调用完了之后,后面创建的点都受这个标记的影响,直到再次标记。

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glBegin(GL_POLYGON);
    glEdgeFlag(GL_TRUE);
    glVertex3fv(v0);
    glEdgeFlag(GL_FALSE);
    glVertex3fv(v1);
    glEdgeFlag(GL_TRUE);
    glVertex3fv(v2);
glEnd();

如上所示,上面的v0和v2被标记为边界点,而v1被标记为非边界点。
正常情况下,OpenGL绘制的线, v0->v1, v1->v2, v2->v0,这三条线,但是由于v1被标记为非边界点,所以v1->v2这条边就是非边界边,结果就不画出来了。

啊,这一小节终于学完啦。靠!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值