文章来源于
现在已经是一种很成熟的图形渲染方法了(在这之前我都不知道顶点法向有什么用23333),其大致思路是在已有的控制顶点和法向的基础上插入新的控制顶点和控制法向,利用bezier三角形分片绘制,加上顶点法向的效果将其做更好的渲染。
算法描述
来自
PN 三角形与贝塞尔三角形
贝塞尔三角形是一种特殊的贝塞尔曲面,它通过控制点和质心坐标信息来确定三次曲面上的点的位置,而PN 三角形又是贝塞尔三角形的一种特殊的实现,即PN 三角形的控制点信息是依据输入三角形的顶点位置信息和法线信息计算求得,而它的质心坐标则是通过细分着色器来进行插值并输出。
几何控制点
输入渲染管线中的三角面片的信息以顶点为单位,如图2所示,
P1
~
P3
是输入顶点的位置信息,
N1
~
N3
是输入顶点的法线信息,基于这些信息可求得控制点信息。如图3 所示,一个三角面片共有10个控制点,其中
b003
,
b300
,
b030
是原三角形的 3 个顶点,而其余的7 个控制点则是依据顶点和法线信息插入的,之后根据控制点信息和细分着色器输出的质心坐标信息共同计算出插入点的位置。
控制点的计算
1) 如图4所示,
bijk
所对应的三角面片上的插入点
P0=iP1+jP2+kP33
;
2) 距
P0
点最近的顶点作为一个平面中的点,以该顶点的法线为该平面的法线;
3)
P0
点在上述平面上的投影即为所求的控制点
bijk
。
求
b210
的过程为:首先计算插入点
P0
,其中,
i=2,j=1,k=0
,之后找到点
P1
和它的法线
N1
所对应的平面,最后将点
P0
投影到前述的平面上,即可得到控制点
b210
。控制点的求法类似,而
b111
则有些特别,求法为:
E=b210+b120+b021+b012+b102+b2016
,
V=P1+P2+P33
,
b111=E+E−V2
。
b111
的值是E 加上从V 到E 向量方向的偏移的结果。
法线控制点
PN 三角形共有6 个法线控制点(如图5 所示),其中顶点处的3 个为顶点的法线向量,而
n110,n011,n101
则是插入的控制点信息,这些控制点信息和细分后的质心坐标共同影响着新顶点的法线向量的插入结果。法线向量的插入可以选择线性的,也可以选择二次的,鉴于PN 三角形点的插入是三次的(即曲面的),而法线向量又是对曲面方程求偏导数的结果,因而选法线的二次插入。
基于面法线的渲染中三角面片之间的法线过渡是离散的,所以需要插入法线,如果是简单的线性插入,所
插入的法线信息可能不能反映法线反射的情况。比如,起始点和终止点的法线向量的方向相同,则在这之间插入的所有法线向量的方向与起始点和终止点的方向相同,但实际情况可能是,起始点和终止点之间的曲线可能是类似于正弦曲线的形状,而在这种情况下,所插入的法线向量的方向显然不是全都相同的。
基于上述原因,在求法线控制点时考虑镜面映射,如图 6 所示, n110 为向量 N1+N22 相对于垂直于向量 P1,P2 的平面的镜面映射,向量 P1,P2 是上述平面的法线向量,但还没有规格化,有了平面上的点 P1+P22 和平面的法线向量, n110 很容易通过 N1+N22 相对于平面法线的投影求出。设 N1+N22 在 P1,P2 上的投影为向量 H ,则
注意在这步之前的三点法向要 单位化,之后的法向也要相应的单位化
Berzier三角形
在上面的做完了之后,分别按照三次和二次Bezier三角形曲面绘制即可
算法实现及实验效果
算法实现
求控制顶点和法向的代码如下
function [ control_new,N_new ] = tran_1to22( controls,N)
%TRAN_1TO2 Summary of this function goes here
% Detailed explanation goes here
% 1,1
% / \
% 2,1--2,2
% / \ / \
% 3,1 --3,2-- 3,3
% / \ / \ / \
% 4,1--4,2-- 4,3 --4,4
control_new=cell(4,4);
control_new{1,1}=controls{1,1};
control_new{4,1}=controls{2,1};control_new{4,4}=controls{2,2};
point12_out=2/3*controls{1,1}+1/3*controls{2,1};
control_new{2,1}=compute_nearest_point_on_surface2(controls{1,1},N(1,:),point12_out);
point21_out=1/3*controls{1,1}+2/3*controls{2,1};
control_new{3,1}=compute_nearest_point_on_surface2(controls{2,1},N(2,:),point21_out);
point13_out=2/3*controls{1,1}+1/3*controls{2,2};
control_new{2,2}=compute_nearest_point_on_surface2(controls{1,1},N(1,:),point13_out);
point31_out=1/3*controls{1,1}+2/3*controls{2,2};
control_new{3,3}=compute_nearest_point_on_surface2(controls{2,2},N(3,:),point31_out);
point23_out=2/3*controls{2,1}+1/3*controls{2,2};
control_new{4,2}=compute_nearest_point_on_surface2(controls{2,1},N(2,:),point23_out);
point32_out=1/3*controls{2,1}+2/3*controls{2,2};
control_new{4,3}=compute_nearest_point_on_surface2(controls{2,2},N(3,:),point32_out);
E=1/6*(control_new{2,1}+control_new{3,1}+control_new{2,2}+control_new{3,3}+control_new{4,2}+control_new{4,3});
V=1/3*(control_new{1,1}+control_new{4,1}+control_new{4,4});
control_new{3,2}=E+1/2*(E-V);
for i=1:3
S=sqrt(N(i,1)^2+N(i,2)^2+N(i,3)^2);
N(i,1)=N(i,1)/S;
N(i,2)=N(i,2)/S;
N(i,3)=N(i,3)/S;
end
N_new=zeros(6,3);
N_new(1,:)=N(1,:);
N_new(4,:)=N(2,:);
N_new(6,:)=N(3,:);
pn12=point12_out+0.5*(N(1,:)+N(2,:));
pn12sur=compute_nearest_point_on_surface2(point12_out,controls{1,1}-controls{2,1},pn12);
pn12_2=2*pn12sur-pn12;
N_new(2,:)=pn12_2-point12_out;
pn13=point13_out+0.5*(N(1,:)+N(3,:));
pn13sur=compute_nearest_point_on_surface2(point13_out,controls{1,1}-controls{2,2},pn13);
pn13_2=2*pn13sur-pn13;
N_new(3,:)=pn13_2-point13_out;
pn23=point23_out+0.5*(N(2,:)+N(3,:));
pn23sur=compute_nearest_point_on_surface2(point23_out,controls{2,1}-controls{2,2},pn23);
pn23_2=2*pn23sur-pn23;
N_new(5,:)=pn23_2-point23_out;
for i=1:6
S=sqrt(N_new(i,1)^2+N_new(i,2)^2+N_new(i,3)^2);
N_new(i,1)=N_new(i,1)/S;
N_new(i,2)=N_new(i,2)/S;
N_new(i,3)=N_new(i,3)/S;
end
end
然后绘制三角形的代码在绘制Bezier三角面片当中
试验效果
棒棒哒~~