OpenGL 几何图元






4.72/5 (29投票s)
通过这个交互式程序学习 OpenGL 几何图元。
Visual C++ 2015 文件
Visual C++ 6 文件(旧)
目录
引言
这个交互式程序的目的是为了更容易地学习 OpenGL 几何图元。OpenGL 中有十种几何图元,从点到多边形,它们都由顶点表示。这个程序将为您提供以特定顺序添加和删除顶点的灵活性,并向您展示图元如何决定顶点的组合方式。它可以用于以下方面:
- 通过...
- 交互式程序学习 OpenGL 几何图元
- 源代码
- 文档
- 学习一些专门用于处理几何图元的 OpenGL 函数
- 手动绘制图元并生成相应的 OpenGL C 代码
用法
编译与运行程序
有关如何编译和运行程序的详细信息,请参阅 GLUT 窗口模板文章中的用法部分。
程序用法
Menu
图元 | 更改几何图元的形状、颜色和其他属性的选项。 |
顶点 | 用于生成顶点、增加/减少生成率、删除顶点、显示/隐藏顶点、显示/隐藏顶点编号、增加/减少顶点大小以及更改顶点颜色的选项。 |
Grid | 用于显示/隐藏网格、打开/关闭顶点自动对齐网格的选项、添加/删除网格行、添加/删除网格列、增加/减少构成网格的线的线宽以及更改网格颜色的选项。 |
生成代码 | 从您的绘图中生成 OpenGL C 代码。默认情况下,代码生成在一个名为 program.c 的文件中。 |
背景颜色 | 选择背景颜色 |
键盘
左/右 | 更改几何图元 |
上/下 | 更改图元颜色 |
a | 打开/关闭网格上的自动绘图 |
C/c | 向网格添加/删除列 |
d | 删除所有顶点 |
g | 显示/隐藏网格 |
h | 显示帮助消息 |
L/l | 增加/减小线宽 |
n | 显示/隐藏编号 |
p | 显示/隐藏顶点 |
R/r | 向网格添加/删除行 |
S/s | 增加/减小点大小 |
T/t | 增加/减小顶点生成速率 |
v | 生成顶点 |
W/w | 增加/减小网格线宽 |
X/x | 增加/减小顶点大小 |
z | 删除最后一个插入的顶点 |
鼠标
鼠标左键 | 添加一个顶点 |
鼠标中键 | 删除一个顶点 |
鼠标右键 | 显示菜单以获取更多选项 |
解释程序
顶点
所有几何图元都以顶点来描述,顶点是定义点本身、线段端点或多边形角的坐标。
为了在 OpenGL 中绘制一个顶点,我们使用 OpenGL 函数 glVertex
。该函数有以下几种变体:
void glVertex2d( GLdouble x, GLdouble y )
void glVertex2f( GLfloat x, GLfloat y )
void glVertex2i( GLint x, GLint y )
void glVertex2s( GLshort x, GLshort y )
void glVertex3d( GLdouble x, GLdouble y, GLdouble z )
void glVertex3f( GLfloat x, GLfloat y, GLfloat z )
void glVertex3i( GLint x, GLint y, GLint z )
void glVertex3s( GLshort x, GLshort y, GLshort z )
void glVertex4d( GLdouble x, GLdouble y, GLdouble z, GLdouble w )
void glVertex4f( GLfloat x, GLfloat y, GLfloat z, GLfloat w )
void glVertex4i( GLint x, GLint y, GLint z, GLint w )
void glVertex4s( GLshort x, GLshort y, GLshort z, GLshort w )
我们将使用 glVertex2f
,因为我们是在 2D 空间中绘制具有浮点坐标 x
和 y
的顶点。
此交互式程序支持以下对顶点的操作:
- 使用鼠标左键添加一个顶点。顶点坐标从鼠标坐标计算得出。
- 使用鼠标中键或按键盘上的“z”键删除我们最近添加的最后一个顶点。
- 通过按键盘上的“d”键或在右键菜单中选择“Vertex\Delete Vertices”菜单项来删除我们添加的所有顶点。
- 通过按键盘上的“v”键或在右键菜单中选择“Vertex\Generate Vertices”菜单项,生成随机坐标的随机数量顶点。
图元
图元是对顶点列表的某种形状的解释。在 OpenGL 中,我们通过将顶点列表放在 glBegin
()glEnd
() 块中来指定某个图元的顶点列表,如下所示:
glBegin (mode); glVertex* (...); ..... glVertex* (...); glEnd ();
其中 mode
是代表所需图元的符号常量。mode
可以是以下十个符号常量之一: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
。
此交互式程序通过以下方式支持图元:
- 维护一个顶点列表。
- 让用户选择所需的几何图元,方法是使用右键菜单的“Primitive\Type”子菜单或按左键和右键。
Points
OpenGL 中的点仅由一个顶点表示。所有内部计算都按照三维顶点进行。指定的二维顶点被赋予 z
坐标零。
GL_POINTS
描述
将每个顶点视为单个点。顶点 n 定义点 n。绘制 N 个点。
示例
glBegin(GL_POINTS); glVertex2f(x1, y1); glEnd();
注意
请注意,在所有图元示例中,都使用了最少数量的顶点来演示该图元的用法。例如,我们为点指定一个顶点,为线段指定两个顶点,为线带指定 3 个顶点,为四边形带指定 6 个顶点,依此类推……
预览
![]() |
![]() |
注意
每个几何图元将有两个预览。
- 第一个预览显示几何图元的单个实例。其目的是描述该图元到底是什么。
- 第二个预览显示使用跨越所有图元的通用点的图元。其目的是展示使用相同的点在选择不同图元时如何产生不同的形状。
有用的点函数
void glPointSize(GLfloat size)线
使用此函数来设置像素点的大小。例如,要将点大小设置为 4 像素,请执行以下操作:
glPointSize(4.0f);
此函数在交互式程序中使用,允许用户更改顶点大小或图元点大小(如果图元是点)。
在 OpenGL 中,线表示线段而不是无限线的数学概念。OpenGL 使得通过端点轻松地将线连接在一起。
GL_LINES
描述
将每对顶点视为独立的线段。顶点 2n-1 和 2n 定义线 n。绘制 N/2 条线。
示例
glBegin(GL_LINES); glVertex2f(x1, y1); glVertex2f(x2, y2); glEnd();
预览
![]() |
![]() |
GL_LINE_STRIP
描述
绘制一组连接的线段,从第一个顶点到最后一个。顶点 n 和 n+1 定义线 n。绘制 N-1 条线。
示例
glBegin(GL_LINE_STRIP); glVertex2f(x1, y1); glVertex2f(x2, y2); glVertex2f(x3, y3); glEnd();
预览
![]() |
![]() |
GL_LINE_LOOP
描述
绘制一组连接的线段,从第一个顶点到最后一个,然后回到第一个。顶点 n 和 n+1 定义线 n。然而,最后一条线由顶点 N 和 1 定义。绘制 N 条线。
示例
glBegin(GL_LINE_LOOP); glVertex2f(x1, y1); glVertex2f(x2, y2); glVertex2f(x3, y3); glEnd();
预览
![]() |
![]() |
有用的线函数
void glLineWidth(GLfloat width)
使用此函数设置像素线宽。例如,要将线宽设置为 2 像素,请执行以下操作:
glLineWidth(2.0f);
此函数在程序中使用,允许用户更改构成网格的线的宽度或图元线宽(如果图元是线)。
void glLineStipple(GLint factor, GLushort pattern)
使用此函数创建具有虚线或点划线图案的线,称为虚线样式。要使用虚线样式,我们必须首先通过调用以下函数来启用它:
glEnable(GL_LINE_STIPPLE);
"pattern" 是一个 16 位值,代表线的图案。16 位中的每一位代表线上 1 个像素 * factor。如果 factor 为 1,则一位代表 1 个像素。如果为 2,则一位代表 2 个像素……如果位的值为 1,则相应的像素开启,否则(位的值为 0)它们关闭。
请注意,为了提高性能,用于虚线样式的位模式在绘制线条时是反向使用的。这是一个例子:
short pattern = 0x0BAD; // 0000 1011 1010 1101 int factor = 1; glLineStipple(factor, pattern); glBegin(GL_LINES); ... glEnd();
此模式会影响线条:第一个像素开启,第二个关闭,第三个开启,第四个开启,第五个关闭,依此类推……
十六进制/二进制表
十六进制 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
二进制 | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 |
线虚线样式功能用于在网格线上应用交替图案,从而在窗口中显示较少的网格,以便将用户的注意力更多地吸引到正在绘制的图元上。
多边形
使用上述图元,我们可以绘制 3D 空间中的任何形状。但是,形状只能以线框形式绘制(即,未填充颜色)。为了绘制实心表面,我们需要的不仅仅是点和线;我们需要多边形。多边形是闭合的形状,可以填充颜色,也可以不填充。
GL_TRIANGLES
描述
将每三个顶点视为独立的三角形。顶点 3n-2、3n-1 和 3n 定义三角形 n。绘制 N/3 个三角形。
最简单的多边形是三角形,只有三条边。
示例
glBegin(GL_TRIANGLES); glVertex2f(x1, y1); glVertex2f(x2, y2); glVertex2f(x3, y3); glEnd();
预览
![]() |
![]() |
GL_TRAINGLE_STRIP
描述
绘制一组连接的三角形。在第一个顶点之后的每个顶点处定义一个三角形。对于奇数 n,顶点 n、n+1 和 n+2 定义三角形 n。对于偶数 n,顶点 n+1、n 和 n+2 定义三角形 n。绘制 N-2 个三角形。
使用三角形带的优点是,在指定了第一个三角形的前三个顶点后,您只需要为每个附加三角形指定一个点。在绘制大量相互连接的三角形以表示更复杂的结构时,这将节省大量数据和时间。
示例
glBegin(GL_TRIANGLE_STRIP); glVertex2f(x1, y1); glVertex2f(x2, y2); glVertex2f(x3, y3); glVertex2f(x4, y4); glEnd();
预览
![]() |
![]() |
GL_TRIANGLE_FAN
描述
绘制一组围绕中心点展开的连接的三角形。在第一个顶点之后的每个顶点处定义一个三角形。顶点 1、n+1 和 n+2 定义三角形 n。绘制 N-2 个三角形。
示例
glBegin(GL_TRIANGLE_FAN); glVertex2f(x1, y1); glVertex2f(x2, y2); glVertex2f(x3, y3); glVertex2f(x4, y4); glEnd();
预览
![]() |
![]() |
GL_QUADS
描述
将每组四个顶点视为独立的四边形。顶点 4n-3、4n-2、4n-1 和 4n 定义四边形 n。绘制 N/4 个四边形。
示例
glBegin(GL_QUADS); glVertex2f(x1, y1); glVertex2f(x2, y2); glVertex2f(x3, y3); glVertex2f(x4, y4); glEnd();
预览
![]() |
![]() |
GL_QUAD_STRIP
描述
绘制一组连接的四边形。在第一个顶点对之后,每对顶点定义一个四边形。顶点 2n-1、2n、2n+2 和 2n+1 定义四边形 n。绘制 N/2-1 个四边形。
示例
glBegin(GL_QUAD_STRIP); glVertex2f(x1, y1); glVertex2f(x2, y2); glVertex2f(x3, y3); glVertex2f(x4, y4); glVertex2f(x5, y5); glVertex2f(x6, y6); glEnd();
预览
![]() |
![]() |
GL_POLYGON
描述
绘制一个**凸**多边形。顶点 1 到 N 定义该多边形。如果多边形中任意两点之间的线段上的所有点或多边形边界上的所有点都位于多边形内部,则该多边形是凸多边形。
示例
glBegin(GL_POLYGON); glVertex2f(x1, y1); glVertex2f(x2, y2); glVertex2f(x3, y3); glVertex2f(x4, y4); glVertex2f(x5, y5); glEnd();
预览
![]() |
![]() |
此交互式程序的一个关键特性是它允许我们测试特定概念并验证其正确性。例如,让我们验证多边形是凸的。
形状 1 | 形状 2 | 形状 3 | |
Lines | ![]() |
![]() |
![]() |
Polygon | ![]() |
![]() |
![]() |
虽然形状 1 和形状 3 似乎遵循凸多边形的规则,但形状 2 对我来说似乎是非凸的,因为我可以将点 2 和 4 连接成一条不落在多边形内的线段。我们可以说 OpenGL 架构审查委员会关于他们在 redbook 中描述的内容是错误的吗?可能是我遗漏了什么……请证明我是错的!
有用的多边形函数
有很多与多边形相关的有用函数,但为了简单起见,我们在此只关注 glPolygonMode
函数。
void glPolygonMode(GLenum face, GLenum mode);
使用此函数来更改多边形的渲染方式。默认情况下,多边形会用当前颜色填充。但是,我们可以禁用填充多边形并显示其轮廓或构成它的顶点。
参数 | 描述 | 值 |
面 | 指定哪个面的多边形受模式更改的影响。 | GL_FRONT GL_BACK GL_FRONT_AND_BACK |
模式 | 指定新的绘图模式。GL_FILL 是默认模式,生成填充的多边形。GL_LINE 生成多边形轮廓,而 GL_POINT 仅绘制多边形的顶点。顶点。 |
GL_FILL GL_LINE GL_POINT |
在我们的交互式程序中,我们使用此函数来帮助绘制我们的多边形及其边框。要绘制多边形,我们说:
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glBegin(GL_POLYGON); glVertex2f(x1, y1); ... glEnd();
要绘制其边框,我们说:
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glBegin(GL_POLYGON); glVertex2f(x1, y1); ... glEnd();
程序视图
程序基本由 4 个可见组件组成:顶点、图元、网格和背景。以下是程序支持的允许您操纵这些组件视图的功能。
- 顶点
- 增加/减小顶点大小。单击“X”增大尺寸,单击“x”减小尺寸。您也可以在 Vertex\Vertex size 下选择 +\- 菜单项。
- 更改顶点颜色。您可以通过选择 Vertex\Vertex Color 菜单项来完成。
- 显示/隐藏顶点。按“p”键或在 Vertex 子菜单下选择 Show/Hide Points 菜单项。
- 显示/隐藏与顶点关联的编号。按“n”键或在 Vertex 子菜单下选择 Show/Hide Numbers 菜单项。
- 图元
- 更改图元颜色。您可以通过选择 Primitive\Color 菜单项或按向上和向下箭头键来完成。
- 如果图元是点,则更改点大小。按“S”增大尺寸,按“s”减小尺寸。您也可以在 Primitive\Point size 下选择 +\- 菜单项。
- 如果图元是线,则更改线宽。按“L”增大宽度,按“l”减小宽度。您也可以在 Primitive\Line width 下选择 +\- 菜单项。
- 如果图元是多边形,则显示/隐藏图元边框并更改边框颜色。您可以使用 Primitive\Polygon 下的菜单项来更改边框颜色并显示/隐藏边框。
- Grid
- 增加/减小网格线宽。按“W”增大宽度,按“w”减小宽度。您也可以在 Grid\Line width 下选择 +\- 菜单项。
- 更改网格线颜色。您可以通过选择 Back Color 菜单项来完成。
- 显示/隐藏网格。按“g”键或在 Grid 子菜单下选择 Show/Hide Grid 菜单项。
- 背景
- 更改背景颜色。您可以通过选择 Back Color 菜单项来完成。
查看本文附加的示例演示,了解程序的工作视频。
生成顶点
您可以生成随机颜色的随机数量的顶点。您可以通过选择 Vertex\Generate Vertices 菜单项或按“v”键来完成。
生成的顶点数量是随机的,但它取决于一个最大值,即生成率。要增加顶点数量的概率,您可以增加生成率。这可以通过选择 Vertex\Generation Rate 子菜单下的 +\- 菜单项来完成。
使用顶点生成功能的一个有趣方式是创建例如网格上所有点之间的所有可能线。例如,我可以将当前图元设置为 GL_LINES
,然后不断按“v”键,直到生成所有点之间的所有线。
Grid
自动绘图
当关闭自动绘图时,顶点会精确地放置在用户单击鼠标按钮的位置。
然而,当启用自动绘图时,顶点会自动对齐到网格,将它们放置在网格上最近的线交叉点。下图显示了上面绘制的相同顶点,但启用了自动绘图。
此功能的实现非常简单,因为它只围绕着将浮点值四舍五入到最接近整数的逻辑。考虑到标准 C 中没有 round
函数,我已经实现了自己的函数,如下所示:
int round(double number) { return (number >= 0) ? (int)(number + 0.5) : (int)(number - 0.5); }
这是负责处理鼠标点击的 mouse
回调函数的代码。x
和 y
坐标是相对于窗口的鼠标坐标,单位是像素。由于我们在 OpenGL 中绘制的是世界坐标而不是鼠标坐标,因此我们应该将 x
和 y
窗口坐标转换为相应的世界坐标 pointX
和 pointY
。请注意,当 autoPlot
为 true
时,pointX
和 pointY
坐标是如何四舍五入的。
//-------------------------------------------------------------------------
// This function is passed to the glutMouseFunc and is called
// whenever the mouse is clicked.
//-------------------------------------------------------------------------
void mouse(int button, int state, int x, int y)
{
if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
{
float pointX, pointY;
pointX = (float)x/window_width * world_width;
pointY = (float)(window_height - y)/window_height * world_height;
if (autoPlot)
{
pointX = round(pointX);
pointY = round(pointY);
}
// Add a vertex to the list of vertices...
addVertex(&head, &tail, pointX, pointY, 0.0f);
// automatically calls the display function
glutPostRedisplay();
}
else if(button == GLUT_MIDDLE_BUTTON && state == GLUT_DOWN)
{
deleteVertex(&head, &tail);
glutPostRedisplay();
}
}
自动绘图也适用于随机生成的顶点。因此,当自动绘图关闭且顶点生成时,它们会放置在窗口内的任何位置。但是,当自动绘图开启时,这些点会被放置在网格上。
//-------------------------------------------------------------------------
// Generate a random number of vertices at random positions.
//-------------------------------------------------------------------------
void generateVertices()
{
int i;
int numberOfVertices = rand() % generationRate;
for(i = 0; i < numberOfVertices; i++)
{
float x, y;
if (autoPlot)
{
x = rand() % (int)world_width;
y = rand() % (int)world_height;
}
else
{
x = rand() % window_width;
y = rand() % window_height;
x = (float)x/window_width * world_width;
y = (float)(window_height - y)/window_height * world_height;
}
// Add a vertex to the list of vertices...
addVertex( &head, &tail, x, y, 0.0f);
}
glutPostRedisplay();
}
添加和删除行与列
假设您正在绘制一些东西,但是空间不够用了。按“d”键清除一切,然后重新尝试绘制较小版本的图形,这不是很酷。
此交互式程序使您能够通过添加列和/或行来扩展绘图区域。它还为您提供了通过删除列和/或行来缩小该区域的选项。
假设我想使用 GL_LINE_STRIP
图元绘制一栋房子。我开始绘制,但网格无法容纳我的图形,如下所示:
我可以像下面这样通过扩展网格来解决这个问题。您可以按“R”键添加一行;按“r”键删除一行;按“C”键添加一列;按“c”键删除一列。
请注意,此功能还可以用于缩放绘图区域。
生成代码
因此,您决定变得极其极客,并想只使用 GL_LINES
图元绘制一些有用的几何图形。您对此感到非常高兴,并想将您的作品永垂不朽。很简单。右键单击,然后选择“Generate Code”菜单项。这将生成渲染顶点以及定义它们之间关系的图元所需的 OpenGL C 代码。
生成的代码将放置在一个名为 program.c 的文件中。program.c 文件将包含绘制该图元的 OpenGL 生成代码。如果提供了名为 skeleton.c 的额外文件,程序将读取其内容,用生成的 C 代码替换找到的第一个 HTML 注释,然后将结果写入新文件 program.c。因此,program.c 将是 skeleton.c 的副本,只是它包含的是生成的 C 代码而不是 HTML 注释。我已经随可执行文件一起提供了 skeleton.c 文件。您可以将其替换为您自己的。
我的 skeleton.c 文件包含一个 OpenGL 应用程序的代码,该应用程序使用 drawObject
方法绘制其内容。因此,将 HTML 注释放在该方法内是合乎逻辑的。
void drawObject ()
{
<!-- Insert Geometric Primitives here -->
}
当我们生成 program.c 文件时,它将包含与 skeleton.c 相同的内容,只是 drawObject
方法现在将如下所示:
void drawObject ()
{
// Set the drawing color
glColor3f(0.00, 0.00, 1.00);
// Display geometric primitives
glBegin(GL_LINES);
glVertex2f(1.26, 0.96);
glVertex2f(1.72, 1.48);
... // and so on
glEnd();
}
需要注意的一个重要事项是,您的骨架程序的*世界坐标*必须与交互式程序的*世界坐标*匹配,才能使您的图元在您的程序中显示的方式与在交互式程序中完全相同。
结论
本文不仅解释了 OpenGL 几何图元,还为您提供了一个交互式程序,让您无需编写代码即可检查所学概念。如果您需要,它甚至可以为您生成代码。
希望它对许多对图形感兴趣的人有用,并且值得所投入的时间。请告诉我您的想法!
彩蛋
查找它!
复活节彩蛋在GitHub 版本中已禁用,因此如果您从那里获取代码,您将看到更多功能。
参考文献
修订历史
02/22/2008:
- 原始文章发布