欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 社会 > An OpenGL Toolbox

An OpenGL Toolbox

2025/1/27 2:16:15 来源:https://blog.csdn.net/weixin_48524215/article/details/145357680  浏览:    关键词:An OpenGL Toolbox

3.An OpenGL Toolbox

声明:该代码来自:Computer Graphics Through OpenGL From Theory to Experiments,仅用作学习参考

3.1 Vertex Arrays and Their Drawing Commands

顶点数组及其绘制命令:将几何数据存储在一个位置,方便用户访问。

3.2 Vertex Buffer Objects

顶点缓存对象:在图形服务器上存储与顶点相关的数据,以节省客户机到服务器的传输时间。

opengl客户端-服务器模型意味着每次服务器GPU需要顶点数据, 例如坐标,颜色或诸如此类的数据来执行,例如,调用glDrawElements(), 它必须从客户端CPU中获取。
在PC上,数据通过连接CPU(持有应用程序和数据的客户端)到GPU(图形处理单元,作为绘图的服务器)的总线的传输。通过总线访问数据通常要比本地访问慢很多倍。
为了提高效率,对象允许程序员显式地要求将一些特定的数据集(通常是与顶点相关的数据集,例如顶点数组)从客户机CPU传送到服务器GPU并存储在服务器GPU上以供将来使用
存储顶点数据的网格对象,这些对象被称为顶点网格对象(VBO,Vertex Buffer Object)


例子:绘制一个环形(Annulus)

与VBO相关的代码

#define VERTICES 0
#define INDICES 1// Begin globals.
// Vertex co-ordinate vectors.
//vertices[]和colors[]存储在buffer[VERTICES]中
static float vertices[] =
{30.0, 30.0, 0.0,10.0, 10.0, 0.0,70.0, 30.0, 0.0,90.0, 10.0, 0.0,70.0, 70.0, 0.0,90.0, 90.0, 0.0,30.0, 70.0, 0.0,10.0, 90.0, 0.0
};// Vertex color vectors.
static float colors[] =
{0.0, 0.0, 0.0,1.0, 0.0, 0.0,0.0, 1.0, 0.0,0.0, 0.0, 1.0,1.0, 1.0, 0.0,1.0, 0.0, 1.0,0.0, 1.0, 1.0,1.0, 0.0, 0.0
};// Triangle strip vertex indices in order.
static unsigned int stripIndices[] = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1 };  //存储在buffer[INDICES]中static unsigned int buffer[2]; // Array of buffer ids.
// End globals.
// Drawing routine.
void drawScene(void)
{glClear(GL_COLOR_BUFFER_BIT);// Get a pointer to the vertex buffer.// float* bufferData = (float*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);// Randomly change the color values.// for (int i = 0; i < sizeof(colors) / sizeof(float); i++)// bufferData[sizeof(vertices) / sizeof(float) + i] = (float)rand() / (float)RAND_MAX;// Release the vertex buffer.// glUnmapBuffer(GL_ARRAY_BUFFER);// Draw square annulus.glDrawElements(GL_TRIANGLE_STRIP, 10, GL_UNSIGNED_INT, 0);glutSwapBuffers();
}
// Initialization routine.
void setup(void)
{// 设置清除颜色为白色glClearColor(1.0, 1.0, 1.0, 0.0);// 生成两个缓冲区对象的IDglGenBuffers(2, buffer);//将指定的缓冲区对象绑定到目标缓冲区类型//目标缓冲区类型GL_ARRAY_BUFFER//buffer[VERTICES]是之前通过glGenBuffers生成的缓冲区对象ID之一glBindBuffer(GL_ARRAY_BUFFER, buffer[VERTICES]);  //buffer[VERTICES]存储顶点的坐标和颜色//为当前绑定到GL_ARRAY_BUFFER的缓冲区对象分配存储空间//sizeof(vertices) + sizeof(colors)指定了缓冲区的大小,即顶点数据和颜色数据的总和//第三个参数NULL表示我们只预留空间,不立即提供数据//GL_STATIC_DRAW是一个提示,告诉OpenGL我们打算如何使用这个数据。GL_STATIC_DRAW表示数据将不会被频繁更改,主要用于静态绘制glBufferData(GL_ARRAY_BUFFER, sizeof(vertices) + sizeof(colors), NULL, GL_STATIC_DRAW);//将顶点坐标数据复制到当前绑定的顶点缓冲区对象的前半部分//GL_ARRAY_BUFFER指定了目标缓冲区类型//0表示数据复制的起始偏移量,即从缓冲区的起始位置开始复制//sizeof(vertices)指定了要复制的数据大小,即顶点坐标数据的大小//vertices是指向顶点坐标数据的指针(数组名是第一个元素的地址)glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);//将顶点颜色数据复制到当前绑定的顶点缓冲区对象的后半部分。//GL_ARRAY_BUFFER指定了目标缓冲区类型。//sizeof(vertices)表示数据复制的起始偏移量,即从缓冲区的中间位置(顶点坐标数据之后)开始复制。//sizeof(colors)指定了要复制的数据大小,即顶点颜色数据的大小。//colors是指向顶点颜色数据的指针(数组名是第一个元素的地址)glBufferSubData(GL_ARRAY_BUFFER, sizeof(vertices), sizeof(colors), colors);//将指定的缓冲区对象绑定到目标缓冲区类型GL_ELEMENT_ARRAY_BUFFER。//buffer[INDICES]是之前通过glGenBuffers生成的缓冲区对象ID之一。//绑定后,所有对GL_ELEMENT_ARRAY_BUFFER的操作都会影响到这个缓冲区对象glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer[INDICES]);//buffer[INDICES]顺序存储顶点(包含顶点坐标和颜色)的下标//当前绑定到GL_ELEMENT_ARRAY_BUFFER的缓冲区对象分配存储空间并初始化数据。//GL_ELEMENT_ARRAY_BUFFER指定了目标缓冲区类型。//sizeof(stripIndices)指定了要复制的数据大小,即索引数据的大小。//stripIndices是指向索引数据的指针。//最后一个参数GL_STATIC_DRAW是一个提示,告诉OpenGL我们打算如何使用这个数据。GL_STATIC_DRAW表示数据将不会被频繁更改,主要用于静态绘制glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(stripIndices), stripIndices, GL_STATIC_DRAW);// 启用顶点数组和颜色数组//启用顶点数组功能。启用后,OpenGL将使用顶点数组中的数据进行绘制。GL_VERTEX_ARRAY指定了要启用的顶点数组类型,即顶点坐标数组glEnableClientState(GL_VERTEX_ARRAY);//启用颜色数组功能。启用后,OpenGL将使用颜色数组中的数据进行绘制。GL_COLOR_ARRAY指定了要启用的顶点数组类型,即顶点颜色数组glEnableClientState(GL_COLOR_ARRAY);//指定顶点指针,指向顶点数据的起始位置//3表示每个顶点有3个分量(x, y, z)。//GL_FLOAT表示每个分量的数据类型是浮点数。//0表示顶点之间的间隔(步幅),这里为0表示顶点数据是紧密排列的。//最后一个0表示顶点数据在缓冲区中的起始位置偏移量。glVertexPointer(3, GL_FLOAT, 0, 0);//指定颜色指针,指向颜色数据的起始位置//3表示每个颜色有3个分量(r, g, b)。//GL_FLOAT表示每个分量的数据类型是浮点数。//0表示颜色之间的间隔(步幅),这里为0表示颜色数据是紧密排列的。//(void *)(sizeof(vertices))表示颜色数据在缓冲区中的起始位置偏移量,这里偏移量为sizeof(vertices),即顶点数据之后的位置。glColorPointer(3, GL_FLOAT, 0, (void *)(sizeof(vertices)));// 设置定时器函数,每5毫秒调用一次animate函数glutTimerFunc(5, animate, 1);
}

3.3 Vertex Array Objects

顶点数组对象:封装了一组定义对象顶点数组的调用。

一个有许多对象的繁忙场景,每个对象都用自己的顶点数组编码,可能也在VBO中,可能需要在这些数组和数组集之间多次切换,导致诸如glBindBuffer()和glVertexPointer()之类的调用激增。
从3.0版本开始,OpenGL提供了一种简洁的机制来处理这个问题:顶点数组对象(VAO)是一个容器,用于保存指定一个或多个顶点数组的所有调用。因此,一旦所有这些指定特定对象顶点数组的调用都与VAO相关联,只需要在绘制对象之前激活该VAO;换句话说,可以认为VAO封装了与对象相关的存储状态

VBO和VAO的区别

VBO(顶点缓冲对象):
VBO是一个缓冲区对象,用于存储顶点数据(如顶点坐标、颜色、法线、纹理坐标等)。
• VBO将顶点数据从CPU内存传输到GPU内存,以便GPU可以高效地访问这些数据进行渲染。
• 可以通过glGenBuffers生成VBO,通过glBindBuffer绑定VBO,通过glBufferData或glBufferSubData将数据传输到VBO

VAO(顶点数组对象):
• VAO是一个对象,用于存储与顶点数组相关的状态,包括VBO的绑定状态和顶点属性指针。
• VAO本身不存储顶点数据,而是存储顶点数据的布局和格式信息
• 通过VAO,可以一次性绑定所有相关的VBO和顶点属性指针,简化了渲染时的状态设置。
• 可以通过glGenVertexArrays生成VAO,通过glBindVertexArray绑定VAO。

• VAO管理和记录VBO的绑定状态和顶点属性指针。
• 当绑定一个VAO时,所有对VBO的绑定和顶点属性指针的设置都会记录在该VAO中。
通过绑定不同的VAO,可以快速切换不同的顶点数据和布局,而无需重新设置VBO和顶点属性指针


例子:绘制一个环形(Annulus)和一个三角形(Triangle)

与VAO相关的代码

#define VERTICES 0
#define INDICES 1
#define ANNULUS 0
#define TRIANGLE 1// Begin globals.
// Vertex co-ordinate vectors for the annulus.环
static float vertices1[] =
{30.0, 30.0, 0.0,10.0, 10.0, 0.0,70.0, 30.0, 0.0,90.0, 10.0, 0.0,70.0, 70.0, 0.0,90.0, 90.0, 0.0,30.0, 70.0, 0.0,10.0, 90.0, 0.0
};// Vertex color vectors for the annulus.环
static float colors1[] =
{0.0, 0.0, 0.0,1.0, 0.0, 0.0,0.0, 1.0, 0.0,0.0, 0.0, 1.0,1.0, 1.0, 0.0,1.0, 0.0, 1.0,0.0, 1.0, 1.0,1.0, 0.0, 0.0
};// Vertex co-ordinate vectors for the triangle.三角形
static float vertices2[] =
{40.0, 40.0, 0.0,60.0, 40.0, 0.0,60.0, 60.0, 0.0
};// Vertex color vectors for the triangle.三角形
static float colors2[] =
{0.0, 1.0, 1.0,1.0, 0.0, 0.0,0.0, 1.0, 0.0
};// Triangle strip vertex indices in order.
static unsigned int stripIndices[] = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1 };static unsigned int buffer[2]; // Array of buffer ids.static unsigned int vao[2]; // Array of VAO ids.
// End globals.// Drawing routine.
void drawScene(void)
{glClear(GL_COLOR_BUFFER_BIT);// Draw annulus.//绑定存储环形顶点数组和缓冲区的VAO。glBindVertexArray(vao[ANNULUS]);//使用顶点索引绘制环形//GL_TRIANGLE_STRIP表示使用三角形条带绘制,//10表示绘制10个顶点,GL_UNSIGNED_INT表示索引数据类型为无符号整数,0表示索引缓冲区的起始位置glDrawElements(GL_TRIANGLE_STRIP, 10, GL_UNSIGNED_INT, 0);// Draw triangle.//绑定存储三角形顶点数组和缓冲区的VAO。glBindVertexArray(vao[TRIANGLE]);//直接绘制三角形//GL_TRIANGLES表示使用三角形绘制,0表示从第一个顶点开始,3表示绘制3个顶点。glDrawArrays(GL_TRIANGLES, 0, 3);glFlush();
}// 初始化例程。
// Initialization routine.
//1.设置清除颜色。
//2.生成和绑定VAO。
//3.生成和绑定VBO。
//4.将顶点和颜色数据复制到VBO。
//5.启用顶点数组并指定顶点和颜色指针
void setup(void)
{// 设置清除颜色为白色。glClearColor(1.0, 1.0, 1.0, 0.0);// 生成两个VAO id。glGenVertexArrays(2, vao);// 绑定VAO id vao[ANNULUS],以下的顶点数组调用将应用于此VAO。glBindVertexArray(vao[ANNULUS]);//存储环形的顶点数组和缓冲区的定义调用。// 生成两个缓冲区对象。glGenBuffers(2, buffer);// 绑定顶点缓冲区并预留空间。//将指定的缓冲区对象绑定到目标缓冲区类型//目标缓冲区类型GL_ARRAY_BUFFER//buffer[VERTICES]是之前通过glGenBuffers生成的缓冲区对象ID之一glBindBuffer(GL_ARRAY_BUFFER, buffer[VERTICES]);//buffer[VERTICES]存储环形的顶点坐标和颜色//为当前绑定到GL_ARRAY_BUFFER的缓冲区对象分配存储空间//sizeof(vertices) + sizeof(colors)指定了缓冲区的大小,即顶点数据和颜色数据的总和//第三个参数NULL表示我们只预留空间,不立即提供数据//GL_STATIC_DRAW是一个提示,告诉OpenGL我们打算如何使用这个数据。GL_STATIC_DRAW表示数据将不会被频繁更改,主要用于静态绘制glBufferData(GL_ARRAY_BUFFER, sizeof(vertices1) + sizeof(colors1), NULL, GL_STATIC_DRAW);//将顶点坐标数据复制到当前绑定的顶点缓冲区对象的前半部分//GL_ARRAY_BUFFER指定了目标缓冲区类型//0表示数据复制的起始偏移量,即从缓冲区的起始位置开始复制//sizeof(vertices)指定了要复制的数据大小,即顶点坐标数据的大小//vertices是指向顶点坐标数据的指针(数组名是第一个元素的地址)glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices1), vertices1);// 将顶点颜色数据复制到顶点缓冲区的后半部分。glBufferSubData(GL_ARRAY_BUFFER, sizeof(vertices1), sizeof(colors1), colors1);// 绑定并填充索引缓冲区。glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer[INDICES]);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(stripIndices), stripIndices, GL_STATIC_DRAW);// 启用两个顶点数组:坐标和颜色。glEnableClientState(GL_VERTEX_ARRAY);glEnableClientState(GL_COLOR_ARRAY);// 指定顶点和颜色指针到相应数据的起始位置。glVertexPointer(3, GL_FLOAT, 0, 0);glColorPointer(3, GL_FLOAT, 0, (void*)(sizeof(vertices1)));// 结束绑定VAO id vao[ANNULUS]。//=======================================================// 绑定VAO id vao[TRIANGLE],以下的顶点数组调用将应用于此VAO。glBindVertexArray(vao[TRIANGLE]);//存储三角形的顶点数组和缓冲区的定义调用。// 生成一个缓冲区对象。glGenBuffers(1, buffer);// 绑定顶点缓冲区并预留空间。glBindBuffer(GL_ARRAY_BUFFER, buffer[VERTICES]);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2) + sizeof(colors2), NULL, GL_STATIC_DRAW);// 将顶点坐标数据复制到顶点缓冲区的前半部分。glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices2), vertices2);// 将顶点颜色数据复制到顶点缓冲区的后半部分。glBufferSubData(GL_ARRAY_BUFFER, sizeof(vertices2), sizeof(colors2), colors2);// 启用两个顶点数组:坐标和颜色。glEnableClientState(GL_VERTEX_ARRAY);glEnableClientState(GL_COLOR_ARRAY);//指定顶点指针,指向顶点数据的起始位置//3表示每个顶点有3个分量(x, y, z)。//GL_FLOAT表示每个分量的数据类型是浮点数。//0表示顶点之间的间隔(步幅),这里为0表示顶点数据是紧密排列的。//最后一个0表示顶点数据在缓冲区中的起始位置偏移量。glVertexPointer(3, GL_FLOAT, 0, 0);//指定颜色指针,指向颜色数据的起始位置//3表示每个颜色有3个分量(r, g, b)。//GL_FLOAT表示每个分量的数据类型是浮点数。//0表示颜色之间的间隔(步幅),这里为0表示颜色数据是紧密排列的。//(void *)(sizeof(vertices))表示颜色数据在缓冲区中的起始位置偏移量,这里偏移量为sizeof(vertices),即顶点数据之后的位置。glColorPointer(3, GL_FLOAT, 0, (void*)(sizeof(vertices2)));// 结束绑定VAO id vao[TRIANGLE]。
}

3.4 Display Lists

显示列表:用于存储经常调用的代码片段的宏

在OpenGL中,显示列表(Display List)是一种优化绘图性能的机制。它允许你将一系列OpenGL命令预先编译并存储起来,然后在需要时通过调用显示列表来执行这些命令。显示列表的主要作用包括:

  1. 性能优化:通过将一系列绘图命令预先编译并存储在显示列表中,可以减少在每次绘图时解析和执行这些命令的开销,从而提高绘图性能。
  2. 代码简化:使用显示列表可以简化代码结构,因为你可以将复杂的绘图命令封装在一个显示列表中,然后通过简单的调用来执行这些命令。
  3. 减少冗余操作:对于需要重复绘制的图形对象,使用显示列表可以避免每次绘制时重复发送相同的命令,从而减少冗余操作

例子:绘制多个螺旋

与显示列表相关的代码

// Globals.
//一个全局变量,用于存储显示列表的索引
static unsigned int aHelix; // List index.// Initialization routine.
void setup(void)
{float t; // Angle parameter.//生成一个显示列表索引aHelix = glGenLists(1); //列表的索引,一个非零无符号整数// Begin create a display list.//开始创建显示列表glNewList(aHelix, GL_COMPILE);// Draw a helix.glBegin(GL_LINE_STRIP);for (t = -10 * M_PI; t <= 10 * M_PI; t += M_PI / 20.0)glVertex3f(20 * cos(t), 20 * sin(t), t);glEnd();glEndList();// End create a display list.glClearColor(1.0, 1.0, 1.0, 0.0);
}// Drawing routine.
void drawScene(void)
{glClear(GL_COLOR_BUFFER_BIT);glColor3f(1.0, 1.0, 1.0);//使用不同的颜色和变换矩阵绘制多个螺旋线//每个螺旋线通过 glCallList(aHelix); 调用显示列表来绘制glColor3f(1.0, 0.0, 0.0);//用于保存当前的变换矩阵。glPushMatrix();glTranslatef(0.0, 0.0, -70.0);//平移glCallList(aHelix); // Execute display list.//用于恢复当前的变换矩阵。glPopMatrix();glColor3f(0.0, 1.0, 0.0);glPushMatrix();glTranslatef(30.0, 0.0, -70.0);glScalef(0.5, 0.5, 0.5);//缩放glCallList(aHelix); // Execute display list.glPopMatrix();glColor3f(0.0, 0.0, 1.0);glPushMatrix();glTranslatef(-25.0, 0.0, -70.0);glRotatef(90.0, 0.0, 1.0, 0.0);//旋转glCallList(aHelix); // Execute display list.glPopMatrix();glColor3f(1.0, 1.0, 0.0);glPushMatrix();glTranslatef(0.0, -20.0, -70.0);glRotatef(90.0, 0.0, 0.0, 1.0);glCallList(aHelix); // Execute display list.glPopMatrix();glColor3f(1.0, 0.0, 1.0);glPushMatrix();glTranslatef(-40.0, 40.0, -70.0);glScalef(0.5, 0.5, 0.5);glCallList(aHelix); // Execute display list.glPopMatrix();glColor3f(0.0, 1.0, 1.0);glPushMatrix();glTranslatef(30.0, 30.0, -70.0);glRotatef(90.0, 1.0, 0.0, 0.0);glCallList(aHelix); // Execute display list.glPopMatrix();glFlush();
}

3.5 Drawing Text

绘制文本

图形文本可以有两种类型:位图(也称为栅格)和笔画(也称为矢量)。
位图文本中的字符是用矩形块中的位元创建的,而笔画文本中的字符是用直线基元创建的。
FreeGLUT库支持位图字符和笔画字符。调用glutBitmapCharacter(*font, character)和glutStrokeCharacter(*font, character)以指定的字体渲染字符。

3.6 Programming the Mouse

编程鼠标按钮点击,转动滚轮和鼠标运动。

// Mouse callback routine.
void mouseControl(int button, int state, int x, int y)
{if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)// Store the clicked point in the points array after correcting from event to OpenGL co-ordinates.points.push_back( Point(x, height - y, pointSize) );if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) exit(0);glutPostRedisplay();
}

3.7 Programming Non-ASCII Keys

编程非ascii键
非ASCII键是指那些在标准ASCII字符集(American Standard Code for Information Interchange)之外的键。标准ASCII字符集包含128个字符,包括常见的英文字母(大小写)、数字、标点符号和一些控制字符(如换行、回车等)。非ASCII键则包括:

  1. 扩展ASCII字符:这些字符的编码范围是128到255,包含一些特殊符号、外语字符等。
  2. 功能键:如F1到F12键、方向键、Home、End、Page Up、Page Down等。
  3. 修饰键:如Shift、Ctrl、Alt、Caps Lock等。
  4. 其他特殊键:如Insert、Delete、Print Screen、Pause/Break等。
    在处理键盘输入时,非ASCII键通常需要通过特定的键盘事件处理函数来捕获和处理,而不是通过简单的字符输入处理函数

在迄今为止的各种程序中,我们已经通过键盘输入与OpenGL窗口进行交互,方法是通过调用glutKeyboardFunc(keyboard_handling_func)在主例程中注册一个处理函数keyboard_handling_func()。要与非ascii键(如箭头键、F键和上下翻页键)交互,同样需要通过调用glutSpecialFunc(special_key_handling_func)注册一个处理函数special_key_handling_func()。

例子:定义方向键对应的特定事件

// Callback routine for non-ASCII key entry.
void specialKeyInput(int key, int x, int y)
{if (key == GLUT_KEY_UP) Yvalue += 0.1;if (key == GLUT_KEY_DOWN) Yvalue -= 0.1;if (key == GLUT_KEY_LEFT) Xvalue -= 0.1;if (key == GLUT_KEY_RIGHT) Xvalue += 0.1;glutPostRedisplay();
}

3.8 Menus

编程弹出菜单


与弹出菜单相关的代码

// The top menu callback function.
void top_menu(int id)
{if (id == 1) exit(0);
}// The sub-menu callback function.
void color_menu(int id)
{if (id == 2){square_color[0] = 1.0; square_color[1] = 0.0; square_color[2] = 0.0;}if (id == 3){square_color[0] = 0.0; square_color[1] = 0.0; square_color[2] = 1.0;}glutPostRedisplay();
}// Routine to make the menu.
void makeMenu(void)
{// The sub-menu is created first (because it should be visible when the top// menu is created): its callback function is registered and menu entries added.int sub_menu;sub_menu = glutCreateMenu(color_menu);glutAddMenuEntry("Red", 2);glutAddMenuEntry("Blue", 3);// The top menu is created: its callback function is registered and menu entries,// including a submenu, added.glutCreateMenu(top_menu);glutAddSubMenu("Color", sub_menu);glutAddMenuEntry("Quit", 1);// The menu is attached to a mouse button.glutAttachMenu(GLUT_RIGHT_BUTTON);
}

3.9 Line Stipples

线条点画:将图案应用到线条上。



与linestipple相关的代码

// Globals.
static int stippleID = 0; // Stipple ID.
// Drawing routine.
void drawScene(void)
{glClear(GL_COLOR_BUFFER_BIT);glColor3f(0.0, 0.0, 0.0);glEnable(GL_LINE_STIPPLE); // Enable line stippling.// Speficy the stipple pattern and write a label.glRasterPos3f(30.0, 40.0, 0.0);switch (stippleID){case 0:glDisable(GL_LINE_STIPPLE);writeBitmapString((void*)font, "No stipple");break;case 1:glLineStipple(1, 0x5555);writeBitmapString((void*)font, "glLineStipple(1, 0x5555)");break;case 2:glLineStipple(1, 0x0101);writeBitmapString((void*)font, "glLineStipple(1, 0x0101)");break;case 3:glLineStipple(1, 0x00FF);writeBitmapString((void*)font, "glLineStipple(1, 0x00FF)");break;case 4:glLineStipple(5, 0x5555);writeBitmapString((void*)font, "glLineStipple(5, 0x5555)");break;default:break;}// Draw one straight line segment.glBegin(GL_LINES);glVertex3f(25.0, 50.0, 0.0);glVertex3f(75.0, 50.0, 0.0);glEnd();glDisable(GL_LINE_STIPPLE); // Disable line stippling.glFlush();
}// Keyboard input processing routine.
void keyInput(unsigned char key, int x, int y)
{switch (key){case ' ': //输入空格if (stippleID < 4) stippleID++;else stippleID = 0;glutPostRedisplay();break;case 27:exit(0);break;default:break;}
}

3.10 FreeGLUT Objects

FreeGLUT对象:现成的库对象

在FreeGLUT库中,solid和wireframe是指对象的渲染模式。具体来说:
• Solid(实心):对象以实心方式渲染,表面是填充的,看起来是一个完整的三维形状。
• Wireframe(线框):对象以线框方式渲染,只显示对象的边缘线,看起来像是一个三维的网格。
FreeGLUT提供了一些函数来绘制常见的三维对象,这些对象可以以实心或线框方式渲染



// Drawing routine.
void drawScene()
{glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glLoadIdentity();// Position the objects for viewing.gluLookAt(0.0, 3.0, 12.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);glLineWidth(2.0); // Thicken the wireframes.// Commands to turn the objects.glPushMatrix();glRotatef(Zangle, 0.0, 0.0, 1.0);glRotatef(Yangle, 0.0, 1.0, 0.0);glRotatef(Xangle, 1.0, 0.0, 0.0);// Draw objects.switch (objID){case 1:glutSolidSphere(5.0, 40, 40);objName = "Solid Sphere";break;case 2:glutWireSphere(5.0, 40, 40);objName = "Wire Sphere";break;case 3:glutSolidCube(7.0);objName = "Solid Cube";break;case 4:glutWireCube(7.0);objName = "Wire Cube";break;case 5:glutSolidCone(3.0, 8.0, 30, 30);objName = "Solid Cone";break;case 6:glutWireCone(3.0, 8.0, 30, 30);objName = "Wire Cone";break;case 7:glutSolidTorus(1.0, 4.0, 30, 30);objName = "Solid Torus";break;case 8:glutWireTorus(1.0, 4.0, 30, 30);objName = "Wire Torus";break;case 9:glScalef(3.0, 3.0, 3.0);glutSolidDodecahedron();objName = "Solid Dodecahedron";break;case 10:glScalef(3.0, 3.0, 3.0);glutWireDodecahedron();objName = "Wire Dodecahedron";break;case 11:glScalef(5.0, 5.0, 5.0);glutSolidOctahedron();objName = "Solid Octahecron";break;case 12:glScalef(5.0, 5.0, 5.0);glutWireOctahedron();objName = "Wire Octahedron";break;case 13:glScalef(6.0, 6.0, 6.0);glutSolidTetrahedron();objName = "Solid Tetrahedron";break;case 14:glScalef(6.0, 6.0, 6.0);glutWireTetrahedron();objName = "Wire Tetrahedron";break;case 15:glScalef(5.0, 5.0, 5.0);glutSolidIcosahedron();objName = "Solid Icosahedron";break;case 16:glScalef(5.0, 5.0, 5.0);glutWireIcosahedron();objName = "Wire Icosahedron";break;case 17:glutSolidTeapot(4.0);objName = "Solid Teapot";break;case 18:glutWireTeapot(4.0);objName = "Wire Teapot";break;default:break;}glPopMatrix();// Write label after disabling lighting.glDisable(GL_LIGHTING);glColor3f(0.0, 0.0, 0.0);writeObjectName();glEnable(GL_LIGHTING);glutSwapBuffers();
}// Initialization routine.
void setup(void)
{// Material property vectors.float matSpec[] = { 0.0, 1.0, 1.0, 1.0 };float matShine[] = { 50.0 };float matAmbAndDif[] = { 0.0, 0.1, 1.0, 1.0 };// Light property vectors.float lightAmb[] = { 0.0, 0.1, 1.0, 1.0 };float lightDifAndSpec[] = { 0.0, 0.1, 1.0, 1.0 };float lightPos[] = { 0.0, 7.0, 3.0, 0.0 };float globAmb[] = { 0.2, 0.2, 0.2, 1.0 };// Material properties of the objects.glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, matSpec);glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, matShine);glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, matAmbAndDif);// Light0 properties.glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmb);glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDifAndSpec);glLightfv(GL_LIGHT0, GL_SPECULAR, lightDifAndSpec);glLightfv(GL_LIGHT0, GL_POSITION, lightPos);// Poperties of the ambient light.glLightModelfv(GL_LIGHT_MODEL_AMBIENT, globAmb); // Global ambient light.glEnable(GL_LIGHTING); // Enable lighting calculations.glEnable(GL_LIGHT0); // Enable particular light source.glEnable(GL_DEPTH_TEST); // Enable depth testing.glEnable(GL_NORMALIZE); // Enable automatic normalization of normals.glClearColor(1.0, 1.0, 1.0, 0.0);
}

3.11 Clipping Planes

裁剪平面:除了捆绑观看盒或视锥台的自动六个平面外,用来裁剪场景的平面。程序员可以指定额外的裁剪平面。

透视投影中视锥体的六个裁剪平面


指定裁剪平面

glEnale的话 <0的部分被裁剪
glDisable的话 >=的部分被裁剪

// Drawing routine.
void drawScene(void)
{float angle;int i;double eqn0[4] = { 0.0, 0.0, -1.0, 0.25 }; // Data for clipping plane 0.//Ax+By+Cz+Ddouble eqn1[4] = { 1.0, 0.5, 0.0, -60.0 }; // Data for clipping plane 1.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glPolygonMode(GL_FRONT, GL_FILL);glClipPlane(GL_CLIP_PLANE0, eqn0); // Specify clipping plane 0.glClipPlane(GL_CLIP_PLANE1, eqn1); // Specify clipping plane 1.if (isClip0) glEnable(GL_CLIP_PLANE0); // Clip points s.t. z > 0.25.else glDisable(GL_CLIP_PLANE0);//glEnale的话 <0的部分被裁剪//glDisable的话 >=的部分被裁剪if (isClip1) glEnable(GL_CLIP_PLANE1); // Clip points s.t. x > 75.0.else glDisable(GL_CLIP_PLANE1);// Upper left circular annulus: the white disc overwrites the red disc.glColor3f(1.0, 0.0, 0.0);drawDisc(20.0, 25.0, 75.0, 0.0);glColor3f(1.0, 1.0, 1.0);drawDisc(10.0, 25.0, 75.0, 0.0);// Upper right circular annulus: the white disc floats above the red disc blocking it.glEnable(GL_DEPTH_TEST); // Enable depth testing. glColor3f(1.0, 0.0, 0.0);drawDisc(20.0, 75.0, 75.0, 0.0);glColor3f(1.0, 1.0, 1.0);drawDisc(10.0, 75.0, 75.0, 0.5); // Compare this z-value with that of the red disc.glDisable(GL_DEPTH_TEST); // Disable depth testing.// Lower circular annulus: with a true hole.if (isWire) glPolygonMode(GL_FRONT, GL_LINE);else glPolygonMode(GL_FRONT, GL_FILL);glColor3f(1.0, 0.0, 0.0);glBegin(GL_TRIANGLE_STRIP);for (i = 0; i <= N; ++i){angle = 2 * M_PI * i / N;glVertex3f(50 + cos(angle) * 10.0, 30 + sin(angle) * 10.0, 0.0);glVertex3f(50 + cos(angle) * 20.0, 30 + sin(angle) * 20.0, 0.0);}glEnd();// Write labels.glColor3f(0.0, 0.0, 0.0);glRasterPos3f(15.0, 51.0, 0.0);writeBitmapString((void*)font, "Overwritten");glRasterPos3f(69.0, 51.0, 0.0);writeBitmapString((void*)font, "Floating");glRasterPos3f(38.0, 6.0, 0.0);writeBitmapString((void*)font, "The real deal!");glFlush();
}

3.12 Frustum,Differently

Frustum,不同的是:gluPerspective()比glFrustum()更直观地指定了一个查看的截锥体,并且参数更少。

参数fovy,称为视场角,是沿yz平面在金字塔顶端的夹角(截锥体是截尾);
纵横比aspect是aspect=截锥体正面的宽度width/高度height;

glFrustum(left, right, bottom, top, near, far)
请注意 OpenGL 的一个小怪癖,即 near 和 far 值在符号上颠倒。
给函数glFrustum输入near,far为正数,实际上为 z = − near , z = − far \text{z}=-\text{near}, \text{z}=-\text{far} z=near,z=far

截锥体的八个顶点关于z轴对称,即 left = − right \text{left} = -\text{right} left=right bottom = − top \text{bottom} =-\text{top} bottom=top

gluPerspective(fovy, aspect, near, far)


width = right − left height = top − bottom aspect = width / height tan ⁡ fovy 2 = top |near| \text{width}=\text{right}-\text{left}\\ ~\\ \text{height}=\text{top}-\text{bottom}\\ ~\\ \text{aspect}=\text{width}/\text{height}\\ ~\\ \tan\frac{\text{fovy}}{2}=\frac{\text{top}}{\text{|near|}} width=rightleft height=topbottom aspect=width/height tan2fovy=|near|top

假设给定glFrustum(-5.0, 5.0,-5.0, 5.0, 5.0, 100.0);
计算gluPerspective(fovy, aspect, near, far) 中fovy和aspect

glFrustum(left, right, bottom, top, near, far)
left=-5、right=5、bottom=-5、top=5、near=5、far=100
width = right - left = 5-(-5) = 10、height = top - bottom = 5-(-5) = 10
aspect = width / height = 10 / 10 = 1
tan(fovy/2) = top/near= 5/5 =1
fovy/2=45°,fovy=90°

3.13 Viewports

视口:OpenGL窗口中渲染绘图的特定部分。

场景的视区是 OpenGL 窗口中绘制场景的区域。默认情况下,它是整个窗口。但是,glViewPort() 调用可用于绘制到较小的矩形子区域。

调用 glViewport(x, y, w, h) 将视区指定为 OpenGL 窗口的矩形子区域,该窗口的左下角位于点 (x, y) 处,宽度为 w,高度为 h。单位为像素,OpenGL 窗口中的坐标为:原点位于左下角,x 轴的增加方向为向右,y 轴的增加方向为向上。

3.14 Multiple Windows

多窗口:多个顶级OpenGL窗口

FreeGLUT 库的 glutCreateWindow() 调用可以在主例程中多次调用,以创建多个顶级 OpenGL 窗口。每个顶级窗口的 display routine、resize 例程等属性可以独立指定。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com