先根据经纬度计算出球面上各点,再把球分成很多小三角形,其中两极除外的四边形也分成上下二个三角形,画三角形(设为P0P1P2)时,每个点计算该面本身(pl),下面(Pdown),右面(Pright),右下面(Pdownright),求光照时,根据每个点到光源位置的经衰减后得到光线颜色,再分别计算环境光,漫反射光,镜面反射光,加点该点的颜色上(点的颜色是该点右下面的颜色,与距离衰减系数(f[i]=1.0/(c0+c1*dist[i]+c2*dist[i]*dist[i]))和面相对光源的正对率(dCosc=Dot(Lv,N1))成正比,计算时也要检测光源是否能够照到该点上,如果光照不到点上则比率为0),然后再将三维点投影到二维平面上,再根据该面是否消隐来填充三角形.
不过该程序中可能存在问题: 程序中的Lv是从原心(0,0,0)到光源lightP的一个向量,即光源的点坐标.改进方法是:对四个面,每个面添加一个中心(或其中某个点)M,令Lv=lightP-M即可.不过,程序把照到各面上的光当成平行光也未尝不可.
画三角形的源代码如下:
void CTestView::Triangle(CDC *mdc,P3d *p,bool First)//三角形面片
{
Vector vP10(P1[0]),vP11(P1[1]),vP12(P1[2]);//三角形平面片点矢量
Vector vPd0(Pdown[0]),vPd1(Pdown[1]),vPd2(Pdown[2]);
Vector vPr0(Pright[0]),vPr1(Pright[1]),vPr2(Pright[2]);
Vector vPdr0(Pdownright[0]),vPdr1(Pdownright[1]),vPdr2(Pdownright[2]);
Vector VectorN1=NormalVector(vP10,vP11,vP12);//主面片法矢量
Vector VectorNd=NormalVector(vPd0,vPd1,vPd2);//下面片法矢量
Vector VectorNr=NormalVector(vPr0,vPr1,vPr2);//右面片法矢量
Vector VectorNdr=NormalVector(vPdr0,vPdr1,vPdr2);//下右面片法矢量
Vector N1,Nd,Nr,Ndr;
N1=VectorN1.Unit();Nd=VectorNd.Unit();Nr=VectorNr.Unit();Ndr=VectorNdr.Unit();
Vector LPosition(lightP.position),Lv=LPosition.Unit();//光源向量
MyRGB Ambientc=lightP.ambient;
MyRGB Ambientd=lightP.ambient;
MyRGB Ambientdr=lightP.ambient;
MyRGB Ambientr=lightP.ambient;
Vector vP0(p[0]),Uv;
Vector VisualP(R*k[5],R*k[6],R*k[4]);//视点矢量球坐标
Vector VisualV=VisualP-vP0;//视矢量
Uv=VisualV.Unit();
if(Chk1)//环境光
{
if(First)
{
p[0].c=Ambientc;
p[1].c=Ambientd;
p[2].c=Ambientr;
}
else
{
p[0].c=Ambientd;
p[1].c=Ambientdr;
p[2].c=Ambientr;
}
}
double c0,c1,c2,dist[3];//c0常数衰减因子,c1线性衰减因子,c2二次衰减因子,dist定点与光源的距离
double f[3];
c0=0.65;c1=0.00002;c2=0.000001;
for(int i=0;i<3;i++)
{
dist[i]=sqrt((p[i].x-Positionx)*(p[i].x-Positionx)
+(p[i].y-Positiony)*(p[i].y-Positiony)
+(p[i].z-Positionz)*(p[i].z-Positionz));
f[i]=1.0/(c0+c1*dist[i]+c2*dist[i]*dist[i]);//衰减后的光系数
f[i]=Min(f[i]);
}
double dCosc,dCosd,dCosr,dCosdr;
dCosc=Dot(Lv,N1);dCosd=Dot(Lv,Nd);dCosr=Dot(Lv,Nr);dCosdr=Dot(Lv,Ndr);
dCosc=(dCosc<0.0f)?0.0f:dCosc;//如果<=0表明光照不到该面上,故为0
dCosd=(dCosd<0.0f)?0.0f:dCosd;
dCosr=(dCosr<0.0f)?0.0f:dCosr;
dCosdr=(dCosdr<0.0f)?0.0f:dCosdr;
MyRGB Diffusec=lightP.diffuse*dCosc;
MyRGB Diffused=lightP.diffuse*dCosd;
MyRGB Diffuser=lightP.diffuse*dCosr;
MyRGB Diffusedr=lightP.diffuse*dCosdr;
if(Chk2)//漫反射光
{
if(First) //计算每个点的右下平面所接收到的光
{
p[0].c=Ambientc+Diffusec*f[0]; p[1].c=Ambientd+Diffused*f[1];
p[2].c=Ambientr+Diffuser*f[2];
}
else
{
p[0].c=Ambientd+Diffused*f[0];
p[1].c=Ambientdr+Diffusedr*f[1];
p[2].c=Ambientr+Diffuser*f[2];
}
}
if(Chk3)//镜面反射光
{
double sCosc,sCosd,sCosdr,sCosr;
Vector Hvector,Hv;
Hvector=(LPosition+VisualV)*0.5;//平分矢量
Hv=Hvector.Unit();
sCosc=Dot(Hv,N1);sCosd=Dot(Hv,Nd);sCosr=Dot(Hv,Nr);sCosdr=Dot(Hv,Ndr);
for(int i=0;i<n;i++)//计算n次方
{
sCosc*=sCosc;
sCosd*=sCosd;
sCosr*=sCosr;
sCosdr*=sCosdr;
}
sCosc=(dCosc<0.0f)?0.0f:sCosc;
sCosd=(dCosd<0.0f)?0.0f:sCosd;
sCosr=(dCosr<0.0f)?0.0f:sCosr;
sCosdr=(dCosdr<0.0f)?0.0f:sCosdr;
MyRGB Specularc=lightP.specular*sCosc;
MyRGB Speculard=lightP.specular*sCosd;
MyRGB Specularr=lightP.specular*sCosr;
MyRGB Speculardr=lightP.specular*sCosdr;
if(First)
{
p[0].c=Ambientc+Diffusec*f[0]+Specularc*f[0];
p[1].c=Ambientd+Diffused*f[1]+Speculard*f[1];
p[2].c=Ambientr+Diffuser*f[2]+Specularr*f[2];
}
else
{
p[0].c=Ambientd+Diffused*f[0]+Speculard*f[0];
p[1].c=Ambientdr+Diffusedr*f[1]+Speculardr*f[1];
p[2].c=Ambientr+Diffuser*f[2]+Specularr*f[2];
}
}
for(int k=0;k<3;k++)
{
Project(p[k]);
Point[k].x=ScreenP.x;
Point[k].y=ScreenP.y;
Point[k].c=ScreenP.c;
}
if(Dot(Uv,N1)>=0)//根据数量积正负消隐
{
CreatBucket();//初始化桶
Et();//用于建立边表
SphereFill(mdc);//进行填充
}
}