计算机图形学-扫描转换直线段-直线方程法-DDA算法-中点算法-OPENGL实现-详解

    xiaoxiao2023-11-03  149

    扫描转换直线段

    说明与环境配置环境配置 扫描转换直线段方法一: 直线方程法代码描述: 算法比较简单, 暂无代码. 方法二: 数字差分分析DDA算法代码描述: 方法三: 中点算法代码描述: 所有代码下载与效果展示:

    说明与环境配置

    生成一个线段的方法主要有三种:

    直线方程法数字差分分析DDA算法中点算法, (Bresenham 算法) 我所采用的实现方式: OPENGL1.1库 + VS2019

    环境配置

    https://blog.csdn.net/BoyInC0de/article/details/90079870

    扫描转换直线段

    方法一: 直线方程法

    该方法根据直线的几何方程来群定线段路径上的像素位置

    方程: y = m x + b y=mx + b y=mx+b 通俗来讲, 只要确定   m \ m  m   b \ b  b, 就可以通过不断将   x i \ x_i  xi代入方程计算像素   ( x i , y i ) \ (x_i, y_i)  (xi,yi)的值

    那么对于区间   [ x 0 , x 1 ] \ [x_0,x_1]  [x0,x1]只要将其离散化到   k 0 , k 1 . . . . k n \ k_0,k_1....k_n  k0,k1....kn, 其中   k i + 1 = k i + 1 , k 0 = x 0 , k n = x 1 \ k_{i+1} = k_i+1, k_0=x_0, k_n=x_1  ki+1=ki+1,k0=x0,kn=x1 我们就可以通过   y i = m x i + b \ y_i=mx_i+b  yi=mxi+b计算纵坐标 然后通过取整得到   y \ y  y坐标 取整: { ( k i , y i ) } i = 0 n → { ( k i , y i , r ) } i = 0 n \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   y i , r = r o u n d ( y i ) = ( i n t ) ( y i + 0.5 ) \ y_{i,r} = round(y_i)=(int) (y_i + 0.5)  yi,r=round(yi)=(int)(yi+0.5) 和后两种算法相比, 该算法具有很明显的弊端. 运算特点: 乘法+加法+取整 浮点运算

    代码描述: 算法比较简单, 暂无代码.

    方法二: 数字差分分析DDA算法

    方法: y i + 1 = m x i + 1 + b = m ( x i + 1 ) + b = m x i + b + m = y i + 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 ) = A x + B y + C = 0 F(x,y)=Ax+By+C=0 F(x,y)=Ax+By+C=0 该式中: A = y 0 − y 1 = − Δ y B = x 1 − x 0 = Δ x C = x 0 ∗ y 1 − x 1 ∗ y 0 \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=y0y1=Δy=x1x0=Δx=x0y1x1y0 直线具有正负划分性, 直线上方的点:   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.

    那么这条蛇的出发点就是   ( x 0 , y 0 ) \ (x_0,y_0)  (x0,y0), 那么它的下一个点应该是什么呢? 也就是说根据   ( x i , y i , r ) \ (x_i,y_{i,r})  (xi,yi,r) 如何确定他的下一个像素点   ( x i + 1 , y i + 1 , r ) \ (x_{i+1},y_{i+1,r})  (xi+1,yi+1,r), (y_{i,r}是取整后的坐标)

    这里我们根据所取点间的中点M与直线的位置构造一个函数判别式: d = F ( M ) = F ( x i + 1 , y i , 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点位于直线的上方还是下方, 也就是说, 算完   ( x i , y i , r ) \ (x_i,y_{i,r})  (xi,yi,r)以后,   x i \ x_i  xi加上1也就是点   E \ E  E 再点E再向上加0.5个点位, 就是点   M \ M  M, 而我们只要将   M \ M  M坐标代入直线方程, 就可以根据正负来判断它是在直线上面还是下面

    如果   d ≥ 0 \ d\geq0  d0, 中点M在线段上方, 取点E如果   d < 0 \ d\lt0  d<0, 中点M在线段下方, 取点NE

    接下来取包括初始点在内的连续三个点, 做一个计算, 参考图: 初始点:   ( x 0 , y 0 ) \ (x_0,y_0)  (x0,y0) , 中点:   M ( x 0 + 1 , y 0 + 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 ( 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 0 y 1 − x 1 y 0 = y 0 − y 1 + 0.5 ( x 1 − x 0 ) = A + 0.5 B \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=(y0y1)(x0+1)+(x1x0)(y0+0.5)+x0y1x1y0=y0y1+0.5(x1x0)=A+0.5B 接下来判定再下一个像素,   ( x i + 2 , y i + 1 , r ) \ (x_{i}+2, y_{i+1,r})  (xi+2,yi+1,r) 第一种情况上一点的:   d ≥ 0 \ d\geq0  d0: 初始点:   ( x 0 , y 0 ) \ (x_0,y_0)  (x0,y0) , 第一个点:   M ( x 0 + 1 , y 0 ) \ M(x_0+1,y_0)  M(x0+1,y0), 那么第二个点   ( x 0 + 2 , y 0 + 0.5 ) \ (x_0+2,y_0+0.5)  (x0+2,y0+0.5) 代入F(x,y) 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 0 y 1 − x 1 y 0 = ( y 0 − y 1 ) + 0.5 ( x 1 − x 0 ) + ( y 0 − y 1 ) = A + 0.5 B + 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=(y0y1)(x0+2)+(x1x0)(y0+0.5)+x0y1x1y0=(y0y1)+0.5(x1x0)+(y0y1)=A+0.5B+A=d+A 第二种情况上一点的:   d < 0 \ d\lt0  d<0: 初始点:   ( x 0 , y 0 ) \ (x_0,y_0)  (x0,y0) , 第一个点:   M ( x 0 + 1 , y 0 + 0.5 ) \ M(x_0+1,y_0+0.5)  M(x0+1,y0+0.5), 那么第二个点   ( x 0 + 2 , y 0 + 1.5 ) \ (x_0+2,y_0+1.5)  (x0+2,y0+1.5) 代入F(x,y) 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 0 y 1 − x 1 y 0 = y 0 − y 1 + 0.5 ( x 1 − x 0 ) + ( y 0 − y 1 ) + ( x 1 − x 0 ) = A + 0.5 B + 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=(y0y1)(x0+2)+(x1x0)(y0+1.5)+x0y1x1y0=y0y1+0.5(x1x0)+(y0y1)+(x1x0)=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) ) 那么采用归纳法: d 0 = A + 0.5 B d i + 1 = { d i + A , d i > 0 d i + A + B , d i ≤ 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>0di0 接下来做一个优化: 用2d来代替d 增量都是整数, 只有初始值包含小数, 可以用2d代替d, 2a改写成a + a 这样一来, 算法中只有整数变量, 不含乘除法, 可用硬件实现.

    但是, 这只是斜率 0 ≤ m ≤ 1 0\leq m\leq 1 0m1的情况 按照上面的归纳法, 通过计算, 便可以得到以下方程:

    0 ≤ m ≤ 1 0\leq m\leq 1 0m1: d 0 = 2 A + B d i + 1 = { d i + 2 A , d i > 0 d i + 2 A + 2 B , d i ≤ 0 d i < 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>0di0<0,y1

    m > 1 m\gt 1 m>1: d 0 = A + 2 B d i + 1 = { d i + 2 A + 2 B , d i > 0 d i + 2 B , d i ≤ 0 d i > 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>0di0>0,x1

    − 1 ≤ m ≤ 0 -1\leq m\leq 0 1m0: d 0 = 2 A − B d i + 1 = { d i + 2 A − 2 B , d i > 0 d i + 2 A , d i ≤ 0 d i > 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=2AB={di+2A2B,di+2A,di>0di0>0,y1

    m < − 1 m\lt -1 m<1: d 0 = A − 2 B d i + 1 = { d i − 2 B , d i > 0 d i + 2 A − 2 B , d i ≤ 0 d i < 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=A2B={di2B,di+2A2B,di>0di0<0,x1

    代码描述:

    #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算法绘制

    最新回复(0)