y = k x + b y = kx + b y=kx+b
x每增加1,y增量为k
|k|要小于1,大于则互换x,y
原因:防止画出来的线太稀疏
象限 | |dx|>|dy|? | D x | D y |
---|---|---|---|
1a | 是 | 1 | k |
1b | 否 | 1/k | 1 |
2a | 是 | -1 | k |
2b | 否 | -1/k | 1 |
3a | 是 | -1 | -k |
3b | 否 | -1/k | -1 |
4a | 是 | 1 | -k |
4b | 否 | 1/k | -1 |
y = a x + b y + c y = ax + by +c y=ax+by+c
其中, a = y 0 − y 1 , b = x 1 − x 0 a=y_0-y_1,b=x_1-x_0 a=y0−y1,b=x1−x0
我们每次都把中点(x+1,y+0.5)带入方程,记结果为d,与0比较
d≥0 中点在直线上方,取 ( x i + 1 , y i ) (x_i+1,y_i) (xi+1,yi)
d<0 中点在直线下方,取 ( x i + 1 , y i + 1 ) (x_i+1,y_i+1) (xi+1,yi+1)
其中,初始值 d 0 = a + 0.5 b d_0 = a+0.5b d0=a+0.5b
可以用 2d 代替 d 来摆脱小数,提高效率。
我们计算直线的斜率 k = d y d x k=\frac{dy}{dx} k=dxdy
和DDA一样,x每增加1,y增加k
于是,我们可以这样算:
d 0 = 0 d_0=0 d0=0 d i + 1 = d i + k d_{i+1} = d_i+k di+1=di+k
当 d≥1 的时候 d = d − 1 d = d-1 d=d−1
当然,为了便于计算,可令 e = d − 0.5 e = d-0.5 e=d−0.5
e 0 = − 0.5 e_0=-0.5 e0=−0.5 e i + 1 = e i + k e_{i+1} = e_i+k ei+1=ei+k
当 e≥0 的时候 e = e − 1 e = e-1 e=e−1
d = x 2 + y 2 − R 2 d = x^2+y^2-R^2 d=x2+y2−R2
与前面类似 d 0 = 1.25 − R d_0=1.25-R d0=1.25−R
为方便计算,使用e=d-0.25代替d
作射线求交点各数k
特殊情况:
与各顶点连线,形成有向角 θ i \theta_i θi,然后作累加
举个例子,6号扫描线与多边形交点并按x值递增排序为A,B,C,D
然后我们就开始配对,AB一对,CD一堆,并给AB和CD这两个线段填色。
依此类推,就能给多边形填完色。
活性边表: x x x △ x △x △x y m a x y_{max} ymax
边表桶:
特殊情况:
对多边形上每一条非水平边上的每个象素开始向右求余
改进后有栅栏填充算法和边界标志算法
种子入栈
栈非空,转3;栈空,结束
栈顶元素出栈,如果它未填充则填充,然后找其他方向未填充的点,将其入栈,找完所有方向后(4联通就是4个方向,8联通就是8个方向),转2
假设方向是上下左右,那么顺序就是
- 栈:H
- 填色:H 栈: I F J
- 填色:J 栈:I F K N
- 填色:N 栈:I F K M
- 填色:M 栈:I F K L
- 填色:L 栈:I F K
- 填色:K 栈:I F
- 填色:F 栈:I D G
- 填色:G 栈:I D E
- 填色:E 栈:I D
- 填色:D 栈:I C
- 填色:C 栈:I B
- 填色:B 栈:I A
- 填色:A 栈:I
- 填色:I
其实就是种子填充的改进版,减少了递归次数
扫描线种子填充算法
初始化:堆栈置空。将种子点(x,y)入栈。
出栈:若栈空则结束。否则取栈顶元素(x,y),以y作为当前扫描线。
填充并确定种子点所在区段:从种子点(x,y)出发,沿当前扫描线向左、右两个方向填充,直到非内部。分别标记区段的左、右端点坐标为xl和xr。
并确定新的种子点:在区间[xl,xr]中检查与当前扫描线y上、下相邻的两条扫描线上的象素。若存在非边界、未填充的象素,则把每一区间的最右象素作为种子点压入堆栈,返回第(2)步。
- 填充DFHJM
- 上下搜寻
- DFHJM任意一个往下走填充EDIK
- N往上走填充M
- D往上走填充C
- 上下搜寻
- M往上走填充L
- C往上走填充B
- 上下搜寻
- B往上走填充A
有两个方式,一个是提高分辨率,另一个是改进算法,这里当然讲算法喽
根据面积改变亮度达到反走样效果
面积计算(m是斜率,像素宽为1)
上述阴影面积是介于0-1之间的正数,用它乘以象素的最大灰度值,再取整,即可得到象素的显示灰度值。
其实就是在非加权区域采样的基础上加了个权值来表示与中心距离,然后计算灰度的时候要考虑这个权值。
D 3 D 2 D 1 D 0 D_3D_2D_1D_0 D3D2D1D0分别表示是否上于上边界,是否下于下边界,是否右于右边界,是否左于左边界
和编码的区别在于最后一步,不是交点处分为两段,而是中点处,然后不断二分,把完全在窗口外的舍弃,直到精度满足要求。
首先,我们将这个线段确定一个方向,然后将线段和窗口延长,得到四个交点,记为 r 1 , r 2 , r 3 , r 4 r1,r2,r3,r4 r1,r2,r3,r4(这也是它们的横坐标)
这样,我们得到两个入边和两个出边(PPT上叫始边和终边)
这样我们要裁剪得到的线段的两个端点的横坐标 u 1 u 2 u_1u_2 u1u2就可以确定了
那么我们要如何确定入边出边呢?
每个交点的横坐标为 r k = q k p k r_k = \frac{q_k}{p_k} rk=pkqk
如果 p k ≠ 0 p_k \neq 0 pk=0
多边形的各条边的两端点S、P。它们与裁剪线的位置关系只有四种:
情况(1)仅输出顶点P;
情况(2)输出0个顶点
情况(3)输出线段SP与裁剪线的交点 I
情况(4)输出线段SP与裁剪线的交点 I 和终点 P
这个我还没实践过,所以直接粘贴下PPT。算法思想的话,PPT上这个够看了。
简单来说,就是我们的裁剪窗口不再是矩形了,而是一个多边形。
算法思想
加入一个深度缓存数组z[m][n]
来存最小深度值
加入一个帧缓存数组FB[m][n]
来存像素点对应颜色值
每个像素点上放深度最小的多边形的颜色
扫描线的交点把这条扫描线分成了若干个区间,每个区间上必然是同样一种颜色
对于有重合的区间,如** a 6 a 7 a_6a_7 a6a7**这个区间,要么显示F2的颜色,要么显示F3的颜色,不会出现颜色的跳跃
如果把扫描线和多边形的这些交点都求出来,对每个区间,只要判断一个像素的要什么颜色,那么整个区间的颜色都解决了,这就是区间扫描线算法的主要思想。
需要的数据结构有很多,比如多边形y桶、有效多边形表APT、边Y桶、有效边表AET,具体这些是啥,看PPT吧
在规则化图像空间中,将多边形按深度Z值自小至大排序,用前面的可见多边形去切割其后面的多边形,使得最终每一个多边形要么是完全可见,要么完全不可见。
我这里写得有点点乱,以后有空再修改吧。
P i P_i Pi是控制点
有n个控制点,那么曲线的阶数就是n-1,计算量巨大
Bezier曲线的性质
这个图是PPT上的,和我下面的有些许不同,但实际是一样的。
这个图是我要讲的,下面就逐步来解释
啥是B样条曲线?
整条曲线用一个完整的表达形式,但内在的量是一段一段的,比如一堆的3次曲线拼过去,两条之间满足2次连续
怎么分段呢?
每一段的基函数怎么求?
de Boor-Cox递推定义
只要是k阶(k-1次)的B样条基函数,构造一种递推的公式,由0次构造1次,1次构造2次,2次构造3次…依次类推
B样条函数定义区间 u ∈ [ u k − 1 , u n + 1 ] u\in[u_{k-1},u_{n+1}] u∈[uk−1,un+1] 是怎么来的?
B样条曲线的性质
二维
三维
二维
三维
二维
三维
绕z轴旋转
绕x轴旋转
绕y轴旋转