【OpenGL学习】 四种绘制直线的算法
我是用MFC框架进行测试的,由于本人也没有专门系统学习MFC框架,代码若有不足之处,请指出。
一,先来一个最简单的DDA算法
DDA算法全称为数值微分法,基于微分方程来绘制直线。
①推导微分方程如下:
②,dM时间步长的倒数的详解:
可以看到
当|k|<=1时
dx=1或者-1,此时的x为计长方向
当|k|>1时
dy=1或者-1,此时的y为计长方向
绘制时需要用dM来控制绘制的点数
③绘制像素的问题:
为了“方便”管理算法,我为不同的绘制函数新建了一个类了。。。(其实可以写到一个类里面。。。。)
④代码实现:
MyDDA.cpp
#include "stdafx.h"
#include "MyDDA.h" MyDDA::MyDDA()
{
} MyDDA::~MyDDA()
{
} void MyDDA::SetDc(CDC * dc)
{
this->pdc = dc;
} void MyDDA::Moveline(CPoint start, CPoint end)
{
int x1, x2;
int y1, y2;
x1 = start.x;
x2 = end.x;
y1 = start.y;
y2 = end.y;
float dm = ;
if (abs(x2 - x1) >= abs(y2 - y1))
dm = abs(x2 - x1);
else
dm = abs(y2 - y1);
float dx = (float)(x2 - x1) / dm;
float dy = (float)(y2 - y1) / dm;
float x = x1 + 0.5;
float y = y1 + 0.5;
int i = ;
while (i<dm) {
this->pdc->SetPixel((int)x, (int)y, RGB(, , ));
x += dx;
y += dy;
i += ;
} }
总结:
其实这个算法还算是挺简单的,就是要确定是X还算Y为计长方向,需要注意的是循环过程中计长方向自增1,另一个方向按照当直线在计长方向自增1时,直线在轴上的投影的大小自增就可以了。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
二,逐点比较法
逐点比较法是在画线的过程中,每画一点就与理想直线比较,以决定下一点的走向,以一步步逼近的方法点亮最接近直线的一组像素。
①绘制前先将直线平移
使y坐标较小的点位于坐标原点(绘制的时候再平移回去,跟几何变换一个道理)
如图:
②推导过程:
先来谈一下直线的偏移值怎么表示
可以看到:
如果d是正值说明当前绘制点在绘制直线的上方。
反之,在下方。
③由于每次计算都需要计算两次乘法,计算工作量大,所以可以利用递推公式来,借助当前点的偏移值,获取下一个点的偏移值。
递推公式推导过程如下图所示:
④终点判断:
⑤整理下我们在编程时需要的参数:
⑥实现代码:
注释部分的代码比较容易理解一点,但是太长了,注释下面的代码是简化的版本,两者的作用是一样的。
PtoP.cpp
void PtoP::Moveline(CPoint start, CPoint end)
{
int xA, yA;
if (start.y > end.y) {
xA = start.x - end.x;
yA = start.y - end.y;
}
else
{
xA = end.x - start.x;
yA = end.y - start.y;
} int n = abs(xA) + abs(yA); int x = , y = , F = ;
/*
if (xA > 0)//1
{
for (int i = 0; i < n; i++)
{
if (F >= 0)
{
x += 1;
F -= yA;
if(start.y>end.y)
this->pdc->SetPixel(x+end.x,y+end.y, RGB(0, 0, 0));
else
this->pdc->SetPixel(x + start.x, y + start.y, RGB(0, 0, 0));
}
else
{
y += 1;
F += xA;
if (start.y > end.y)
this->pdc->SetPixel(x + end.x, y + end.y, RGB(0, 0, 0));
else
this->pdc->SetPixel(x + start.x, y + start.y, RGB(0, 0, 0));
}
} }
else//2
{
for (int i = 0; i < n; i++)
{
if (F >= 0)
{
y += 1;
F += xA;
if (start.y > end.y)
this->pdc->SetPixel(x + end.x, y + end.y, RGB(0, 0, 0));
else
this->pdc->SetPixel(x + start.x, y + start.y, RGB(0, 0, 0));
}
else
{
x-= 1;
F += yA;
if (start.y > end.y)
this->pdc->SetPixel(x + end.x, y + end.y, RGB(0, 0, 0));
else
this->pdc->SetPixel(x + start.x, y + start.y, RGB(0, 0, 0));
} } }
*/
for (int i = ; i < n; i++) {
if (xA > ) {
if (F >= )
{
x++;
F -= yA;
}
else
{
y++;
F += xA;
}
}
else {
if (F >= )
{
y++;
F += xA;
}
else
{
x--;
F += yA;
}
}
if (start.y > end.y)
this->pdc->SetPixel(x + end.x, y + end.y, RGB(, , ));
else
this->pdc->SetPixel(x + start.x, y + start.y, RGB(, , ));
}
}
⑦总结:
逐点比较法绘制最重要的是递推公式的推导,以及避免原偏移值公式的无理运算。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
三,Bresenham画线法
①引入
Bresenham画线算法是应用最广泛的直线生成算法,它采用加减以及乘2运算(即移位运算)来实现。
首先先从直线斜率为0<=k<=1的八分之一象限来开始讨论。
如图:
②如何判别选取点S还是点T
③迭代公式
④起点的判别值
⑤其他情况的判别
⑥整理所需变量
⑦代码实现
Bresenham.cpp
#include "stdafx.h"
#include "Bresenham.h" Bresenham::Bresenham()
{
} Bresenham::~Bresenham()
{
} void Bresenham::SetDc(CDC * dc)
{
this->pdc = dc;
} void Bresenham::Moveline(CPoint start, CPoint end)
{
int x1=start.x, y1=start.y;
int x2=end.x, y2=end.y; this->pdc->SetPixel(start.x,start.y,RGB(,,));
int dx, dy;
dx = abs(x2-x1);
dy = abs(y2-y1);
int flag=;
if (dx == && dy == )
return;
if (dy > dx)
{
flag = ;
swap_value(x1,y1);
swap_value(x2,y2);
swap_value(dx,dy);
}
int tx = (x2 - x1) > ? : -;
int ty = (y2 - y1) > ? : -;
int curx = x1 + ;
int cury = y1;
int dS = * dy;
int dT = * (dy-dx);
int d =dS-dx;
while (curx != x2)
{
if (d >= ) {
d += dT;
cury += ty;
}
else
{
d += dS;
}
if (flag)
this->pdc->SetPixel(cury,curx,RGB(,,));
else
this->pdc->SetPixel(curx, cury, RGB(, , ));
curx+=tx;
}
} void Bresenham::swap_value(int & a, int & b)
{
/*
a ^= b;
b ^= a;
a ^= b;
*/
int temp = a;
a = b;
b = temp; }
总结:Bresenham算法关键点还是在于迭代公式的推导,以及如何选择下一个点,判断x轴和y轴的步长是自增还是自减。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
四,中点画线法
中点画线法的思路跟Bresenham算法的思路极为相似,在学习完之后,我就觉得两者的算法不同之处就是取点的判断标准。
①构造判别式
②递推公式推导
③其他斜率情形
④整理变量
⑤代码实现
#include "stdafx.h"
#include "MidpointLine.h" MidpointLine::MidpointLine()
{
} MidpointLine::~MidpointLine()
{
} void MidpointLine::SetDc(CDC * dc)
{
this->pdc = dc;
} void MidpointLine::Moveline(CPoint start, CPoint end)
{
int x0 = start.x, x1 = end.x, y0 = start.y, y1 = end.y;
int a, b, d2, x, y, flag = ;
if (abs(x1 - x0) < abs(y1 - y0))
{
swap_value(x0,y0);
swap_value(x1, y1);
flag = ;
}
if (x0 > x1) {//保证x0<x1,方便判别斜率
swap_value(x0, x1);
swap_value(y0, y1);
}
a = y0 - y1;
b = x1 - x0;
d2 = *a + b;//摆脱小数点,提高效率
if (y0 < y1) {//k>0
x = x0; y = y0;
this->pdc->SetPixel(x,y,RGB(,,));
while (x < x1)
{
if (d2 < )
{
x++;
y++;
d2 =d2+ *a + *b;
}
else {
x++;
d2 =d2+ * a;
} if(flag)//|k|>1
this->pdc->SetPixel(y, x, RGB(, , ));
else
this->pdc->SetPixel(x, y, RGB(, , ));
}
}
else {//k<0
x = x1;
y = y1;
this->pdc->SetPixel(x, y, RGB(, , ));
while (x >x0)
{
if (d2 < )
{
x--;
y++;
d2 = d2- * a + * b;
}
else {
x--;
d2 =d2- * a;
} if (flag)//|k|>1
this->pdc->SetPixel(y, x, RGB(, , ));
else
this->pdc->SetPixel(x, y, RGB(, , ));
}
}
} void MidpointLine::swap_value(int & a, int & b)
{
/*
a ^= b;
b ^= a;
a ^= b;
*/
int temp = a;
a = b;
b = temp;
}
总结:
判断的准则不同也导致函数的写法跟之前Bresenham函数有很大的差别,最明显的就是中点判断法需要保证绘制直线的走向,以方便判断直线的斜率,而Bresenham算法则不需要这一点,它只需要知道tx,ty就能知道直线的走向,对于起点,终点在哪里。
并没有太大的约束。
最后来一张四种算法同时绘制直线的合照吧。。。。
最左边是DDA,依次是逐点比较法,Bresenham,中点画线法。
该工程的github链接:https://github.com/Thousandyearsofwar/DrawLine
【OpenGL学习】 四种绘制直线的算法的更多相关文章
- Java实现操作系统中四种动态内存分配算法:BF+NF+WF+FF
1 概述 本文是利用Java实现操作系统中的四种动态内存分配方式 ,分别是: BF NF WF FF 分两部分,第一部分是介绍四种分配方式的概念以及例子,第二部分是代码实现以及讲解. 2 四种分配方式 ...
- Dubbo -- 四种loadBalance负载均衡算法
Dubbo中的一个关键接口LoadBalance,dubbo是按照其中的规则来调用多台provider的服务的. 先看一下接口的实现类图: 从上图中我们可以看到dubbo提供了四种算法来实现负载均衡. ...
- 用php实现四种常见的排序算法
几种常见的排序 排序是一个程序员的基本功,对于初级phper,更是可以通过排序算法来锻炼自己的思维能力. 所谓排序,就是对一组数据,按照某个顺序排列的过程.下面就总结四种常用的php排序算法,分别是冒 ...
- 四 akka学习 四种多线程的解决方案
http://blog.csdn.net/chenleixing/article/details/44044243 四种多线程的解决方案
- 四种简单的排序算法的php实现
无聊,用php写几个排序算法,算法介绍请移步这里,这里使用php实现了几个简单的,如下 //选择排序 function selection_sort($arr){ $len = count($arr) ...
- ADO.NET基础学习-----四种模型,防止SQL注入
1.ExcuteNonQuery 执行非查询语句,返回受影响的行数. // 1.ExcuteNonQuery string sqlconn = "Data Source=wss;Initia ...
- 用MATLAB结合四种方法搜寻罗马尼亚度假问题
选修了cs的AI课,开始有点不适应,只能用matlab硬着头皮上了,不过matlab代码全网仅此一份,倒有点小自豪. 一.练习题目 分别用宽度优先.深度优先.贪婪算法和 A*算法求解"罗马利 ...
- golang实现四种排序(快速,冒泡,插入,选择)
本文系转载 原文地址: http://www.limerence2017.com/2019/06/29/golang07/ 前面已经介绍golang基本的语法和容器了,这一篇文章用golang实现四种 ...
- OpenGL学习进程(10)第七课:四边形绘制与动画基础
本节是OpenGL学习的第七个课时,下面以四边形为例介绍绘制OpenGL动画的相关知识: (1)绘制几种不同的四边形: 1)四边形(GL_QUADS) OpenGL的GL_QUADS图 ...
随机推荐
- 15 Vue计算属性和侦听器
计算属性 模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的. 在模板中放入太多的逻辑会让模板过重且难以维护.例如: split = 字符中间空格分割, reverse= 反转 join('' ...
- ES6学习记录(一)
Class类 Class的静态方法 类相当于实例的原型,所有在类中定义的方法,都会被实例继承.如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态 ...
- mysql语句实现年龄分布
select nnd as '年龄段',count(*) as '人数' from( select case when age>= and age<= then '1-10' when a ...
- 第三章 基本的bash shell命令
1.硬链接:等同于引用了原文件,并未产生新的文件,不同的硬链接共用一个inode 2.符号链接:创建的是一个新文件,新文件指向原文件,因为是不同的文件,所以有不同的inode
- Android工程的合并
http://www.xuanyusong.com/archives/3395 1.游戏包名( 类似 com.xx.xxx ) Android应用程序只能有一个包名,如果两个游戏包名一样,那么后者安装 ...
- docker笔记--docker 各系统安装
在线安装 Docker 在 CentOS/RHEL 中安装 Docker 在终端中运行下面的命令安装 Docker. sudo yum install -y yum-utils sudo yum-co ...
- [APIO2017]商旅——分数优化+floyd+SPFA判负环+二分答案
题目链接: [APIO2017]商旅 枚举任意两个点$(s,t)$,求出在$s$买入一个物品并在$t$卖出的最大收益. 新建一条从$s$到$t$的边,边权为最大收益,长度为原图从$s$到$t$的最短路 ...
- ICEM-圆锥的一种画法(2D转3D)
原视频下载地址:https://pan.baidu.com/s/1jIOEelo 密码: btap
- JS-七大查找算法
顺序查找 二分查找 插值查找 斐波那契查找 树表查找 分块查找 哈希查找 查找定义:根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素(或记录).查找算法分类:1)静态查找和动态查找:注 ...
- mysql中查询某个字段重复的数据
SELECT corp_name,count(*) as sums FROM corp_tax WHERE corp_year = 2018 AND corp_month = 8 group by c ...