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);

    // 生成两个缓冲区对象的ID
    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(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+D
	double 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 例程等属性可以独立指定。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/959584.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

GCC之编译(8)AR打包命令

GCC之(8)AR二进制打包命令 Author: Once Day Date: 2025年1月23日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章请查看专栏: Linux实践记录_Once-Day的博客-C…

【miniconda】:langraph的windows构建

langraph需要python3.11 langraph强烈建议使用py3.11 默认是3.12 官方 下载仓库 下载老版本的python (后续发现新版miniconda也能安装老版本的python) 在这里

微信小程序中常见的 跳转方式 及其特点的表格总结(wx.navigateTo 适合需要返回上一页的场景)

文章目录 详细说明总结wx.navigateTo 的特点为什么 wx.navigateTo 最常用&#xff1f;其他跳转方式的使用频率总结 以下是微信小程序中常见的跳转方式及其特点的表格总结&#xff1a; 跳转方式API 方法特点适用场景wx.navigateTowx.navigateTo({ url: 路径 })保留当前页面&…

python3+TensorFlow 2.x(四)反向传播

目录 反向传播算法 反向传播算法基本步骤&#xff1a; 反向中的参数变化 总结 反向传播算法 反向传播算法&#xff08;Backpropagation&#xff09;是训练人工神经网络时使用的一个重要算法&#xff0c;它是通过计算梯度并优化神经网络的权重来最小化误差。反向传播算法的核…

深度学习 Pytorch 单层神经网络

神经网络是模仿人类大脑结构所构建的算法&#xff0c;在人脑里&#xff0c;我们有轴突连接神经元&#xff0c;在算法中&#xff0c;我们用圆表示神经元&#xff0c;用线表示神经元之间的连接&#xff0c;数据从神经网络的左侧输入&#xff0c;让神经元处理之后&#xff0c;从右…

使用vscode + Roo Code (prev. Roo Cline)+DeepSeek-R1使用一句话需求做了个实验

摘要 使用vscode、Roo Code和deepseek-reasoner进行了一个实验&#xff0c;尝试使用一句话需求来生成小红书封面图片。工具根据需求提供了详细的架构方案&#xff0c;包括技术栈选择、核心模块划分、目录结构建议等。然后&#xff0c;工具自动化地完成了开发和测试&#xff0c;…

微服务搭建----springboot接入Nacos2.x

springboot接入Nacos2.x nacos之前用的版本是1.0的&#xff0c;现在重新搭建一个2.0版本的&#xff0c;学如逆水行舟&#xff0c;不进则退&#xff0c;废话不多说&#xff0c;开搞 1、 nacos2.x搭建 1&#xff0c;首先第一步查询下项目之间的版本对照&#xff0c;不然后期会…

TDengine 与上海电气工业互联网平台完成兼容性认证

在工业数字化转型和智能化升级的浪潮中&#xff0c;企业对高效、可靠的数据管理解决方案的需求日益增长。特别是在风电智能运维、火电远程运维、机床售后服务等复杂多样的工业场景下&#xff0c;如何实现海量设备和时序数据的高效管理&#xff0c;已经成为推动行业升级的关键。…

“大模型横扫千军”背后的大数据挖掘--浅谈MapReduce

文章目录 O 背景知识1 数据挖掘2 邦费罗尼原则3 TF.IDF4 哈希函数5 分布式文件系统 一、MapReduce基本介绍1. Map 任务2. 按键分组3. Reduce 任务4. 节点失效处理5.小测验&#xff1a;在一个大型语料库上有100个map任务和若干reduce任务&#xff1a; 二、基于MapReduce的基本运…

25美赛ABCDEF题详细建模过程+可视化图表+参考论文+写作模版+数据预处理

详情见该链接&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 25美国大学生数学建模如何准备&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;-CSDN博客文章浏览阅读791次&#xff0c;点赞13次&#xff0c;收藏7次。通过了解比赛基本…

Python:元组构造式和字典推导式

&#xff08;Python 元组构造式和字典推导式整理笔记&#xff09; 1. 元组构造式 1.1 创建元组 使用圆括号&#xff1a; tuple1 (1, 2.5, (three, four), [True, 5], False) print(tuple1) # 输出: (1, 2.5, (three, four), [True, 5], False) 省略圆括号&#xff1a; tup…

appium自动化环境搭建

一、appium介绍 appium介绍 appium是一个开源工具、支持跨平台、用于自动化ios、安卓手机和windows桌面平台上面的原生、移动web和混合应用&#xff0c;支持多种编程语言(python&#xff0c;java&#xff0c;Ruby&#xff0c;Javascript、PHP等) 原生应用和混合应用&#xf…

vue3组件el-table报错

传给table标签的data不是数组就会报错&#xff0c; 摁着商品管理代码找了半天也没发现哪里错了&#xff0c;而且关闭报错表格数据能正常显示&#xff0c; 。。。 最后发现我还有个订单管理页面&#xff0c;这里面的data初始化成ref( )了&#xff0c;把这个组件注释掉&#xf…

基于SpringBoot+WebSocket的前后端连接,并接入文心一言大模型API

前言&#xff1a; 本片博客只讲述了操作的大致流程&#xff0c;具体实现步骤并不标准&#xff0c;请以参考为准。 本文前提&#xff1a;熟悉使用webSocket 如果大家还不了解什么是WebSocket&#xff0c;可以参考我的这篇博客&#xff1a; rWebSocket 详解&#xff1a;全双工…

《边界感知的分而治之方法:基于扩散模型的无监督阴影去除解决方案》学习笔记

paper&#xff1a;Boundary-Aware Divide and Conquer: A Diffusion-Based Solution for Unsupervised Shadow Removal 目录 摘要 1、介绍 2、相关工作 2.1 阴影去除 2.2 去噪扩散概率模型&#xff08;Denoising Diffusion Probabilistic Models, DDPM&#xff09; 3、方…

linux-mysql在centos7安装和基础配置

1.安装mysql数据库 1.使用官网安装 1.检查是否存在mysql的分支mariadb [rootlocalhost ~]# rpm -qa |grep mariadb mariadb-libs-5.5.64-1.el7.x86_64 [rootlocalhost ~]# 2.卸载这个分支包 [rootlocalhost ~]# rpm -qa | grep mariadb mariadb-libs-5.5.64-1.el7.x86_64 …

Python!从0开始学爬虫:(一)HTTP协议 及 请求与响应

前言 爬虫需要基础知识&#xff0c;HTTP协议只是个开始&#xff0c;除此之外还有很多&#xff0c;我们慢慢来记录。 今天的HTTP协议&#xff0c;会有助于我们更好的了解网络。 一、什么是HTTP协议 &#xff08;1&#xff09;定义 HTTP&#xff08;超文本传输协议&#xff…

MySQL数据库笔记——最左前缀原则原理及其注意事项

大家好&#xff0c;这里是Good Note&#xff0c;关注 公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细介绍MySQL索引的关键潜规则——最左前缀原则。 文章目录 图示单值索引和联合索引单值索引联合索引 最左前缀原则示例分析1. 全值匹配查询时2. 匹配左…

Java数据结构 (链表反转(LinkedList----Leetcode206))

1. 链表的当前结构 每个方框代表一个节点&#xff0c;每个节点包含两个部分&#xff1a; 左侧的数字&#xff1a;节点存储的值&#xff0c;例如 45、34 等。右侧的地址&#xff08;如 0x90&#xff09;&#xff1a;表示该节点 next 指针指向的下一个节点的内存地址。 例子中&a…

IMX6ull项目环境配置

文件解压缩&#xff1a; .tar.gz 格式解压为 tar -zxvf .tar.bz2 格式解压为 tar -jxvf 2.4版本后的U-boot.bin移植进SD卡后&#xff0c;通过串口启动配置开发板和虚拟机网络。 setenv ipaddr 192.168.2.230 setenv ethaddr 00:04:9f:…