Compare commits

..

5 Commits

8 changed files with 332 additions and 108 deletions
+2
View File
@@ -0,0 +1,2 @@
*.dll
*.zip
+3 -3
View File
@@ -52,7 +52,7 @@ void init() { // 定义 OpenGL 初始化函数。
glMatrixMode(GL_PROJECTION); // 切换到投影矩阵模式。 glMatrixMode(GL_PROJECTION); // 切换到投影矩阵模式。
glLoadIdentity(); // 将当前投影矩阵重置为单位矩阵。 glLoadIdentity(); // 将当前投影矩阵重置为单位矩阵。
gluOrtho2D(0.0, WIDTH, 0.0, HEIGHT); // 设置二维正交投影范围。 gluOrtho2D(0.0, WIDTH, 0.0, HEIGHT); // 设置二维正交投影范围。
} // 初始化函数结束。 }
int main(int argc, char** argv) { // 程序入口函数。 int main(int argc, char** argv) { // 程序入口函数。
glutInit(&argc, argv); // 初始化 GLUT。 glutInit(&argc, argv); // 初始化 GLUT。
@@ -63,5 +63,5 @@ int main(int argc, char** argv) { // 程序入口函数。
init(); // 调用初始化函数。 init(); // 调用初始化函数。
glutDisplayFunc(display); // 注册显示回调函数。 glutDisplayFunc(display); // 注册显示回调函数。
glutMainLoop(); // 进入 GLUT 事件循环。 glutMainLoop(); // 进入 GLUT 事件循环。
return 0; // 程序正常结束。 return 0;
} // 主函数结束。 }
+85 -105
View File
@@ -1,43 +1,56 @@
#include <GL/glut.h> // 引入 GLUT/OpenGL 头文件,提供窗口和绘图函数。 #include <GL/glut.h> // 引入 OpenGL 头文件,提供窗口和绘图函数。
#include <iostream> // 引入输入输出流库,便于需要时输出调试信息。
// 空行:分隔头文件和区域编码定义。 // 定义区域编码
// --- 定义区域编码 ---
#define INSIDE 0 // 0000,表示点在裁剪窗口内部。 #define INSIDE 0 // 0000,表示点在裁剪窗口内部。
#define LEFT 1 // 0001,表示点在裁剪窗口左侧。 #define LEFT 1 // 0001,表示点在裁剪窗口左侧。
#define RIGHT 2 // 0010,表示点在裁剪窗口右侧。 #define RIGHT 2 // 0010,表示点在裁剪窗口右侧。
#define BOTTOM 4 // 0100,表示点在裁剪窗口下方。 #define BOTTOM 4 // 0100,表示点在裁剪窗口下方。
#define TOP 8 // 1000,表示点在裁剪窗口上方。 #define TOP 8 // 1000,表示点在裁剪窗口上方。
// 空行:分隔宏定义和结构体定义。
// --- 定义裁剪窗口结构 ---
struct ClipWindow { // 定义用于保存裁剪窗口边界的结构体。 struct ClipWindow { // 定义用于保存裁剪窗口边界的结构体。
float xmin, xmax, ymin, ymax; // 保存裁剪窗口的左、右、下、上边界。 float xmin, xmax, ymin, ymax; // 保存裁剪窗口的左、右、下、上边界。
}; // 结构体定义结束。 }; // 结构体定义结束。
// 空行:分隔结构体和全局变量。
// --- 全局变量设置 --- struct Point { // 定义二维点结构体。
float x, y; // 保存点的横坐标和纵坐标。
}; // 点结构体定义结束。
ClipWindow rect; // 定义全局裁剪窗口对象。 ClipWindow rect; // 定义全局裁剪窗口对象。
int x_0, y_0, x_1, y_1; // 定义线段端点坐标变量 Point lineStart = {60, 80}; // 定义原始线段起点
// 空行:分隔全局变量和函数说明 Point lineEnd = {520, 360}; // 定义原始线段终点
/** // 文档注释开始。
* 计算点 (x, y) 的区域编码 // 说明 computeCode 函数的作用 int computeCode(Point p, ClipWindow rect) { // 根据点坐标和裁剪窗口计算区域编码
*/ // 文档注释结束。
int computeCode(float x, float y, ClipWindow rect) { // 根据点坐标和裁剪窗口计算区域编码。
int code = INSIDE; // 初始认为点在窗口内部。 int code = INSIDE; // 初始认为点在窗口内部。
if (x < rect.xmin) code |= LEFT; // 如果点在左边界外,则添加 LEFT 标记。 if (p.x < rect.xmin) code |= LEFT; // 如果点在左边界外,则添加 LEFT 标记。
else if (x > rect.xmax) code |= RIGHT; // 如果点在右边界外,则添加 RIGHT 标记。 else if (p.x > rect.xmax) code |= RIGHT; // 如果点在右边界外,则添加 RIGHT 标记。
if (y < rect.ymin) code |= BOTTOM; // 如果点在下边界外,则添加 BOTTOM 标记。 if (p.y < rect.ymin) code |= BOTTOM; // 如果点在下边界外,则添加 BOTTOM 标记。
else if (y > rect.ymax) code |= TOP; // 如果点在上边界外,则添加 TOP 标记。 else if (p.y > rect.ymax) code |= TOP; // 如果点在上边界外,则添加 TOP 标记。
return code; // 返回最终区域编码。 return code; // 返回最终区域编码。
} // 区域编码函数结束。 }
// 空行:分隔区域编码函数和裁剪函数说明。
/** // 文档注释开始 void drawLine(Point p1, Point p2, float r, float g, float b) { // 按指定颜色绘制一条线段
* Cohen-Sutherland 算法核心 // 说明该函数执行 Cohen-Sutherland 线段裁剪 glColor3f(r, g, b); // 设置线段颜色
* 注意:这里传入坐标的引用,直接修改坐标值以得到裁剪后的结果 // 说明引用参数会被函数内部更新 glBegin(GL_LINES); // 开始绘制线段
*/ // 文档注释结束 glVertex2f(p1.x, p1.y); // 提交第一个端点
void cohenSutherlandClip(float& x_0, float& y_0, float& x_1, float& y_1, ClipWindow rect) { // 定义 Cohen-Sutherland 线段裁剪函数 glVertex2f(p2.x, p2.y); // 提交第二个端点
int code1 = computeCode(x_0, y_0, rect); // 计算第一个端点的区域编码 glEnd(); // 结束绘制
int code2 = computeCode(x_1, y_1, rect); // 计算第二个端点的区域编码。 }
void drawClipWindow(ClipWindow rect) { // 绘制裁剪窗口。
glColor3f(1.0f, 0.0f, 0.0f); // 设置绘制颜色为红色。
glBegin(GL_LINE_LOOP); // 开始绘制闭合线框。
glVertex2f(rect.xmin, rect.ymin); // 提交裁剪窗口左下角顶点。
glVertex2f(rect.xmax, rect.ymin); // 提交裁剪窗口右下角顶点。
glVertex2f(rect.xmax, rect.ymax); // 提交裁剪窗口右上角顶点。
glVertex2f(rect.xmin, rect.ymax); // 提交裁剪窗口左上角顶点。
glEnd(); // 结束闭合线框顶点提交。
}
void cohenSutherlandClip(Point p1, Point p2, ClipWindow rect) { // 定义 Cohen-Sutherland 线段裁剪函数。
int code1 = computeCode(p1, rect); // 计算第一个端点的区域编码。
int code2 = computeCode(p2, rect); // 计算第二个端点的区域编码。
bool accept = false; // 定义是否接受裁剪结果的标志,初始为 false。 bool accept = false; // 定义是否接受裁剪结果的标志,初始为 false。
// 空行:分隔初始化和裁剪循环。
while (true) { // 不断处理线段,直到确定接受或舍弃。 while (true) { // 不断处理线段,直到确定接受或舍弃。
// 如果两端点都在窗口内 (0000 | 0000 == 0)。 // 如果两端点都在窗口内 (0000 | 0000 == 0)。
if (!(code1 | code2)) { // 两个区域编码按位或为 0,说明整条线段在窗口内。 if (!(code1 | code2)) { // 两个区域编码按位或为 0,说明整条线段在窗口内。
@@ -51,103 +64,71 @@ void cohenSutherlandClip(float& x_0, float& y_0, float& x_1, float& y_1, ClipWin
// 3. 线段部分在窗口内,需要求交点。 // 3. 线段部分在窗口内,需要求交点。
else { // 线段部分可见,需要把窗口外端点裁到边界上。 else { // 线段部分可见,需要把窗口外端点裁到边界上。
int code_out; // 保存当前选中的窗口外端点区域编码。 int code_out; // 保存当前选中的窗口外端点区域编码。
float x, y; // 保存线段与裁剪边界的交点坐标。 Point crossPoint; // 保存线段与裁剪边界的交点坐标。
// 空行:分隔局部变量和外部端点选择。
// 选择窗口外的一个端点。 // 选择窗口外的一个端点。
if (code1 != 0) // 如果第一个端点在窗口外。 if (code1 != 0) // 如果第一个端点在窗口外。
code_out = code1; // 选择第一个端点作为待裁剪端点。 code_out = code1; // 选择第一个端点作为待裁剪端点。
else // 否则说明第二个端点在窗口外。 else // 否则说明第二个端点在窗口外。
code_out = code2; // 选择第二个端点作为待裁剪端点。 code_out = code2; // 选择第二个端点作为待裁剪端点。
// 空行:分隔外部端点选择和交点计算。
// 计算交点。 // 计算交点。
if (code_out & TOP) { // 如果端点在上边界外,则与上边界求交。 if (code_out & TOP) { // 如果端点在上边界外,则与上边界求交。
x = x_0 + (x_1 - x_0) * (rect.ymax - y_0) / (y_1 - y_0); // 根据直线参数方程计算交点 x 坐标。 crossPoint.x = p1.x + (p2.x - p1.x) * (rect.ymax - p1.y) / (p2.y - p1.y); // 根据直线参数方程计算交点 x 坐标。
y = rect.ymax; // 交点 y 坐标等于上边界。 crossPoint.y = rect.ymax; // 交点 y 坐标等于上边界。
} // 上边界求交结束。 } // 上边界求交结束。
else if (code_out & BOTTOM) { // 如果端点在下边界外,则与下边界求交。 else if (code_out & BOTTOM) { // 如果端点在下边界外,则与下边界求交。
x = x_0 + (x_1 - x_0) * (rect.ymin - y_0) / (y_1 - y_0); // 根据直线参数方程计算交点 x 坐标。 crossPoint.x = p1.x + (p2.x - p1.x) * (rect.ymin - p1.y) / (p2.y - p1.y); // 根据直线参数方程计算交点 x 坐标。
y = rect.ymin; // 交点 y 坐标等于下边界。 crossPoint.y = rect.ymin; // 交点 y 坐标等于下边界。
} // 下边界求交结束。 } // 下边界求交结束。
else if (code_out & RIGHT) { // 如果端点在右边界外,则与右边界求交。 else if (code_out & RIGHT) { // 如果端点在右边界外,则与右边界求交。
y = y_0 + (y_1 - y_0) * (rect.xmax - x_0) / (x_1 - x_0); // 根据直线参数方程计算交点 y 坐标。 crossPoint.y = p1.y + (p2.y - p1.y) * (rect.xmax - p1.x) / (p2.x - p1.x); // 根据直线参数方程计算交点 y 坐标。
x = rect.xmax; // 交点 x 坐标等于右边界。 crossPoint.x = rect.xmax; // 交点 x 坐标等于右边界。
} // 右边界求交结束。 } // 右边界求交结束。
else if (code_out & LEFT) { // 如果端点在左边界外,则与左边界求交。 else if (code_out & LEFT) { // 如果端点在左边界外,则与左边界求交。
y = y_0 + (y_1 - y_0) * (rect.xmin - x_0) / (x_1 - x_0); // 根据直线参数方程计算交点 y 坐标。 crossPoint.y = p1.y + (p2.y - p1.y) * (rect.xmin - p1.x) / (p2.x - p1.x); // 根据直线参数方程计算交点 y 坐标。
x = rect.xmin; // 交点 x 坐标等于左边界。 crossPoint.x = rect.xmin; // 交点 x 坐标等于左边界。
} // 左边界求交结束。 } // 左边界求交结束。
// 空行:分隔交点计算和端点替换。
// 用交点替换原来的外部端点。 // 用交点替换原来的外部端点。
if (code_out == code1) { // 如果被裁剪的是第一个端点。 if (code_out == code1) { // 如果被裁剪的是第一个端点。
x_0 = x; y_0 = y; // 用交点更新第一个端点坐标。 p1 = crossPoint; // 用交点更新第一个端点坐标。
code1 = computeCode(x_0, y_0, rect); // 重新计算第一个端点的区域编码。 code1 = computeCode(p1, rect); // 重新计算第一个端点的区域编码。
} // 第一个端点更新结束。 } // 第一个端点更新结束。
else { // 否则被裁剪的是第二个端点。 else { // 否则被裁剪的是第二个端点。
x_1 = x; y_1 = y; // 用交点更新第二个端点坐标。 p2 = crossPoint; // 用交点更新第二个端点坐标。
code2 = computeCode(x_1, y_1, rect); // 重新计算第二个端点的区域编码。 code2 = computeCode(p2, rect); // 重新计算第二个端点的区域编码。
} // 第二个端点更新结束。 }
} // 部分可见处理结束。 }
} // 裁剪循环结束。 }
// 空行:分隔裁剪循环和结果绘制。
// 如果接受,则绘制裁剪后的线段(蓝色)。 // 如果接受,则绘制裁剪后的线段(蓝色)。
if (accept) { // 如果线段最终被接受。 if (accept) { // 如果线段最终被接受。
glBegin(GL_LINES); // 开始按线段方式提交顶点 drawLine(p1, p2, 0.0f, 0.0f, 1.0f); // 绘制裁剪后的蓝色线段
glColor3f(0.0f, 0.0f, 1.0f); // 设置绘制颜色为蓝色。
glVertex2f(x_0, y_0); // 提交裁剪后线段的起点。
glVertex2f(x_1, y_1); // 提交裁剪后线段的终点。
glEnd(); // 结束线段顶点提交。
} // 接受线段绘制结束。 } // 接受线段绘制结束。
} // Cohen-Sutherland 裁剪函数结束。 }
// 空行:分隔裁剪函数和显示回调函数 // 显示回调函数
// --- 显示回调函数 ---
void myDisplay() { // 定义窗口重绘时调用的显示函数。 void myDisplay() { // 定义窗口重绘时调用的显示函数。
glClear(GL_COLOR_BUFFER_BIT); // 清除颜色缓冲区。 glClear(GL_COLOR_BUFFER_BIT); // 清除颜色缓冲区。
// 空行:分隔清屏和裁剪窗口绘制。
// 1. 绘制裁剪窗口 (红色线框) drawClipWindow(rect); // 绘制红色裁剪窗口
glColor3f(1.0f, 0.0f, 0.0f); // 设置绘制颜色为红色 drawLine(lineStart, lineEnd, 0.0f, 1.0f, 0.0f); // 绘制绿色原始线段
glBegin(GL_LINE_LOOP); // 开始绘制闭合线框。
glVertex2f(rect.xmin, rect.ymin); // 提交裁剪窗口左下角顶点 // 执行裁剪并绘制结果 (蓝色)
glVertex2f(rect.xmax, rect.ymin); // 提交裁剪窗口右下角顶点。
glVertex2f(rect.xmax, rect.ymax); // 提交裁剪窗口右上角顶点。
glVertex2f(rect.xmin, rect.ymax); // 提交裁剪窗口左上角顶点。
glEnd(); // 结束闭合线框顶点提交。
// 空行:分隔裁剪窗口绘制和原始线段绘制。
// 2. 绘制原始线段 (绿色)。
// 注意:为了演示效果,我们在调用裁剪函数前,先保存原始坐标,
// 或者在这里直接用初始值绘制。这里我们使用初始值绘制。
int orig_x_0 = 50, orig_y_0 = 350; // 定义原始线段的第一个端点坐标。
int orig_x_1 = 550, orig_y_1 = 50; // 定义原始线段的第二个端点坐标。
// 空行:分隔原始坐标定义和原始线段绘制。
glBegin(GL_LINES); // 开始按线段方式提交顶点。
glColor3f(0.0f, 1.0f, 0.0f); // 设置绘制颜色为绿色。
glVertex2f(orig_x_0, orig_y_0); // 提交原始线段起点。
glVertex2f(orig_x_1, orig_y_1); // 提交原始线段终点。
glEnd(); // 结束原始线段顶点提交。
// 空行:分隔原始线段绘制和裁剪结果绘制。
// 3. 执行裁剪并绘制结果 (蓝色)。
// 使用 float 变量传入,因为算法内部会修改它们。
float cx_0 = orig_x_0, cy_0 = orig_y_0; // 将原始起点复制为浮点变量。
float cx_1 = orig_x_1, cy_1 = orig_y_1; // 将原始终点复制为浮点变量。
// 空行:分隔裁剪坐标变量和线宽设置。
// 加粗蓝色线条以便区分。
glLineWidth(3.0); // 设置线宽为 3 像素。 glLineWidth(3.0); // 设置线宽为 3 像素。
cohenSutherlandClip(cx_0, cy_0, cx_1, cy_1, rect); // 执行裁剪算法并绘制裁剪后的线段。 cohenSutherlandClip(lineStart, lineEnd, rect); // 执行裁剪算法并绘制裁剪后的线段。
glLineWidth(1.0); // 恢复线宽。 glLineWidth(1.0); // 恢复线宽。
// 空行:分隔裁剪结果绘制和刷新。
glFlush(); // 强制执行所有尚未执行的 OpenGL 绘图命令。 glFlush(); // 强制执行所有尚未执行的 OpenGL 绘图命令。
} // 显示回调函数结束。 } // 显示回调函数结束。
// 空行:分隔显示回调函数和初始化函数。
// --- 初始化设置 --- // 初始化设置
void Init() { // 定义初始化函数。 void Init() { // 定义初始化函数。
glShadeModel(GL_FLAT); // 设置平面着色模式。 glShadeModel(GL_FLAT); // 设置平面着色模式。
// 空行:分隔 OpenGL 状态和裁剪窗口边界。 rect.xmin = 150; // 设置裁剪窗口边界。
rect.xmin = 100; // 设置裁剪窗口边界。 rect.xmax = 450; // 设置裁剪窗口边界。
rect.xmax = 400; // 设置裁剪窗口边界。 rect.ymin = 120; // 设置裁剪窗口边界。
rect.ymin = 100; // 设置裁剪窗口边界。 rect.ymax = 320; // 设置裁剪窗口边界。
rect.ymax = 300; // 设置裁剪窗口上边界。 }
} // 初始化函数结束。
// 空行:分隔初始化函数和窗口调整函数。 // 窗口大小调整
// --- 窗口大小调整 ---
void myReshape(int w, int h) { // 定义窗口尺寸变化时的回调函数。 void myReshape(int w, int h) { // 定义窗口尺寸变化时的回调函数。
glViewport(0, 0, (GLsizei)w, (GLsizei)h); // 设置视口覆盖整个窗口。 glViewport(0, 0, (GLsizei)w, (GLsizei)h); // 设置视口覆盖整个窗口。
glMatrixMode(GL_PROJECTION); // 切换到投影矩阵模式。 glMatrixMode(GL_PROJECTION); // 切换到投影矩阵模式。
@@ -155,21 +136,20 @@ void myReshape(int w, int h) { // 定义窗口尺寸变化时的回调函数。
// 设置二维投影范围,与坐标数值对应。 // 设置二维投影范围,与坐标数值对应。
gluOrtho2D(0.0, (GLdouble)w, 0.0, (GLdouble)h); // 根据窗口宽高设置二维正交投影范围。 gluOrtho2D(0.0, (GLdouble)w, 0.0, (GLdouble)h); // 根据窗口宽高设置二维正交投影范围。
glMatrixMode(GL_MODELVIEW); // 切换回模型视图矩阵模式。 glMatrixMode(GL_MODELVIEW); // 切换回模型视图矩阵模式。
} // 窗口大小调整回调函数结束。 }
// 空行:分隔窗口调整函数和主函数。
// --- 主函数 --- int main(int argc, char* argv[]) {
int main(int argc, char* argv[]) { // 程序入口函数,接收命令行参数。
glutInit(&argc, argv); // 初始化 GLUT。 glutInit(&argc, argv); // 初始化 GLUT。
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); // 设置显示模式为 RGB 颜色和单缓冲。 glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); // 设置显示模式为 RGB 颜色和单缓冲。
glutInitWindowPosition(100, 100); // 设置窗口初始位置。 glutInitWindowPosition(100, 100); // 设置窗口初始位置。
glutInitWindowSize(600, 400); // 设置窗口初始大小为 600x400。 glutInitWindowSize(600, 400); // 设置窗口初始大小为 600x400。
glutCreateWindow("实验2_03毕爽爽 Cohen-Sutherland 算法"); // 创建窗口并设置标题。 glutCreateWindow("实验2_03毕爽爽 Cohen-Sutherland 算法"); // 创建窗口并设置标题。
// 空行:分隔窗口创建和初始化。
Init(); // 调用初始化函数,设置裁剪窗口。 Init(); // 调用初始化函数,设置裁剪窗口。
// 空行:分隔初始化和回调注册。
glutDisplayFunc(myDisplay); // 注册显示回调函数。 glutDisplayFunc(myDisplay); // 注册显示回调函数。
glutReshapeFunc(myReshape); // 注册窗口大小变化回调函数。 glutReshapeFunc(myReshape); // 注册窗口大小变化回调函数。
// 空行:分隔回调注册和主循环。
glutMainLoop(); // 进入 GLUT 事件处理主循环。 glutMainLoop(); // 进入 GLUT 事件处理主循环。
return 0; // 正常结束程序。 return 0;
} // 主函数结束。 }
BIN
View File
Binary file not shown.
+124
View File
@@ -0,0 +1,124 @@
#include <GL/glut.h> // 引入OpenGL 头文件,提供窗口和绘图函数。
#include <cmath> // 引入数学函数库,用于计算正弦和余弦。
const int WIDTH = 800; // 定义窗口宽度。
const int HEIGHT = 800; // 定义窗口高度。
const float PI = 3.1415926f; // 定义圆周率常量。
struct Point { // 定义用于保存二维点坐标的结构体。
float x; // 保存点的 x 坐标。
float y; // 保存点的 y 坐标。
};
// 定义原始三角形的三个顶点。
Point original[3] = {
{200.0f, 300.0f},
{200.0f, 200.0f},
{300.0f, 200.0f}
};
// 绘制指定颜色的三角形。
void drawTriangle(Point p[3], float r, float g, float b) {
glColor3f(r, g, b); // 设置绘制颜色。
glBegin(GL_TRIANGLES); // 开始按实心三角形方式提交顶点。
for (int i = 0; i < 3; i++) { // 依次提交三角形的三个顶点。
glVertex2f(p[i].x, p[i].y); // 提交当前顶点坐标。
}
glEnd(); // 结束实心三角形绘制。
}
void translateTriangle(Point src[3], Point dst[3], float tx, float ty) { // 对三角形进行平移变换。
for (int i = 0; i < 3; i++) { // 依次处理每个顶点。
dst[i].x = src[i].x + tx; // x 坐标加上平移量 tx。
dst[i].y = src[i].y + ty; // y 坐标加上平移量 ty。
}
}
void scaleTriangle(Point src[3], Point dst[3], float sx, float sy) { // 对三角形进行缩放变换。
for (int i = 0; i < 3; i++) { // 依次处理每个顶点。
dst[i].x = src[i].x * sx; // x 坐标乘以缩放系数 sx。
dst[i].y = src[i].y * sy; // y 坐标乘以缩放系数 sy。
}
}
void rotateTriangle(Point src[3], Point dst[3], float angleDeg) { // 对三角形进行旋转变换。
float rad = angleDeg * PI / 180.0f; // 将角度转换为弧度。
float c = cos(rad); // 计算旋转角的余弦值。
float s = sin(rad); // 计算旋转角的正弦值。
for (int i = 0; i < 3; i++) { // 依次处理每个顶点。
dst[i].x = src[i].x * c - src[i].y * s; // 根据旋转公式计算新的 x 坐标。
dst[i].y = src[i].x * s + src[i].y * c; // 根据旋转公式计算新的 y 坐标。
}
}
void drawAxes() { // 绘制坐标轴,便于观察图形位置。
glColor3f(0.75f, 0.75f, 0.75f); // 设置坐标轴颜色为灰色。
glLineWidth(1.0f); // 设置坐标轴线宽。
glBegin(GL_LINES); // 开始按线段方式提交顶点。
glVertex2f(0.0f, 0.0f); // 提交 x 轴起点。
glVertex2f(500.0f, 0.0f); // 提交 x 轴终点。
glVertex2f(0.0f, 0.0f); // 提交 y 轴起点。
glVertex2f(0.0f, 500.0f); // 提交 y 轴终点。
glEnd(); // 结束坐标轴绘制。
}
void myDisplay() { // 定义窗口重绘时调用的显示函数。
glClear(GL_COLOR_BUFFER_BIT); // 清除颜色缓冲区。
drawAxes(); // 绘制坐标轴。
Point temp[3]; // 临时保存变换后的顶点坐标。
// 1. 绘制原始三角形,颜色为红色。
drawTriangle(original, 1.0f, 0.0f, 0.0f);
// 2. 绘制平移后的三角形,颜色为绿色。
translateTriangle(original, temp, 100.0f, 0.0f);
drawTriangle(temp, 0.0f, 0.75f, 0.0f);
translateTriangle(original, temp, 0.0f, -100.0f);
drawTriangle(temp, 0.0f, 0.75f, 0.0f);
// 3. 绘制缩放后的三角形,颜色为品红色。
scaleTriangle(original, temp, 0.5f, 0.5f);
drawTriangle(temp, 1.0f, 0.0f, 1.0f);
scaleTriangle(original, temp, 1.0f, 1.5f);
drawTriangle(temp, 1.0f, 0.0f, 1.0f);
scaleTriangle(original, temp, 1.5f, 1.5f);
drawTriangle(temp, 1.0f, 0.0f, 1.0f);
// 4. 绘制旋转后的三角形,颜色为蓝色。
rotateTriangle(original, temp, 30.0f); // 逆时针旋转 30 度。
drawTriangle(temp, 0.0f, 0.0f, 1.0f);
rotateTriangle(original, temp, -33.0f); // 顺时针旋转 33 度。
drawTriangle(temp, 0.0f, 0.0f, 1.0f);
glFlush(); // 强制执行所有尚未执行的 OpenGL 绘图命令。
}
void Init() {
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // 设置背景颜色为黑色。
glMatrixMode(GL_PROJECTION); // 切换到投影矩阵模式。
glLoadIdentity(); // 将当前投影矩阵重置为单位矩阵。
gluOrtho2D(-20.0, 500.0, -20.0, 500.0); // 设置二维正交投影范围。
}
int main(int argc, char** argv) { // 程序入口函数。
glutInit(&argc, argv); // 初始化 GLUT。
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // 设置显示模式为 RGB 颜色和单缓冲。
glutInitWindowSize(WIDTH, HEIGHT); // 设置窗口初始大小。
glutInitWindowPosition(100, 100); // 设置窗口初始位置。
glutCreateWindow("实验3_03毕爽爽 图形几何变换"); // 创建窗口并设置标题。
Init(); // 调用初始化函数。
glutDisplayFunc(myDisplay); // 注册显示回调函数。
glutMainLoop(); // 进入 GLUT 事件处理主循环。
return 0;
}
BIN
View File
Binary file not shown.
+118
View File
@@ -0,0 +1,118 @@
#include <GL/glut.h> // 引入 GLUT 和 OpenGL 相关函数。
const int WINDOW_WIDTH = 800; // 设置窗口宽度。
const int WINDOW_HEIGHT = 600; // 设置窗口高度。
const int CURVE_SEGMENTS = 10000; // 设置曲线分段数量,数量越大曲线越平滑。
struct Vec2 { // 定义二维坐标点结构体。
double x; // 保存点的 x 坐标。
double y; // 保存点的 y 坐标。
};
Vec2 controlStart = { -300.0, -100.0 }; // Bezier 曲线的起点。
Vec2 controlHandleA = { 0.0, 200.0 }; // 第一个控制点,用来影响曲线起始方向。
Vec2 controlHandleB = { 200.0, 300.0 }; // 第二个控制点,用来影响曲线结束方向。
Vec2 controlEnd = { 300.0, 100.0 }; // Bezier 曲线的终点。
Vec2 calculateBezierPoint(double t) { // 根据参数 t 计算三次 Bezier 曲线上的点。
double inverseT = 1.0 - t; // 计算 1 - t,方便套用 Bezier 公式。
double startWeight = inverseT * inverseT * inverseT; // 起点对应的权重。
double handleAWeight = 3.0 * t * inverseT * inverseT; // 第一个控制点对应的权重。
double handleBWeight = 3.0 * t * t * inverseT; // 第二个控制点对应的权重。
double endWeight = t * t * t; // 终点对应的权重。
Vec2 curvePoint; // 保存当前计算出的曲线点。
curvePoint.x = startWeight * controlStart.x // 按权重累加起点的 x 坐标。
+ handleAWeight * controlHandleA.x // 按权重累加第一个控制点的 x 坐标。
+ handleBWeight * controlHandleB.x // 按权重累加第二个控制点的 x 坐标。
+ endWeight * controlEnd.x; // 按权重累加终点的 x 坐标。
curvePoint.y = startWeight * controlStart.y // 按权重累加起点的 y 坐标。
+ handleAWeight * controlHandleA.y // 按权重累加第一个控制点的 y 坐标。
+ handleBWeight * controlHandleB.y // 按权重累加第二个控制点的 y 坐标。
+ endWeight * controlEnd.y; // 按权重累加终点的 y 坐标。
return curvePoint; // 返回曲线上当前参数 t 对应的点。
}
void submitControlVertices() { // 将四个控制点提交给当前 OpenGL 绘制命令。
glVertex2d(controlStart.x, controlStart.y); // 提交起点坐标。
glVertex2d(controlHandleA.x, controlHandleA.y); // 提交第一个控制点坐标。
glVertex2d(controlHandleB.x, controlHandleB.y); // 提交第二个控制点坐标。
glVertex2d(controlEnd.x, controlEnd.y); // 提交终点坐标。
}
void renderCoordinateAxes() { // 绘制坐标轴。
glColor3f(0.5f, 0.5f, 0.5f); // 设置坐标轴颜色为灰色。
glLineWidth(1.0f); // 设置坐标轴线宽。
glBegin(GL_LINES); // 开始绘制独立线段。
glVertex2d(-400.0, 0.0); // x 轴左端点。
glVertex2d(400.0, 0.0); // x 轴右端点。
glVertex2d(0.0, -200.0); // y 轴下端点。
glVertex2d(0.0, 400.0); // y 轴上端点。
glEnd(); // 结束坐标轴绘制。
}
void renderControlPolygon() { // 绘制连接控制点的折线。
glColor3f(0.0f, 0.0f, 1.0f); // 设置控制多边形颜色为蓝色。
glLineWidth(2.0f); // 设置控制多边形线宽。
glBegin(GL_LINE_STRIP); // 开始绘制连续折线。
submitControlVertices(); // 按顺序提交四个控制点。
glEnd(); // 结束控制多边形绘制。
}
void renderBezierCurve() { // 绘制三次 Bezier 曲线。
glColor3f(1.0f, 1.0f, 0.0f); // 设置曲线颜色为黄色。
glLineWidth(3.0f); // 设置曲线线宽。
double step = 1.0 / CURVE_SEGMENTS; // 计算每次增加的 t 步长。
glBegin(GL_LINE_STRIP); // 开始用连续折线近似绘制曲线。
for (double t = 0.0; t <= 1.0; t += step) { // 从 t=0 到 t=1 依次取点。
Vec2 curvePoint = calculateBezierPoint(t); // 计算当前 t 对应的曲线点。
glVertex2d(curvePoint.x, curvePoint.y); // 提交当前曲线点坐标。
}
glEnd(); // 结束曲线绘制。
}
void renderControlPoints() { // 绘制四个控制点。
glColor3f(1.0f, 0.0f, 0.0f); // 设置控制点颜色为红色。
glPointSize(8.0f); // 设置控制点大小。
glBegin(GL_POINTS); // 开始绘制点。
submitControlVertices(); // 提交四个控制点的位置。
glEnd(); // 结束控制点绘制。
}
void displayScene() { // GLUT 显示回调函数,负责绘制整个画面。
glClear(GL_COLOR_BUFFER_BIT); // 清空颜色缓冲区,准备重新绘制。
renderCoordinateAxes(); // 绘制坐标轴。
renderControlPolygon(); // 绘制控制多边形。
renderBezierCurve(); // 绘制 Bezier 曲线。
renderControlPoints(); // 绘制控制点。
glFlush(); // 强制执行前面的 OpenGL 绘图命令。
}
void initializeOpenGL() { // 初始化 OpenGL 绘图环境。
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // 设置背景颜色为黑色。
glMatrixMode(GL_PROJECTION); // 切换到投影矩阵模式。
glLoadIdentity(); // 重置当前投影矩阵。
gluOrtho2D(-400.0, 400.0, -200.0, 400.0); // 设置二维正交投影范围。
}
int main(int argc, char** argv) { // 程序入口函数。
glutInit(&argc, argv); // 初始化 GLUT。
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // 使用单缓冲和 RGB 颜色模式。
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT); // 设置窗口大小。
glutInitWindowPosition(100, 100); // 设置窗口初始位置。
glutCreateWindow("实验4_01毕爽爽三次 Bezier 曲线"); // 创建窗口并设置标题。
initializeOpenGL(); // 初始化 OpenGL 参数。
glutDisplayFunc(displayScene); // 注册显示回调函数。
glutMainLoop(); // 进入 GLUT 主事件循环。
return 0; // 程序正常结束。
}
BIN
View File
Binary file not shown.