图形学笔记(五) 空间图形的几何表示(加工中)

空间图形的几何表示

曲线

什么是埃尔米特曲线?

**埃尔米特曲线(Hermit)**是一种插值曲线,也是样条曲线家族中最好理解的一种曲线。

Hermit曲线给定曲线两个端点的位置矢量P0P_0P1P_1和切线矢量T0T_0T1T_1,通过插值获得曲线在定义域[0,1]上的三次参数方程:

图形学笔记(五) 空间图形的几何表示(加工中)

我们设目标参数方程为P(t)=at3+bt2+ct+dP(t)=at^3+bt^2+ct+d,则有P(t)=3at2+2bt+cP'(t)=3at^2+2bt+c,将0和1代入t易得:
{ P(0)=d P(1)=a+b+c+d P(0)=c P(1)=3a+2b+c(14) \left \{ \begin{array}{c} \ P(0)&=&d\\\ P(1)&=&a+b+c+d\\\ P'(0)&=&c\\\ P'(1)&=&3a+2b+c \end{array}\right.\tag{14}
现在我们设h0=P(0),h1=P(1),h2=P(0),h3=P(1)h_0=P(0),h_1=P(1),h_2=P'(0),h_3=P'(1),则有:
[h0h1h2h3]=[0001111100103210][abcd][abcd]=[2211332100101000][h0h1h2h3] \begin{bmatrix} h_0\\h_1\\h_2\\h_3 \end{bmatrix}=\begin{bmatrix} 0&0&0&1\\1&1&1&1\\0&0&1&0\\3&2&1&0 \end{bmatrix}\begin{bmatrix} a\\b\\c\\d \end{bmatrix}\\\Rightarrow\begin{bmatrix} a\\b\\c\\d \end{bmatrix}=\begin{bmatrix} 2&-2&1&1\\-3&3&-2&-1\\0&0&1&0\\1&0&0&0 \end{bmatrix}\begin{bmatrix} h_0\\h_1\\h_2\\h_3 \end{bmatrix}
我们将参数方程P(t)=at3+bt2+ct+dP(t)=at^3+bt^2+ct+d写成矩阵形式:
P(t)=[abcd][t3t2t1] P(t)=\begin{bmatrix} a&b&c&d \end{bmatrix}\begin{bmatrix} t^3\\t^2\\t\\1 \end{bmatrix}
将列向量abcd的表达式转置,代入上式,得到:
P(t)=[h0h1h2h3][2301230012101100][t3t2t1] P(t)=\begin{bmatrix} h_0&h_1&h_2&h_3 \end{bmatrix}\begin{bmatrix} 2&-3&0&1\\-2&3&0&0\\1&-2&1&0\\1&-1&0&0 \end{bmatrix}\begin{bmatrix} t^3\\t^2\\t\\1 \end{bmatrix}
将后两项乘起来可以得到:

图形学笔记(五) 空间图形的几何表示(加工中)

P(t)=i=03hiHi(t) P(t)=\sum_{i=0}^3h_iH_i(t)\\
其中h1=P(0)=P0h_1=P(0)=P_0h2=P(1)=P1h_2=P(1)=P_1h3=P(0)=T0h_3=P'(0)=T_0,0h4=P(1)=T1h_4=P'(1)=T_1

什么是贝塞尔曲线?

贝塞尔曲线也是参数格式的曲线,n阶贝塞尔曲线是n次参数方程,其原理图如下:

图形学笔记(五) 空间图形的几何表示(加工中)

贝塞尔曲线完全由其控制点决定其形状,n个控制点对应着n-1阶的贝塞尔曲线,并且可以通过递归的方式来绘制。贝塞尔曲线是典型的递归法曲线。

一阶贝塞尔曲线就是对两个控制点进行线性插值,其参数方程为:
B1(t)=P0+(P1P0)t=(1t)P0+tP1,t[0,1] B_1(t)=P_0+(P_1-P_0)t=(1-t)P_0+tP_1,t\in[0,1]
二阶贝塞尔曲线由三个控制点组成,假设三个控制点依次为P0、P1、P2,先分别对P0P1和P1P2两段进行线性插值得到两个新控制点P0’、P1’,将二阶贝塞尔曲线降为一阶:
P0=(1t)P0+tP1P1=(1t)P1+tP2 P'_0=(1-t)P_0+tP_1\\ P'_1=(1-t)P_1+tP_2
然后再对P0’P1’求线性插值:
B2(t)=(1t)P0+tP1=(1t)2P0+2t(1t)P1+t2P2 B_2(t)=(1-t)P'_0+tP'_1=(1-t)^2P_0+2t(1-t)P_1+t^2P_2
三阶贝塞尔曲线由四个控制点组成,假设四个控制点依次为P0、P1、P2、P3,先分别对P0P1、P1P2和P2P3三段求线性插值得到三个新控制点P0’、P1’、P2’,将三阶贝塞尔曲线降为二阶:
P0=(1t)P0+tP1P1=(1t)P1+tP2P2=(1t)P2+tP3 P'_0=(1-t)P_0+tP_1\\ P'_1=(1-t)P_1+tP_2\\ P'_2=(1-t)P_2+tP_3
然后再利用新控制点求二阶贝塞尔曲线:
P0=(1t)P0+tP1P1=(1t)P1+tP2 P''_0=(1-t)P'_0+tP'_1\\ P''_1=(1-t)P'_1+tP'_2
最后对P0’‘和P1’'进行线性插值:
B3(t)=(1t)P0+tP1=(1t)3P0+3t(1t)2P1+3t2(1t)P2+t3P3 B_3(t)=(1-t)P''_0+tP''_1=(1-t)^3P_0+3t(1-t)^2P_1+3t^2(1-t)P_2+t^3P_3
不难发现,n阶贝塞尔曲线展开式系数是n阶二项式展开系数,可以得到n阶贝塞尔曲线的通项公式:
Bn(t)=i=0nn!i!(ni)!ti(1t)niPi, t[0,1] B_n(t)=\sum_{i=0}^n\frac{n!}{i!(n-i)!}t^i(1-t)^{n-i}P_i,\ t\in[0,1]
其中,n阶贝塞尔曲线的第i项系数Bi,n(t)又被记作Bernstein基函数
Bi,n(t)=n!i!(ni)!ti(1t)n1Bn(t)=Bi,n(t)Pi, t[0,1] B_{i,n}(t)=\frac{n!}{i!(n-i)!}t^i(1-t)^{n-1}\\ B_n(t)=B_{i,n}(t)P_i,\ t\in[0,1]
对n阶贝塞尔曲线进行求导可得:
Bn(t)=i=0n1Bi,n1(t)[n(Pi+1Pi)], t[0,1] B'_n(t)=\sum_{i=0}^{n-1}B_{i,n-1}(t)[n(P_{i+1}-P_i)],\ t\in[0,1]
经观察,n阶贝塞尔曲线的导数显然是n-1阶贝塞尔曲线,其控制点为原曲线控制点的组合。

贝塞尔曲线具有端点性质:第一个控制点和最后一个控制点恰好是曲线的起点和重点。

贝塞尔曲线具有凸包性质:贝塞尔曲线始终位于包含了所有控制点的最小凸多边形中。注意,不是按控制点顺序围成的凸多边形。

贝塞尔曲线具有端点导数性质:贝塞尔曲线在P0和Pn处的斜率分别等于直线P0P1和直线Pn-1Pn的斜率,即贝塞尔曲线端点的一阶导数仅同其相邻的一条边向量有关,与更远的各边无关。推广的,贝塞尔曲线端点的r阶导数仅同其相邻的r条边向量有关,与更远的各边无关。

贝塞尔曲线最大的问题在于,高阶贝塞尔曲线的路径不够直观,很难让美术人员利用贝塞尔曲线进行设计。但我们注意到,由于贝塞尔曲线具有端点性质和端点导数性质,二阶和三阶贝塞尔曲线的形状相对直观。所以在实际使用中我们会讨论到将多个贝塞尔曲线尤其是二、三阶贝塞尔曲线拼接成直观曲线的算法。

如何拼接贝塞尔曲线?

我们先聊聊什么是连续性

在讨论曲线连续性时,我们经常见到Cn连续Gn连续的说法。如果不同曲线都用参数方程来描述,那么在连接点处n阶导数连续,就被称为Cn连续。在图形学中,一般最高只要求C2连续,因为更高阶的连续已经超出了人眼视觉能识别的层次。但Cn连续有一个致命的缺陷,那就是连续性会随参数的选取而变化,这使得Cn连续不具备普适性,不能用于广泛的学术交流。

为了避免这个缺陷,人们引入了几何连续性和自然参数方程,定义曲线的弧长s作为自然参数,这样任何参数方程都可以转换为自然参数方程,在连接点处自然参数方程的n阶导数连续,就称为Gn连续。在图形学中,一般最高也只要求G2连续。

考察G0,G1,G2连续的几何意义。G0就是满足位置连续,即两曲线端点重合。G1满足两曲线在端点上具有相同的切线方向。G2满足曲线在端点上具有相同的自然参数二阶导失,也就是对一般参数方程具有相同的曲率矢量。

曲率矢量的求法:
k=r(t)×r(t)r(t)3 k=\frac{r'(t)\times r''(t)}{|r'(t)|^3}
设两条贝塞尔曲线P和Q的参数方程P(t)和Q(t),它们的控制点序列分别是P0、P1……Pn和Q0、Q1……Qm,则满足C0和G0的连接条件是P(1)=G(0),即Pn=Q0。满足G1的连接条件是Pn-1、Pn=Q0、Q1三点共线且顺序排列。在满足G1连接的基础上,如果**|Pn-1Pn|=|Q0Q1|**,则满足C1连接。

根据端点导数性质,C2和G2的达成与Pn-2和Q2有关。记ai=Pi-Pi-1,bi=Qi-Qi-1,则对曲线P和Q的端点求其曲率,也就是曲率矢量的模,得:
KP(1)=(n1)an1×annan3KQ(0)=(m1)b1×b2mb13 K_P(1)=\frac{(n-1)|a_{n-1}\times a_n|}{n|a_n|^3}\\ K_Q(0)=\frac{(m-1)|b_1\times b_2|}{m|b_1|^3}
将Pn-1、Pn=Q0、Q1所在的公切线记为直线l,an-1与an之间的夹角记为θ,b1与b2之间的夹角记为φ,Pn-2到l的距离记为h1,Q2到l的距离记为h2。根据几何关系可知:
an1×an=an1×an×sinθ=an1×an×h1an1=anh1b1×b2=b1×b2×sinθ=b1×b2×h2b1=b1h2 |a_{n-1}\times a_n|= |a_{n-1}|\times|a_n|\times\sin\theta =|a_{n-1}|\times|a_n|\times\frac{h_1}{|a_{n-1}|}=|a_n|h_1\\ |b_1\times b_2|= |b_1|\times|b_2|\times\sin\theta =|b_1|\times|b_2|\times\frac{h_2}{|b_1|}=|b_1|h_2\\
图形学笔记(五) 空间图形的几何表示(加工中)

过端点Pn=Q0作公切线l的垂线v,过端点Pn-2和Q2分别作l的平行线交v于点A、B。连接APn-1、BQ1,过点Pn-1作线端APn-1的垂线CPn-1交v于C,过点Q1作线端BQ1的垂线DQ1交v于D。记C到l的距离为g1,D到l的距离为g2

在直角三角形ACPn-1和直角三角形BDQ1中,有:
an2=h1g1b12=h2g2 a_n^2=h_1g_1\\ b_1^2=h_2g_2
于是将曲率变形为:
KP(1)=n1nanh1an3=n1n1g1KQ(0)=m1mb1h2b13=m1m1g2 K_P(1)=\frac{n-1}{n}\frac{|a_n|h_1}{|a_n|^3}=\frac{n-1}{n}\frac1{g_1}\\ K_Q(0)=\frac{m-1}{m}\frac{|b_1|h_2}{|b_1|^3}=\frac{m-1}{m}\frac1{g_2}
满足G2的条件是KQ(0)=βKP(1),此时:
β=n(m1)m(n1)g1g2 \beta=\frac{n(m-1)}{m(n-1)}\frac{g_1}{g_2}
满足C2的条件是β=1且两曲线具有公共的切平面,要满足β=1即:
g1=m(n1)n(m1)g2 g_1=\frac{m(n-1)}{n(m-1)}g_2
在特殊条件下,若m=n,则满足β=1的条件是g1=g2

要满足两曲线具有公共切平面,则点Pn-2、Pn-1、Pn=Q0、Q1、Q2五点必须共面,并且点Pn-2和点Q2应位于另三个顶点所在公切线的同侧。

如何获得匀速贝塞尔曲线?

我们已经得到了贝塞尔曲线的参数方程,但显然,如果将参数t作为时间变量,将物体沿曲线运动,我们很难得到一个匀速的运动效果。事实上,在靠近控制点处运动的较慢,而在曲线中段则运行的较快。同时,我们也很难在贝塞尔曲线的拼接处获得良好的平滑速度。

我们可以先求得B(t)相对于t的速度公式,我们先将t看作时间变量,在单个方向上对t求导就可以得到
v(t)=Bx(t)2+By(t)2+Bz(t)2 v(t)=\sqrt{B_x'(t)^2+B_y'(t)^2+B_z'(t)^2}
现在我们以二阶贝塞尔曲线为例,记Bx(t)2+By(t)2+Bz(t)2=At2+Bt+CB_x'(t)^2+B_y'(t)^2+B_z'(t)^2 = At^2+Bt+C,将左式展开:
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲\begin{split} …
注意到,A为完全平方式,我们设α=2P04P1+2P2\alpha = 2P_0-4P_1+2P_2,则A=αx2+αy2+αx2A=\alpha_x^2+\alpha_y^2+\alpha_x^2。同理我们记β=2P02P1\beta=2P_0-2P_1,则有C=βx2+βy2+βz2C=\beta_x^2+\beta_y^2+\beta_z^2B=2(αxβx+αzβy+αzβz)B=-2(\alpha_x\beta_x+\alpha_z\beta_y+\alpha_z\beta_z)。经整理得:
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲\begin{split} …
写成向量形式即:
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲\begin{split} …
根据这个公式得出贝塞尔曲线得长度公式L(t)L(t)
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲\begin{split} …
显然,整条曲线的长度为L(1),我们求变量t的函数T(t)T(t),使得L(T(t))=L(1)tL(T(t))=L(1)*t,则T(t)=L1[L(1)t]T(t)=L^{-1}[L(1)*t]。由于对L(t)求逆函数几乎不可能,在这里我们利用L(t)的导数v(t),使用牛顿切线法求出离散的近似解:
Tn+1=Tn+ΔLv(Tn) T_{n+1}=T_n+\frac{\Delta L}{v(T_n)}
其中ΔL\Delta L是每次前进的长度,如果按t[0,1]t\in [0,1]来计算前进的长度则:
ΔL=L(1)×tTn \Delta L = L(1)\times t -T_n
有了上面的经验,我们再来求三阶贝塞尔曲线的TnT_n,记Bx(t)2+By(t)2+Bz(t)2=At4+Bt3+Ct2+Dt+EB_x'(t)^2+B_y'(t)^2+B_z'(t)^2 = At^4+Bt^3+Ct^2+Dt+E,将左式展开得:
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲\begin{split} …
观察到,A和E为完全平方式,我们取α=3P09P1+9P23P3\alpha=3P_0-9P_1+9P_2-3P_3,则A=αx2+αy2+αz2A=\alpha_x^2+\alpha_y^2+\alpha_z^2。取β=3P03P1\beta=3P_0-3P_1,则E=βx2+βy2+βz2E=\beta_x^2+\beta_y^2+\beta_z^2。注意到,D是β的倍数,我们取γ=3P06P1+3P2\gamma=3P_0-6P_1+3P_2,有D=4(βxγx+βyγy+βzγz)D=-4(\beta_x\gamma_x+\beta_y\gamma_y+\beta_z\gamma_z)。类似的,又得到B=4(αxγx+αyγy+αzγz)B=-4(\alpha_x\gamma_x+\alpha_y\gamma_y+\alpha_z\gamma_z)

C的推导过程比较繁琐,但核心思路如下:设a=m3m2,b=m2m1,c=m1m0a=m_3-m_2,b=m_2-m_1,c=m_1-m_0,将C配方得到与a、b、c、a+b、b+c、a+b+c有关得表达式,然后展开得到C=18[2b2+3c2+ac6bc]C=18[2b^2+3c^2+ac-6bc],易得:
C=mm{x,y,z}4γm2+2αmβm C=\sum_m^{m\in \{x,y,z\}}4\gamma_m^2+2\alpha_m\beta_m
最后整理整个公式:
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲\begin{split} …
写成向量形式即:
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲\begin{split} …

什么是B样条曲线?

仅修改贝塞尔曲线的一个控制点,也会影响到整个曲线的形状。为了弥补这个缺陷,deBoor发明了B样条曲线,B样条曲线基于deBoor-Cox递推公式实现。

B样条曲线本质上是对多段短贝塞尔曲线的连续拼接。

n段p次B样条曲线由n+1个控制点P0、P1……Pn练成的n段的控制折线,和一个m维节点向量U={u0,u1,……,um}组成。

其中ui节点(pnots),m个节点将完整的B样条曲线划分成了m-1个曲线段,每个曲线段都定义在一个节点区间上。对p次的B样条曲线来说,这m-1个曲线段都是p次的贝塞尔曲线。

如果ui=ui+1=……=ui+k-1,那么ui节点被称为一个重复度(multiplicity)为k的多重节点1<kp1<k\le p,节点的重复度也被称为度数。否则如果一个节点只出现一次,那么这就是一个简单节点。如果u1-u0=u2-u1=……=um-um-1,即节点等距,则称节点向量为均匀的,称B样条曲线为均匀B样条曲线,否则称节点向量为非均匀的,称B样条曲线为非均匀B样条曲线。

p次B样条曲线要求满足m=n+p+1,显然,不同于贝塞尔曲线,B样条曲线的次数与控制点的数量无关。贝塞尔曲线中的t取值范围是[0,1],而B样条曲线中的u取值范围可能比[0,1]更小。

B样条曲线和贝塞尔曲线的核心差别是,B样条曲线用B样条基函数替代Bernstein基函数,B样条曲线的公式为:
C(u)=i=0nNi,p(u)Pi C(u)=\sum_{i=0}^nN_{i,p}(u)P_i
其中Ni,p(u)是第i个p次B样条基函数,也叫调和函数,或者p次规范B样条基函数。这个函数使用deBoor-Cox递推公式,其定义如下:
Ni,0(u)={1uiu<ui+10otherwiseNi,p(u)uuiui+puiNi,p1(u)+ui+p+1uui+p+1ui+1Ni+1,p1(u) N_{i,0}(u)=\left \{ \begin{array}{c} 1&u_i\le u<u_{i+1}\\0&otherwise \end{array}\right.\\ N_{i,p}(u)\frac{u-u_i}{u_{i+p}-u_i}N_{i,p-1}(u)+\frac{u_{i+p+1}-u}{u_{i+p+1}-u_{i+1}}N_{i+1,p-1}(u)

注:在该递推公式中认为0÷0=0。

可见,B样条基函数的形态主要受ui的取值影响。以U={0,0.25,0.5,0.75,1}的2段2次B样条曲线为例直观的感受B样条基函数的形象:

图形学笔记(五) 空间图形的几何表示(加工中)
图形学笔记(五) 空间图形的几何表示(加工中)
图形学笔记(五) 空间图形的几何表示(加工中)

可以归纳得出,基函数Ni,p(u)在区间[ui,ui+p+1)上非零。反过来,在区间[ui,ui+1)上,共有p+1个p次基函数非零,分别是:Ni-p,p(u),Ni-p+1,p(u),Ni-p+2,p(N)……Ni-1,p(u)和Ni,p(u)。

当计算Ni,p(u)时,它在区间[ui,ui+p+1)上非零。在deBoor-Cox递推公式中,Ni,p(u)被拆解为一个Ni,p-1(u)项和一个Ni+1,p-1(u)项,前者在[ui.,ui+p)上非零,如果u∈[ui.,ui+p),那么uuiui+pui\frac{u-u_i}{u_{i+p}-u_i}恰好是u和这个区间左端的距离与这个区间长度的比;同理,后者在[ui+1,ui+p+1)上非零,如果u∈[ui+1,ui+p+1),那么ui+p+1uui+p+1ui+1\frac{u_{i+p+1}-u}{u_{i+p+1}-u_{i+1}}恰好是u和这个区间右端的距离与这个区间长度的比。如图所示:

图形学笔记(五) 空间图形的几何表示(加工中)

B样条线的形状与节点的度数直接关联,两种具有特殊度数格式的B样条曲线如下:

  • 均匀B样条曲线

    节点成等差数列均匀分布。

图形学笔记(五) 空间图形的几何表示(加工中)

  • 准均匀B样条曲线

    在均匀B样条曲线的基础上,两端节点的度数为p+1,中间节点的度数为p。

图形学笔记(五) 空间图形的几何表示(加工中)

一般的B样条曲线称为开B样条曲线,即曲线不会与控制折线的第一边和最后一边接触,对于开B样条曲线,其定义域为[up,um-p]。可见,均匀B样条曲线就是一种特殊的开B样条曲线。

强制第一个节点和最后一个节点的重复度为p+1,那么产生的曲线就会分别与第一边和最后一边相切,这样的曲线被称为clamped-B样条曲线。可见,准均匀B样条曲线就是一种特殊的clamped-B样条曲线。

通过重复节点和控制点,可以使产生的曲线形成闭环,称为闭B样条曲线。生成闭B样条曲线的方法有两种:

  • Wrapping控制点

    设计一个m+1个节点的节点序列U={u0=0,u1=1m,u2=2m,...um1=m1m,um=1u_0=0,u_1=\frac1m,u_2=\frac2m,...u_{m-1}=\frac{m-1}m,u_m=1},对曲线的头p个和尾p个控制点,令P0=Pnp+1P_0=P_{n-p+1},P1=Pnp+2P_1=P{n-p+2},…,Pp2=Pn1P{p-2}=P_{n-1},Pp1=PnP_{p-1}=P_n。曲线的定义域为[up,unpu_p,u_{n-p}],且在连接点C(upu_p)=C(unpu_{n-p})上保证Cp-1连续性。

  • Wrapping节点

    设计n+1个控制点,并使Pn=P0P_n=P_0。先找到具有n个节点的单调节点序列U,在U基础上增加p+1个节点,并使un+1=u0,un+2=u1,...,un+p=up1,un+p+1=upu_{n+1}=u_0,u_{n+2}=u_1,...,u_{n+p}=u_{p-1},u_{n+p+1}=u_p,这样得到一个定义域为[u0,un+1u_0,u_{n+1}]的闭曲线,在连接点C(u0u_0)=C(un+1u_{n+1})上保证Cp-1连续性。

曲线是如何被光栅化的?

曲线是通过被细分成许多段直线进行光栅化的,所以我们必须先了解平面上直线的光栅化。

设直线的斜率为k,当k=0,k=±1,k=±k=0,k=\pm1,k=\pm\infty时,直线的光栅化方法显而易见。所以我们只考虑在0<k<10\lt|k|\lt1的情况下直线的光栅化算法,这样也可以类比推出在1<k<1<|k|<\infty下的算法。

DDA算法是最简单的直线光栅化算法,它的核心思路在于步进和通过舍入选择像素。

0<k<10\lt|k|\lt1时,选择对x进行步进,即xi+1=xi±1x_{i+1}=x_i\pm1,则yi+1=yi+ky_{i+1}=y_i+k。相对的,当1<k<1<|k|<\infty时,选择对y进行步进,即yi+1=yi±1y_{i+1}=y_i\pm1xi+1=xi±1kx_{i+1}=x_i\pm\frac1k。最后选择对xi+1x_{i+1}yi+1y_{i+1}进行舍入后的像素点进行光栅化。

中点画线法是经典的直线光栅化算法。它相比于DDA的进步在于巧妙的避免了浮点运算,有效提升了渲染效率。

直线的一般式方程为F(x,y)=0F(x,y)=0,这条直线将平面分为三个区域:直线上方的点、直线下方的点以及直线上的点。其中,对于直线上方的点,F(x,y)>0F(x,y)>0;直线下方的点,F(x,y)<0F(x,y)<0;直线上的点,F(x,y)=0F(x,y)=0

中点画线法也使用步进思想,但它在每次移动时,根据中点误差项判断而非舍入来决定是在水平/竖直方向上移动一个像素,还是在斜方向上移动一个像素。

图形学笔记(五) 空间图形的几何表示(加工中)

在上图中,像素点P(xi,yi)P(x_i,y_i)已经被选中,接下来直线既穿过了像素点又PdP_d穿过了像素点PuP_u,我们应当在这两个像素中选择一个进行绘制。我们考察直线与线段PdPuP_dP_u的交点QQ,如果它更靠近PdP_d则绘制PdP_d,否则绘制PuP_u

于是我们将PuP_uPdP_d的中点MM纳入考量,将MM点坐标代入方程F(xi,yi)F(x_i,y_i),即,如果结果小于0,则MMQQ下方,取PuP_u,如果结果大于0,则MMQQ上方,取PdP_d。如果恰等于0,则两者皆可。

我们记di=F(xi+1,yi+0.5)d_i=F(x_i+1,y_i+0.5),当然,每次移动都代入方程来计算did_i是合理的,但并不高效。我们试图通过找到递推公式di+1=di+Δdd_{i+1}=d_i+\Delta d来减少代入方程带来的计算。
di=F(xi+1,yi+0.5)=A(xi+1)+B(yi+0.5)+C d_i=F(x_i+1,y_i+0.5)=A(x_i+1)+B(y_i+0.5)+C
di<0d_i<0时,取PuP_u,即右上方的像素,则
di+1=F(xi+2,yi+1.5)=A(xi+2)+B(yi+1.5)+C=di+A+B d_{i+1}=F(x_i+2,y_i+1.5)=A(x_i+2)+B(y_i+1.5)+C=d_i+A+B
di0d_i \ge 0时,取PdP_d,即右方的像素,则
di+1=F(xi+2,yi+0.5)=A(xi+2)+B(yi+0.5)+C=di+A d_{i+1}=F(x_i+2,y_i+0.5)=A(x_i+2)+B(y_i+0.5)+C=d_i+A
总结可得:
di+1={di+A+Bd<0di+Ad0          ,d0=A+0.5B d_{i+1}= \left \{ \begin{array}{c} d_i+A+B&d<0\\d_i+A&d\ge 0 \end{array}\right.\ \ \ \ \ \ \ \ \ \ ,d_0=A+0.5B

由于d只关心符号,所以可以使用2d代替d来避免浮点运算,进一步提升性能。

Bresenham算法与中点画线法实际上是同一算法,只是对算法的解释不同,在此不多赘述。

在光栅化曲线时,只需要将曲线参数方程映射到像素坐标系下,对不同的参数取值将曲线细分成多条线段,在线端上应用直线光栅化算法即可。

曲面

什么是贝塞尔曲面?

我们先复习贝塞尔曲线的定义:
Bn(t)=i=0nBi,n(t)Pi, t[0,1] B_n(t)=\sum_{i=0}^nB_{i,n}(t)P_i,\ t\in[0,1]
其中Bi,n(t)B_{i,n}(t)是Bernstein基函数:
Bi,n(u)=n!i!(ni)!ui(1u)n1 B_{i,n}(u)=\frac{n!}{i!(n-i)!}u^i(1-u)^{n-1}

贝塞尔曲面本质上就是先对每一行做贝塞尔曲线,然后将这些贝塞尔曲线的结果作为列贝塞尔曲线的控制点。

我们用两个参数u、v代替t,分别在两个方向上描述贝塞尔曲面的形状,相对应的,我们需要n×m个控制点控制整个曲面,记控制点为Pij,in,jmP_{ij},i\in n,j\in m

图形学笔记(五) 空间图形的几何表示(加工中)

在上图中,qi(u)q_i (u)由每行的m个控制点控制,是一条m-1阶的贝塞尔曲线。即:
qi(u)=j=0mBj,n(u)Pij q_i(u)=\sum_{j=0}^mB_{j,n}(u)P_{ij}
紧接着,这n条贝塞尔曲线qi(u)q_i (u)u=u0u=u_0上取得了n个点,将这n个点作为v方向上的贝塞尔曲线的控制点得到整个贝塞尔曲面的方程:
P(u,v)=i=0nBi,n(v)qi(u)P(u,v)=i=0n [Bi,n(v)j=0mBj,n(u)Pij] P(u,v)=\sum_{i=0}^nB_{i,n}(v)q_i(u)\\ P(u,v)=\sum_{i=0}^n\ [B_{i,n}(v)\sum_{j=0}^mB_{j,n}(u)P_{ij}]
显然,贝塞尔曲面的定义域是参数范围[0,1]×[0,1]。

什么是B样条曲面?

B样条曲面的思路和贝塞尔曲面一致,我们也用两个参数u、v代替t,使用n×m个控制点,则相对的,我们需要节点矢量U={u0,u1,…,un+p}和V={v0,v1,…vm+q},他们分别由n+p+1和m+q+1个节点组成,它们的要求和B样条曲线中一致。

只需将贝塞尔曲面中的Bernstein基函数改为B样条基函数就可以得到B样条曲面的定义,在此我们先复习一下B样条基函数的deBoor-Cox递推公式:
Ni,0(u)={1uiu<ui+10otherwiseNi,p(u)uuiui+puiNi,p1(u)+ui+p+1uui+p+1ui+1Ni+1,p1(u) N_{i,0}(u)=\left \{ \begin{array}{c} 1&u_i\le u<u_{i+1}\\0&otherwise \end{array}\right.\\ N_{i,p}(u)\frac{u-u_i}{u_{i+p}-u_i}N_{i,p-1}(u)+\frac{u_{i+p+1}-u}{u_{i+p+1}-u_{i+1}}N_{i+1,p-1}(u)
用B样条基函数替换Bernstein基函数:
P(u,v)=P(u,v)=i=0n [Ni,n(v)j=0mNj,n(u)Pij] P(u,v)=P(u,v)=\sum_{i=0}^n\ [N_{i,n}(v)\sum_{j=0}^mN_{j,n}(u)P_{ij}]
显然,B样条曲面的定义域是参数范围[up,un+1]×[vq,vq+1]。

什么是Nurbs曲面?

Nurbs非均匀有理B样条的缩写,B样条曲面是Nurbs曲面的一种特例。Nurbs建模(也被称为曲面建模)是工业3D建模的一个重要方式,另一种更常见的建模方式则是多边形建模

Nurbs相比一般B样条曲面的改进在于,通过调整曲面中控制点的权,来产生更好的平滑性。我们记WijW_{ij}为控制点PijP_{ij}的权值,除此之外的定义都与B样条曲线相同。

下面直接给出Nurbs曲面的参数方程:
S(u,v)=i=0nj=0mNi,p(u)Nj,q(v)WijPiji=0nj=0mNi,p(u)Nj,q(v)Wij S(u,v)=\frac {\sum_{i=0}^n\sum_{j=0}^mN_{i,p}(u)N_{j,q}(v)W_{ij}P_{ij} } {\sum_{i=0}^n\sum_{j=0}^mN_{i,p}(u)N_{j,q}(v)W_{ij}}
其中Ni,p(u)N_{i,p}(u)是B样条基函数。

显然,Nurbs曲面的定义域也是参数范围[up,un+1]×[vq,vq+1]。

曲面如何进行渲染?

由于曲面是矢量图形,我们采用和曲线一样的渲染思路,在参数空间上以任意精细度对参数点进行采样,将采样结果组成四边形或三角形,然后就可以以多边形的方式对其进行渲染。

对于更高级的渲染方案,可以通过在距离摄像机近处密集采样,距离摄像机远除粗略采样,来提升渲染性能。

多边形

多变形是如何被光栅化的?
什么是消隐算法?
如何进行多边形的三角形化?