本次实验项目
熟悉中点画圆算法。
算法介绍
中点圆算法是一种在像素网格上绘制圆的有效算法,由Jack Bresenham提出。这种算法的核心思想是利用圆的对称性,通过计算圆上的一些关键点来绘制整个圆。它只使用整数运算,因此在计算上非常高效。这种算法特别适合于图形硬件和嵌入式系统中的图形绘制。
算法描述
算法的基本步骤如下:
-
初始化:从圆的顶部开始,即点(0, radius),其中radius是圆的半径。
-
决策参数:定义一个决策参数
p
,初始值为1 - radius
。这个参数用于决定下一个像素点应该绘制在当前位置的哪个方向。 -
绘制对称点:由于圆具有八对称性,每次计算出一个新的点,都可以在其他七个对称位置上绘制相同的点,从而减少计算量。
-
迭代绘制:算法通过迭代的方式,逐步从顶部向右侧绘制圆的上半部分。在每一步中,根据决策参数
p
的值,决定是只增加x坐标(向右移动),还是同时增加x坐标并减少y坐标(向右下方移动)。 -
更新决策参数:根据选择的移动方向,更新决策参数
p
。如果只增加x坐标,p
增加2*x + 1
;如果同时增加x坐标并减少y坐标,p
增加2*(x - y) + 1
。 -
完成绘制:当x坐标大于或等于y坐标时,停止迭代,此时圆的上半部分已经绘制完成。由于圆的对称性,下半部分可以通过对称绘制。
功能简单介绍
这个程序是一个基于OpenGL和GLUT库的图形绘制应用,它展示了如何在屏幕上绘制基本的几何图形,包括直线、圆以及放射线。程序使用了经典的Bresenham算法来高效地绘制直线和圆,这是一种仅使用整数运算的算法,特别适合于像素网格上的图形绘制。此外,程序还利用了中点圆算法来绘制圆,这是一种优化的圆绘制算法,能够减少计算量并提高绘制效率。通过设置随机颜色,程序能够在绘制时产生多彩的视觉效果。用户可以看到四个角落的放射线、中心的大圆以及两组彩色的圆环,这些图形共同构成了一幅有趣的图案。
代码
#include <windows.h> // 包含Windows API函数
#include <GL/glut.h> // 包含OpenGL和GLUT库函数
#include <math.h> // 包含数学函数,如cos和sin
#define GLUT_DISABLE_ATEXIT_HACK // 禁用GLUT的atexit hack,避免在程序退出时产生警告
// 定义一个屏幕点类,用于表示屏幕上的点
class screenPt {
private:
GLint x, y; // 点的坐标
public:
// 构造函数,初始化坐标为(0, 0)
screenPt() {
x = y = 0;
}
// 设置点的坐标
void setCoords(GLint xCoordValue, GLint yCoordValue) {
x = xCoordValue;
y = yCoordValue;
}
// 获取x坐标
GLint getx() const {
return x;
}
// 获取y坐标
GLint gety() const {
return y;
}
// x坐标增加1
void incrementx() {
x++;
}
// y坐标减少1
void decrementy() {
y--;
}
};
// 设置像素点的函数,使用OpenGL绘制一个点
void setPixel(int x, int y) {
glBegin(GL_POINTS); // 开始绘制点
glVertex2i(x, y); // 指定点的坐标
glEnd(); // 结束绘制
}
// 在圆的八个对称位置绘制点
void circlePlotPoints(GLint xc, GLint yc, screenPt circPt) {
setPixel(xc + circPt.getx(), yc + circPt.gety());
setPixel(xc + circPt.getx(), yc - circPt.gety());
setPixel(xc - circPt.getx(), yc + circPt.gety());
setPixel(xc - circPt.getx(), yc - circPt.gety());
setPixel(xc + circPt.gety(), yc + circPt.getx());
setPixel(xc - circPt.gety(), yc + circPt.getx());
setPixel(xc + circPt.gety(), yc - circPt.getx());
setPixel(xc - circPt.gety(), yc - circPt.getx());
}
// 使用中点圆算法绘制圆的函数
void circleMidpoint(GLint xc, GLint yc, GLint radius) {
screenPt circPt;
GLint p = 1 - radius;
circPt.setCoords(0, radius); // 设置圆顶点的坐标
// 在每个圆象限中绘制初始点
circlePlotPoints(xc, yc, circPt);
// 计算下一个点并在每个卦限中绘制
while (circPt.getx() < circPt.gety()) {
circPt.incrementx();
if (p < 0)
p += 2 * circPt.getx() + 1;
else {
circPt.decrementy();
p += 2 * (circPt.getx() - circPt.gety()) + 1;
}
circlePlotPoints(xc, yc, circPt);
}
}
// 实现Bresenham直线算法的函数
void lineBresenham(int x0, int y0, int xend, int yend) {
int x = x0, y = y0; // 当前坐标
int dx = abs(xend - x0), dy = abs(yend - y0); // x和y方向的差值
int s1 = (xend > x0) ? 1 : -1, s2 = (yend > y0) ? 1 : -1; // 步进方向
int interchange = 0, p, temp;
if (dy > dx) { // 如果dy大于dx,交换dx和dy
temp = dx;
dx = dy;
dy = temp;
interchange = 1;
}
p = 2 * dy - dx; // 初始化决策参数
for (int i = 1; i <= dx; i++) { // 循环绘制直线
setPixel(x, y);
if (p >= 0) {
if (interchange == 0) y += s2; else x += s1;
p -= 2 * dx;
}
if (interchange == 0) x += s1; else y += s2;
p += 2 * dy;
}
}
// 设置随机颜色的函数
void setRandomColor() {
float r = static_cast<float>(rand()) / static_cast<float>(RAND_MAX); // 生成随机红色值
float g = static_cast<float>(rand()) / static_cast<float>(RAND_MAX); // 生成随机绿色值
float b = static_cast<float>(rand()) / static_cast<float>(RAND_MAX); // 生成随机蓝色值
glColor3f(r, g, b); // 设置当前绘图颜色
}
// 绘制放射线的函数
void drawRadiatingLines(int centerX, int centerY, int radius, int numLines) {
const double angleIncrement = 2 * M_PI / numLines; // 每条线之间的角度增量
for (int i = 0; i < numLines; ++i) {
double angle = i * angleIncrement; // 计算当前线的角度
int x1 = static_cast<int>(centerX + radius * cos(angle)); // 计算终点X坐标
int y1 = static_cast<int>(centerY + radius * sin(angle)); // 计算终点Y坐标
lineBresenham(centerX, centerY, x1, y1); // 使用Bresenham算法绘制线段
}
}
// 绘制线段的函数,这是GLUT的显示回调函数
void lineSegment(void) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓存区
glPointSize(6); // 设置点的大小
setRandomColor();
drawRadiatingLines(450, 450, 50, 16); // 绘制放射线
drawRadiatingLines(-450, 450, 50, 16);
drawRadiatingLines(-450, -450, 50, 16);
drawRadiatingLines(450, -450, 50, 16);
circleMidpoint(0, 0, 450); // 绘制大圆
// 绘制五色环
glPointSize(15);
for (int i = 0; i <= 4; i++) {
int x1 = -250 + i * 125;
setRandomColor();
circleMidpoint(x1, 150, 100);
}
// 绘制彩迪车标
glPointSize(11);
setRandomColor();
for (int i = 0; i <= 3; i++) {
int x2 = -200 + i * 125;
circleMidpoint(x2, -150, 100);
}
glFlush(); // 强制执行所有OpenGL命令
}
// 初始化OpenGL环境的函数
void init(void) {
glClearColor(0.0, 0.0, 0.0, 0.0); // 设置背景色为黑色
glMatrixMode(GL_PROJECTION); // 选择投影矩阵
gluOrtho2D(-500.0, 500.0, -500.0, 500.0); // 设置正交投影,定义坐标系
}
// 主函数
int main(int argc, char** argv) {
glutInit(&argc, argv); // 初始化GLUT库
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // 设置显示模式为单缓冲和RGB颜色模式
glutInitWindowPosition(500, 100); // 设置窗口位置
glutInitWindowSize(1000, 1000); // 设置窗口大小
glutCreateWindow("Bresenham-OpenGL"); // 创建窗口
init(); // 初始化OpenGL环境
glutDisplayFunc(lineSegment); // 设置显示回调函数
glutMainLoop(); // 进入GLUT主事件循环
return 0;
}
运行结果
上手实践
各位学有余力的先生和女士,如有兴趣的话,不妨参考上面内容,动手绘制自己喜欢的图形吧!祝你成功。