(7)---【中点画圆算法】C语言-OpenGL库-计算机图形学

本次实验项目    

        熟悉中点画圆算法。

算法介绍     

        中点圆算法是一种在像素网格上绘制圆的有效算法,由Jack Bresenham提出。这种算法的核心思想是利用圆的对称性,通过计算圆上的一些关键点来绘制整个圆。它只使用整数运算,因此在计算上非常高效。这种算法特别适合于图形硬件和嵌入式系统中的图形绘制。

算法描述       

算法的基本步骤如下:

  1. 初始化:从圆的顶部开始,即点(0, radius),其中radius是圆的半径。

  2. 决策参数:定义一个决策参数p,初始值为1 - radius。这个参数用于决定下一个像素点应该绘制在当前位置的哪个方向。

  3. 绘制对称点:由于圆具有八对称性,每次计算出一个新的点,都可以在其他七个对称位置上绘制相同的点,从而减少计算量。

  4. 迭代绘制:算法通过迭代的方式,逐步从顶部向右侧绘制圆的上半部分。在每一步中,根据决策参数p的值,决定是只增加x坐标(向右移动),还是同时增加x坐标并减少y坐标(向右下方移动)。

  5. 更新决策参数:根据选择的移动方向,更新决策参数p。如果只增加x坐标,p增加2*x + 1;如果同时增加x坐标并减少y坐标,p增加2*(x - y) + 1

  6. 完成绘制:当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;
}

运行结果

上手实践

          各位学有余力的先生和女士,如有兴趣的话,不妨参考上面内容,动手绘制自己喜欢的图形吧!祝你成功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值