(6)---Bresenham画线算法【完整斜率】C语言-OpenGL库-计算机图形学

本次实验项目 

        Bresenham画线算法实现缤纷放射线。 

功能简单介绍

         这是一个OpenGL程序,使用Bresenham算法在窗口中绘制放射状直线。程序初始化OpenGL环境,设置窗口和视图参数,然后进入主循环,在每个周期中清除屏幕,设置随机颜色,并绘制36条从中心点向外辐射的直线,每条直线的颜色随机。Bresenham算法用于高效地在像素网格中确定直线路径上的点,确保直线的精确绘制。

算法介绍

        Bresenham算法,全称Bresenham's line algorithm,是一种在栅格系统(如计算机屏幕)上绘制直线的算法。这个算法由Jack E. Bresenham在1962年发表,它特别适用于计算机图形学中,因为它只使用整数运算,避免了浮点数的计算,从而提高了效率和速度。 Bresenham算法的核心思想是将直线的斜率(梯度)表示为一个分数,然后根据这个分数来决定在每一步中是只增加x坐标还是同时增加x和y坐标。算法通过计算一个决策参数来决定每一步的走向。

算法描述

        假设我们有一个直线方程  y=mx+b,其中 m 是斜率, b 是y轴截距。如下图

斜率分布和步进方向大小 

        考虑斜率 0<m<1 当我们向右移动一个像素(即x+1)时,我们希望确定 y 坐标是否也需要增加。这可以通过比较直线方程在 x+1 坐标处的  y 值和当前 x 时 y 坐标的差异来决定。

        在上面光栅坐标系中,我们以任意点红点为观察对象分析,已知直线方程为 y=mx+b,所以在x_{k+1} 的 y 坐标为 y = m(x_{k} + 1) + b。

        为方便填充像素点,要确定方程计算的 y 值与竖直方向最近的像素点的距离大小。

d_{down} =  y - y_{k}  = m(x_{k} + 1) + b - y_{k}

d_{up} = (y_{k}+1) - y  =  y_{k}+1-m(x_{k} + 1) - b

d_{down} - d_{up}   =2m(x_{k} + 1) - 2y_{k} + 2b -1

        若d_{down} - d_{up}  >0, y 值靠近 y_{k}+1,则填充 y_{k}+1 像素点,反之填充 y_{k} 像素点。循环依次向x方向步进,即可描出直线。基于此,我们对给定的直线两点(x0,y0)与(x_{end}y_{end}),令 m = \frac{\Delta y}{\Delta x}

        定义决策参数:

 p_{k}​ =\Delta xd_{down} - d_{up} ) = 2x_{k}\Delta y - 2y_{k}\Delta x + 2\Delta y + (2b -1)\Delta x

        因为p_{k}d_{down} - d_{up}符号相同,2\Delta y + (2b -1)\Delta x 为常数,及 p_{k}​  = 2x_{k+1}\Delta y - 2y_{k+1}\Delta x + 2\Delta y + (2b -1)\Delta x做差我们得到下一点的决策参数递推式:

p_{k+1} - p_{k}   =2\Delta y(x_{k+1} - x_{k}) - 2\Delta x (y_{k+1} - y_{k})

        因为 x_{k+1} = x_{k}  + 1  ,得

 p_{k+1}  =p_{k} + 2\Delta y - 2\Delta x (y_{k+1} - y_{k})

其中,(y_{k+1} - y_{k})取值为1或0,由 p_{k} 决定,

        若 p_{k} > 0 , p_{k+1}  =p_{k} + 2\Delta y - 2\Delta x ,填充(x_{k}+1,y_{k}+1);

        否则 ,p_{k+1}  =p_{k} + 2\Delta y ,填充(x_{k}+1,y_{k}).

对于起始点(x0,y0)的决策参数可由 y=mx+b 与 代入起始点(x0,y0)的p_{k}​ = 2x_{k}\Delta y - 2y_{k}\Delta x + 2\Delta y + (2b -1)\Delta x 联立求得:

p_{0} = 2\Delta y - \Delta x

这样的决策过程确保了算法能够准确地沿着直线路径绘制像素点。从 k=0 重复 \Delta x-1 次即可画出这(x0,y0)与(x_{end}y_{end})两点间直线  y=mx+b  。

        对于其他斜率依法炮制,只需注意初始点和步进方向即可。

代码

#include <windows.h>  // 包含Windows API函数
#include <GL/glut.h>  // 包含OpenGL和GLUT库函数
#include <math.h>      // 包含数学函数,如cos和sin

#define GLUT_DISABLE_ATEXIT_HACK  // 禁用GLUT的atexit hack,避免在程序退出时产生警告

// 设置像素点的函数
void setPixel(int x, int y) {
    glBegin(GL_POINTS);  // 开始绘制点
    	glVertex2i(x, y);   // 指定点的坐标
    glEnd();            // 结束绘制
}

// 实现Bresenham直线算法的函数
void lineBresenham(int x0, int y0, int x1, int y1) {
    int x, y, dx, dy, s1, s2, p, temp, interchange;
    x = x0;  // 当前x坐标
    y = y0;  // 当前y坐标
    dx = abs(x1 - x0);  // x方向的差值
    dy = abs(y1 - y0);  // y方向的差值
    if (x1 > x0) s1 = 1; else s1 = -1;  // x方向的步进值
    if (y1 > y0) s2 = 1; else s2 = -1;  // y方向的步进值
    if (dy > dx) {  // 如果dy大于dx,则交换dx和dy,并标记interchange
        temp = dx;
        dx = dy;
        dy = temp;
        interchange = 1;
    } else interchange = 0;
    p = 2 * dy - dx;  // 初始化决策参数
    for (int i = 1; i <= dx; i += 1) {  // 循环dx次
        setPixel(x, y);  // 绘制当前像素点
        if (p >= 0) {  // 如果决策参数大于等于0
            if (interchange == 0) y += s2; else x += s1;  // 根据interchange决定是增加x还是y
            p -= 2 * dx;  // 更新决策参数
        }
        if (interchange == 0) x += s1; else y += s2;  // 增加x或y
        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坐标
        setRandomColor();  // 设置随机颜色
        lineBresenham(centerX, centerY, x1, y1);  // 使用Bresenham算法绘制线段
    }
}

// 绘制线段的函数
void lineSegment(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  // 清除屏幕和深度缓存区
    glPointSize(6);  // 设置点的大小
    drawRadiatingLines(0, 0, 300, 36);  // 绘制放射线
    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, 500);  // 设置窗口位置
    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、付费专栏及课程。

余额充值