计算机图形学-扫描转换直线段-直线方程法-DDA算法-中点算法-OPENGL实现-详解
扫描转换直线段
- 说明与环境配置
- 环境配置
- 扫描转换直线段
- 方法一: 直线方程法
- 代码描述: 算法比较简单, 暂无代码.
- 方法二: 数字差分分析DDA算法
- 代码描述:
- 方法三: 中点算法
- 代码描述:
- 所有代码下载与效果展示:
说明与环境配置
生成一个线段的方法主要有三种:
- 直线方程法
- 数字差分分析DDA算法
- 中点算法, (Bresenham 算法)
我所采用的实现方式:
OPENGL1.1库 + VS2019
环境配置
https://blog.csdn.net/BoyInC0de/article/details/90079870
扫描转换直线段
方法一: 直线方程法
该方法根据直线的几何方程来群定线段路径上的像素位置
方程: y=mx+by=mx + by=mx+b
通俗来讲, 只要确定m\ m m和b\ b b, 就可以通过不断将xi\ x_i xi代入方程计算像素(xi,yi)\ (x_i, y_i) (xi,yi)的值
那么对于区间[x0,x1]\ [x_0,x_1] [x0,x1]只要将其离散化到k0,k1....kn\ k_0,k_1....k_n k0,k1....kn,
其中 ki+1=ki+1,k0=x0,kn=x1\ k_{i+1} = k_i+1, k_0=x_0, k_n=x_1 ki+1=ki+1,k0=x0,kn=x1
我们就可以通过yi=mxi+b\ y_i=mx_i+b yi=mxi+b计算纵坐标
然后通过取整得到y\ y y坐标
取整:
{(ki,yi)}i=0n→{(ki,yi,r)}i=0n\left\{ (k_i,y_i) \right\}_{i=0}^n \to \left\{ (k_i,y_{i,r}) \right\}_{i=0}^n {(ki,yi)}i=0n→{(ki,yi,r)}i=0n
yi,r=round(yi)=(int)(yi+0.5)\ y_{i,r} = round(y_i)=(int) (y_i + 0.5) yi,r=round(yi)=(int)(yi+0.5)
和后两种算法相比, 该算法具有很明显的弊端.
运算特点: 乘法+加法+取整 浮点运算
代码描述: 算法比较简单, 暂无代码.
方法二: 数字差分分析DDA算法
方法:
yi+1=mxi+1+b=m(xi+1)+b=mxi+b+m=yi+m\begin{aligned} y_{i+1} & = mx_{i+1}+b \\ & = m(x_i+1) + b \\ & = mx_i + b + m \\ & = y_i + m \\ \end{aligned} yi+1=mxi+1+b=m(xi+1)+b=mxi+b+m=yi+m
运算特点: 加法+取整 浮点运算
代码描述:
void CALLBACK draw(){ //都是全局变量glColor3f(rc, gc, bc);m = (float)dy / dx;y = y_0;for (x = x0; x <= x1; x++) {glBegin(GL_POINTS);glVertex2i(x,(int)(y+0.5));glEnd();y += m;}glFinish();
}
方法三: 中点算法
方法:
直线的隐式方程: F(x,y)=Ax+By+C=0F(x,y)=Ax+By+C=0F(x,y)=Ax+By+C=0
该式中:
A=y0−y1=−ΔyB=x1−x0=ΔxC=x0∗y1−x1∗y0\begin{aligned} A &= y_0 - y_1 = -\Delta y \\ B &= x_1 - x_0 = \Delta x \\ C &= x_0 * y_1 - x_1*y_0 \\ \end{aligned} ABC=y0−y1=−Δy=x1−x0=Δx=x0∗y1−x1∗y0
直线具有正负划分性,
直线上方的点: F(x,y)>0\ F(x,y) \gt 0 F(x,y)>0
直线下方的点: F(x,y)<0\ F(x,y) \lt 0 F(x,y)<0
直线上的点: F(x,y)=0\ F(x,y) = 0 F(x,y)=0
有了正负划分性, 我们可以想象像素点组成了一条蛇, 它沿着直线, 在直线两侧游走.
一会大于0, 一会小于0.
那么这条蛇的出发点就是(x0,y0)\ (x_0,y_0) (x0,y0), 那么它的下一个点应该是什么呢?
也就是说根据(xi,yi,r)\ (x_i,y_{i,r}) (xi,yi,r) 如何确定他的下一个像素点(xi+1,yi+1,r)\ (x_{i+1},y_{i+1,r}) (xi+1,yi+1,r), (y_{i,r}是取整后的坐标)
这里我们根据所取点间的中点M与直线的位置构造一个函数判别式:
d=F(M)=F(xi+1,yi,r+0.5)d=F(M)=F(x_i+1,y_{i,r}+0.5) d=F(M)=F(xi+1,yi,r+0.5)
这个函数的意义是 M点位于直线的上方还是下方, 也就是说, 算完(xi,yi,r)\ (x_i,y_{i,r}) (xi,yi,r)以后, xi\ x_i xi加上1也就是点E\ E E
再点E再向上加0.5个点位, 就是点M\ M M, 而我们只要将M\ M M坐标代入直线方程,
就可以根据正负来判断它是在直线上面还是下面
- 如果d≥0\ d\geq0 d≥0, 中点M在线段上方, 取点E
- 如果d<0\ d\lt0 d<0, 中点M在线段下方, 取点NE
接下来取包括初始点在内的连续三个点, 做一个计算, 参考图:
初始点: (x0,y0)\ (x_0,y_0) (x0,y0) , 中点: M(x0+1,y0+0.5)\ M(x_0+1,y_0+0.5) M(x0+1,y0+0.5) 代入F(x,y)\ F(x,y) F(x,y)
设d=F(x0+1,y0+0.5)=A(x0+1)+B(y0+0.5)+C=(y0−y1)(x0+1)+(x1−x0)(y0+0.5)+x0y1−x1y0=y0−y1+0.5(x1−x0)=A+0.5B\begin{aligned} 设d=F(x_0+1,y_0+0.5) &= A(x_0+1) + B(y_0 + 0.5) + C \\ &= (y_0-y_1)(x_0+1)+(x_1-x_0)(y_0+0.5)+x_0y_1-x_1y_0 \\ &= y_0-y_1+0.5(x_1-x_0) \\ &= A+0.5B \end{aligned} 设d=F(x0+1,y0+0.5)=A(x0+1)+B(y0+0.5)+C=(y0−y1)(x0+1)+(x1−x0)(y0+0.5)+x0y1−x1y0=y0−y1+0.5(x1−x0)=A+0.5B
接下来判定再下一个像素, (xi+2,yi+1,r)\ (x_{i}+2, y_{i+1,r}) (xi+2,yi+1,r)
第一种情况上一点的:d≥0\ d\geq0 d≥0:
初始点: (x0,y0)\ (x_0,y_0) (x0,y0) , 第一个点: M(x0+1,y0)\ M(x_0+1,y_0) M(x0+1,y0), 那么第二个点(x0+2,y0+0.5)\ (x_0+2,y_0+0.5) (x0+2,y0+0.5) 代入F(x,y)
F(x0+2,y0+0.5)=A(x0+2)+B(y0+0.5)+C=(y0−y1)(x0+2)+(x1−x0)(y0+0.5)+x0y1−x1y0=(y0−y1)+0.5(x1−x0)+(y0−y1)=A+0.5B+A=d+A\begin{aligned} F(x_0+2,y_0+0.5) &= A(x_0+2) + B(y_0 + 0.5) + C \\ &= (y_0-y_1)(x_0+2)+(x_1-x_0)(y_0+0.5)+x_0y_1-x_1y_0 \\ &=(y_0-y_1)+0.5(x_1-x_0) + (y_0 - y_1) \\ &= A+0.5B+A \\ &=d + A \end{aligned} F(x0+2,y0+0.5)=A(x0+2)+B(y0+0.5)+C=(y0−y1)(x0+2)+(x1−x0)(y0+0.5)+x0y1−x1y0=(y0−y1)+0.5(x1−x0)+(y0−y1)=A+0.5B+A=d+A
第二种情况上一点的:d<0\ d\lt0 d<0:
初始点: (x0,y0)\ (x_0,y_0) (x0,y0) , 第一个点: M(x0+1,y0+0.5)\ M(x_0+1,y_0+0.5) M(x0+1,y0+0.5), 那么第二个点(x0+2,y0+1.5)\ (x_0+2,y_0+1.5) (x0+2,y0+1.5) 代入F(x,y)
F(x0+2,y0+1.5)=A(x0+2)+B(y0+1.5)+C=(y0−y1)(x0+2)+(x1−x0)(y0+1.5)+x0y1−x1y0=y0−y1+0.5(x1−x0)+(y0−y1)+(x1−x0)=A+0.5B+A+B=d+A+B\begin{aligned} F(x_0+2,y_0+1.5) &= A(x_0+2) + B(y_0 + 1.5) + C \\ &= (y_0-y_1)(x_0+2)+(x_1-x_0)(y_0+1.5)+x_0y_1-x_1y_0 \\ &= y_0-y_1+0.5(x_1-x_0)+(y_0-y_1)+(x_1-x_0) \\ &= A+0.5B+A+B \\ &=d+A+B \end{aligned} F(x0+2,y0+1.5)=A(x0+2)+B(y0+1.5)+C=(y0−y1)(x0+2)+(x1−x0)(y0+1.5)+x0y1−x1y0=y0−y1+0.5(x1−x0)+(y0−y1)+(x1−x0)=A+0.5B+A+B=d+A+B
观察第一种情况: d\ d d 的增量是A\ A A ( 即−Δy-\Delta y−Δy )
观察第二种情况: d\ d d 的增量是A+B\ A+B A+B ( 即 −(Δy−Δx)-(\Delta y-\Delta x )−(Δy−Δx) )
那么采用归纳法:
d0=A+0.5Bdi+1={di+A,di>0di+A+B,di≤0\begin{aligned} d_0 &= A+0.5B \\ d_{i+1} &= \begin{cases} d_i + A, & \text{$d_i\gt 0$} \\ d_i + A+B, & \text{$d_i\leq 0$} \end{cases} \end{aligned} d0di+1=A+0.5B={di+A,di+A+B,di>0di≤0
接下来做一个优化: 用2d来代替d
增量都是整数, 只有初始值包含小数, 可以用2d代替d, 2a改写成a + a
这样一来, 算法中只有整数变量, 不含乘除法, 可用硬件实现.
但是, 这只是斜率 0≤m≤10\leq m\leq 10≤m≤1的情况
按照上面的归纳法, 通过计算, 便可以得到以下方程:
0≤m≤10\leq m\leq 10≤m≤1:
d0=2A+Bdi+1={di+2A,di>0di+2A+2B,di≤0di<0时,y增1\begin{aligned} d_0 &= 2A+B \\ d_{i+1} &= \begin{cases} d_i + 2A, & \text{$d_i\gt 0$} \\ d_i + 2A+2B, & \text{$d_i\leq 0$} \end{cases} \\ d_i&\lt 0时, y增1 \end{aligned} d0di+1di=2A+B={di+2A,di+2A+2B,di>0di≤0<0时,y增1
m>1m\gt 1m>1:
d0=A+2Bdi+1={di+2A+2B,di>0di+2B,di≤0di>0时,x增1\begin{aligned} d_0 &= A+2B \\ d_{i+1} &= \begin{cases} d_i + 2A +2B, & \text{$d_i\gt 0$} \\ d_i +2B, & \text{$d_i\leq 0$} \end{cases} \\ d_i&\gt 0时, x增1 \end{aligned} d0di+1di=A+2B={di+2A+2B,di+2B,di>0di≤0>0时,x增1
−1≤m≤0-1\leq m\leq 0−1≤m≤0:
d0=2A−Bdi+1={di+2A−2B,di>0di+2A,di≤0di>0时,y减1\begin{aligned} d_0 &= 2A-B \\ d_{i+1} &= \begin{cases} d_i + 2A-2B, & \text{$d_i\gt 0$} \\ d_i + 2A, & \text{$d_i\leq 0$} \end{cases} \\ d_i&\gt 0时, y减1 \end{aligned} d0di+1di=2A−B={di+2A−2B,di+2A,di>0di≤0>0时,y减1
m<−1m\lt -1m<−1:
d0=A−2Bdi+1={di−2B,di>0di+2A−2B,di≤0di<0时,x增1\begin{aligned} d_0 &= A-2B \\ d_{i+1} &= \begin{cases} d_i - 2B, & \text{$d_i\gt 0$} \\ d_i + 2A - 2B, & \text{$d_i\leq 0$} \end{cases} \\ d_i&\lt 0时, x增1 \end{aligned} d0di+1di=A−2B={di−2B,di+2A−2B,di>0di≤0<0时,x增1
代码描述:
#include <windows.h>
#include <GL.H>
#include <GLAUX.H>
#include <GLU.H>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//添加这3条语句
#pragma comment (lib, "opengl32.lib")
#pragma comment (lib, "glu32.lib")
#pragma comment (lib, "glaux.lib")
//#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" ) //这句是不让控制台窗体出现,如果想要出现,去掉即可。
inline void init() //初始化
{glClearColor(0.0, 0.0, 0.0, 1.0);//黑色背景
}
//绘制图形函数
float r = 1, g = 0, b = 0;
float x = 30.0f, y = 30.0f;
float R1, R2;
float pi = 3.1415926f;
int i;
int x0; int y_0; int x1; int y_1; float rc; float gc; float bc;void CALLBACK draw()
{float dx = x1 - x0, dy = y_1 - y_0;float m = 0;if (dx != 0) m = dy / dx;else return;if (x0 > x1){int t = x1; x1 = x0; x0 = t;t = y_1; y_1 = y_0; y_0 = t;}int d = 0;int d1 = d, d2 = d;int a = y_0 - y_1, b = x1 - x0, c = x0 * y_1 - x1 * y_0;glColor3f(rc, gc, bc);d = a + a + b;d1 = a + a;d2 = (a + b) + (a + b);int x = x0;int y = y_0;for (int i = x0; i < x1; i++){if (d < 0){y++;d += d2;}else d += d1;x++;glBegin(GL_POINTS);glVertex2i(x, y);glEnd();}glFinish();
}void set(int x0, int y_0, int x1, int y_1, float rc, float gc, float bc) {::x0 = x0; ::x1 = x1; ::y_0 = y_0; ::y_1 = y_1; ::rc = rc; ::gc = gc; ::bc = bc;
}void CALLBACK change(){set(100, 500, 350, 550, 0.0f, 1.0f, 0.0f); draw();
}void main()
{auxInitDisplayMode(AUX_SINGLE | AUX_RGBA);auxInitPosition(100, 0, 1000, 1000);auxInitWindow("CGOpenGL");init();auxIdleFunc(change);auxMainLoop( draw );
}
所有代码下载与效果展示:
该代码采用包含直线段绘制的两种算法:
**中点算法, 和 DDA算法. **
分别采用两种算法描绘出4种不同斜率的直线, 外加一条单独处理的垂直x轴的直线. 有明确的注释.
虽然OpenGL1.1库文件较老, 但不论是对于教学还是实践, 对理解直线段算法都具有重要意义.
下载地址: https://download.csdn.net/download/boyinc0de/11206604
效果展示:
其中左侧图形为: 中点算法绘制
中间竖线为: 特殊处理
右侧图形为: DDA算法绘制
计算机图形学-扫描转换直线段-直线方程法-DDA算法-中点算法-OPENGL实现-详解相关推荐
- 计算机图形学:直线段的生成算法,VS实现数值微分法和中点Bresenham算法
实验要求: 实现直线段生成的两种方法: 1) 数值微分法 和 2) 中点Bresenham算法.用户用鼠标点击两个点,两个点都确定后,利用直线段的绘制算法绘制两个点之间的一条直线段.本文章仅涉及算法的 ...
- 【计算机图形学】中点画线法实现任意斜率直线的绘制
[计算机图形学]中点画线法实现任意斜率直线的绘制 一.中点画线法原理简介 1.建立基础 中点画线法的建立基础是数值微分画线法(DDA),其作为改进算法,沿用了DDA算法的增量思想,针对影响DDA算法效 ...
- 图形学画直线c语言,002计算机图形学之直线画线算法
002计算机图形学之直线画线算法 我们知道直线方程的斜截式是如下的样子: y = kx +b 在显示器上显示直线的话,如果使用如上的方程,每描一个点 需要进行一次浮点乘法,一次浮点加法,和取整操作. ...
- 【计算机图形学】中点画线法实现焦点在x、y轴上的椭圆绘制
[计算机图形学]中点画线法实现焦点在x.y轴上的椭圆绘制 一.中点画线法原理简介 1.建立基础 中点画线法的原理介绍见直线绘制的博文中点画线法实现任意斜率直线的绘制.基本思路是以下一点在椭圆外/内的位 ...
- 计算机图形学学习笔记(4.1)画线算法
前言 现在的显示器都是像素点阵.但是图形在计算机中都用连续的线段或多边形等存储.因此在显示出来之前,要进行光栅化处理. 图形的光栅化(图形的扫描转换)分成两步: 1)根据图形的定义 在点阵单元上确定最 ...
- 研究生专业课计算机科学基础,计算机学科专业基础综合科目408综合教程及历年真题详解(最新版全国硕士研究生招生考试计算机科学与技术学科联考)...
导语 内容提要 开点工作室编著的<计算机学科专业基础综合科目<408>综合教程及历年真题详解(最新版)>作为全国硕士研究生招生考试中计算机科学与技术专业的计算机专业基础综合科目 ...
- 【计算机图形学】壹 · 光栅图形学之直线段的扫描转换算法
有两点P0(x0,y0),P1(x1,y1)P_{0}(x_{0},~y_{0}),~P_{1}(x_{1},~y_{1})P0(x0, y0), P1(x1, y1)确定一条线段L(P0 ...
- 计算机图形学——生成直线的DDA算法
本博客计算机图形学系列文章索引: 地址:<计算机图形学系列相关文章索引(持续更新)> 数值微分法即DDA法(Digital Differential Analyzer),是一种基于直线的微 ...
- 计算机图形学 Bresenham直线生成算法
bresenham算法是一种光栅化的直线生成算法,是计算机图形学目前使用广泛的直线扫描转换算法,具体逻辑很简单,就是描点.所以bresenham的算法研究实际上是研究目标点的选择. 实验环境: ope ...
最新文章
- R构建lasso回归模型并获得最佳正则化系数
- Java数据库连接池知识汇总(C3P0+DBCP+Druid)
- codeblocks调试窗口字体大小以及修改主题
- Qt工作笔记-QTableWidget设置委托(使用QStyledItemDelegate画3只小猪)
- Django学习笔记5-url
- java求字符串数组交集、并集和差集
- 使用蒙特卡罗模拟期权定价
- 机器学习必看书籍推荐
- vxworks,bootrom启动时由7s减少到1s
- NPN三极管放大原理
- 代码风格检查工具vera++
- tslearn使用轮廓系数(silhouette_score)评估KShape聚类效果
- 被遗忘的SEO基础知识
- Windows10聚焦背景纯灰色
- 小学生认识计算机网络教案,小学信息技术《认识电脑键盘》教案
- 快乐万圣节南瓜ppt模板
- Generating SSH2 ECDSA host key:[FAILED]
- 什么是情商?丨附思维导图和提高情商的小建议
- 旷视三年,我学到了什么
- 设计模式 ---- 工厂模式
热门文章
- 描述 Outlook 2003 使用缓存 Exchange 模式
- python获取当前年月日_Python获取、格式化当前时间日期的方法
- 如何更改自己电脑上的公网IP?
- 蓝绿部署、滚动部署、灰度发布(金丝雀发布)
- 【实验五 一维数组】7-7 去掉重复的数据
- 网页抢东西插件_10款堪称神器的Chrome插件,让你大开眼界!
- 激光雷达和相机联合标定之cam_lidar_calibration
- 1. THE REAL-TIME VOLUMETRIC CLOUDSCAPES OF HORIZON ZERO DAWN
- 微信扫描二维码调用手机外部浏览器打开下载apk的链接
- 使用buildroot构建根文件系统遇到问题【已解决】