OpenGL-直线的扫描转换
这篇文章介绍了三种直线扫描转换算法:DDA算法、中点画线算法和Bresenham算法。 DDA算法通过计算直线起点和终点的坐标差值来确定步数和增量,依次绘制直线上的点。算法实现简单,适用于任意直线,但精度较低。 中点画线算法仅适用于斜率为0或1的直线,通过计算中点和斜率来确定直线上的像素点,效率较高。 Bresenham算法根据直线的斜率和误差项来确定像素点的位置,适用于任意直线,且精度高,但实现稍复杂。
OpenGL 学习笔记
这篇文章来介绍直线扫描转换算法
DDA数值微分线段算法
算法简介
数值微分法即DDA法(Digital Differential Analyzer),是一种基于微分方程来生成直线的方法。在计算机图形学中,并没有线段的概念,而是一个个像素点组成了线段。
DDA法生成线段的步骤一般如下:
- 有了起始点(
)和终点( ); - 比较
和 的大小; steps= 和 中较大者;
算法实现
DDA算法实现如下:
#include <cmath>
#include <iostream>
#include <GL/glut.h>
// 窗口宽度和高度
const int WIDTH = 640;
const int HEIGHT = 480;
void drawDDALine(int x1, int y1, int x2, int y2) {
float x = x1;
float y = y1;
// 计算差值
int dx = x2 - x1;
int dy = y2 - y1;
// 确定步数,取 dx 和 dy 中绝对值较大的那个
int steps = std::abs(dx) > std::abs(dy) ? std::abs(dx) : std::abs(dy); //三元表达式
// 计算每一步的增量
float xIncrement = (float)dx / steps;
float yIncrement = (float)dy / steps;
// 开始绘制点
glBegin(GL_POINTS);
glVertex2i((int)round(x), (int)round(y)); // 绘制起点
for (int k = 0; k < steps; k++) {
x += xIncrement;
y += yIncrement;
// 将浮点坐标四舍五入取整转换为整数像素坐标
std::cout << (int)round(x) << ", " << (int)round(y)<<"\n";
glVertex2i((int)round(x), (int)round(y));
}
glEnd();
}
// 显示回调函数
void display() {
drawDDALine(0, 0, 50, 20);
glFlush();
}
// 初始化 OpenGL 设置
void init() {
// 设置背景颜色为白色
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
// 设置投影矩阵为 2D 正交投影
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// 定义可视区域,左下角(0,0),右上角(WIDTH, HEIGHT)
gluOrtho2D(0.0, WIDTH, 0.0, HEIGHT);
}
int main(int argc, char** argv) {
// 初始化 GLUT
glutInit(&argc, argv);
// 设置显示模式:单缓冲、RGB 颜色模式
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
// 设置窗口大小和位置
glutInitWindowSize(WIDTH, HEIGHT);
glutInitWindowPosition(100, 100);
// 创建窗口
glutCreateWindow("DDA算法");
// 注册回调函数
glutDisplayFunc(display);
// 初始化设置
init();
// 进入主循环
glutMainLoop();
return 0;
}
中点画线算法
算法简介
只考虑当直线的斜率
- 如果直线
,则取下边的点也就是 。 - 如果直线
,则取上边的点也就是 。
它的实际过程就是这样每次根据前边的点判断下一个点在哪,然后进行打亮,但这样每次判断的时候都得代入直线方程计算太麻烦了,我们将这俩种情况分别代入直线方程中可以找出规律:
- 当直线
时,经过化解得 ; - 当直线
时,经过化解得 ; - 初始值
。
也就是说每次的增量要么为
算法实现
#include <GL/freeglut.h>
#include <iostream>
#include <cmath>
// 中点画线算法函数 (仅针对斜率 0 <= k <= 1 的情况进行演示,其它象限需类比处理)
void drawLineMidpoint(int x0, int y0, int x1, int y1) {
int a = y0 - y1;
int b = x1 - x0;
int d = 2 * a + b;
int d1 = 2 * a;
int d2 = 2 * (a + b);
int x = x0, y = y0;
glBegin(GL_POINTS);
glVertex2i(x, y); // 画起点
while (x < x1) {
if (d < 0) {
x++;
y++;
d += d2;
} else {
x++;
d += d1;
}
glVertex2i(x, y);
}
glEnd();
}
// 渲染回调函数
void display() {
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 1.0, 1.0); // 设置画笔颜色为白色
// 调用算法画一条直线 (x0, y0) 到 (x1, y1)
// 注意:此处的坐标对应屏幕像素坐标
drawLineMidpoint(50, 50, 450, 300);
glFlush();
}
// 初始化设置
void init() {
glClearColor(0.0, 0.0, 0.0, 1.0); // 背景设为黑色
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// 设置正交投影矩阵,使坐标系与窗口像素对应
gluOrtho2D(0, 500, 0, 500);
}
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow("Midpoint Line Algorithm - FreeGLUT");
init();
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
Bresenham算法
算法简介
假设我们需要由
因此x及y之值分别向右及向下增加,而两点之水平距离为
对于由
要实行此算法,我们需计算每一像素点与该线之间的误差。于上述例子中,误差应为每一点
算法实现
#include <GL/freeglut.h>
#include <iostream>
#include <cmath>
// 通用 Bresenham 画线算法
void drawLineBresenham(int x0, int y0, int x1, int y1) {
int dx = abs(x1 - x0);
int dy = abs(y1 - y0);
int sx = (x0 < x1) ? 1 : -1; // X 方向步进
int sy = (y0 < y1) ? 1 : -1; // Y 方向步进
int err = dx - dy; // 初始误差项
glBegin(GL_POINTS);
while (true) {
glVertex2i(x0, y0); // 绘制当前点
if (x0 == x1 && y0 == y1) break; // 到达终点
int e2 = 2 * err;
// 判断是否在 X 方向步进
if (e2 > -dy) {
err -= dy;
x0 += sx;
}
// 判断是否在 Y 方向步进
if (e2 < dx) {
err += dx;
y0 += sy;
}
}
glEnd();
}
// 渲染回调
void display() {
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0f, 0.8f, 0.4f); // 设置一个好看的绿色(类似你图中的图标颜色)
// 绘制几条不同方向的线来测试算法的健壮性
drawLineBresenham(50, 50, 450, 400); // 第一象限
drawLineBresenham(50, 400, 450, 50); // 第四象限
drawLineBresenham(250, 50, 250, 450); // 垂直线
drawLineBresenham(50, 250, 450, 250); // 水平线
glFlush();
}
void init() {
glClearColor(0.1f, 0.1f, 0.1f, 1.0f); // 深色背景
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, 500, 0, 500); // 建立 500x500 的直角坐标系
}
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(600, 600);
glutCreateWindow("Bresenham Line Algorithm");
init();
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
Comments
OpenGL-直线的扫描转换