版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
D圖象算法3D簡介我們首先從坐標(biāo)系統(tǒng)開始。你也許知道在2D里我們經(jīng)常使用笛卡兒坐標(biāo)系統(tǒng)在平面上來識別點(diǎn)。我們使用二維(X,Y):X表示水平軸坐標(biāo),Y表示縱軸坐標(biāo)。在3維坐標(biāo)系,我們增加了Z,一般用它來表示深度。所以為表示三維坐標(biāo)系的一個(gè)點(diǎn),我們用三個(gè)參數(shù)(X,Y,Z)。這里有不同的笛卡兒三維系統(tǒng)可以使用。但是它們都是左手螺旋或右手螺旋的。右手螺旋是右手手指的卷曲方向指向Z軸正方向,而大拇指指向X軸正方向。左手螺旋是左手手指的卷曲方向指向Z軸負(fù)方向。實(shí)際上,我們可以在任何方向上旋轉(zhuǎn)這些坐標(biāo)系,而且它們?nèi)匀槐3直旧淼奶匦?。在?jì)算機(jī)圖形學(xué),常用坐標(biāo)系為左手坐標(biāo)系(手背向上),所以我們也使用它。:
X正軸朝右
Y正軸向上
Z正軸指向屏幕里
矢量
什么是矢量?幾句話,它是坐標(biāo)集合。首先我們從二維矢量開始,(X,Y):例如矢量P(4,5)(一般,我們用->表示矢量)。我們認(rèn)為矢量P代表點(diǎn)(4,5),它是從原點(diǎn)指向(4,5)的有方向和長度的箭頭。我們談?wù)撌噶康拈L度指從原點(diǎn)到該點(diǎn)的距離。二維距離計(jì)算公式是
|P|=sqrt(x^2+y^2)
這里有一個(gè)有趣的事實(shí):在1D(點(diǎn)在單一的坐標(biāo)軸上),平方根為它的絕對值。讓我們討論三維矢量:例如P(4,-5,9),它的長度為
|P|=sqrt(x^2+y^2+z^2)
它代表在笛卡兒3D空間的一個(gè)點(diǎn)?;驈脑c(diǎn)到該點(diǎn)的一個(gè)箭頭代表該矢量。在有關(guān)操作一節(jié)里,我們討論更多的知識。
矩陣
開始,我們從簡單的開始:我們使用二維矩陣4乘4矩陣,為什么是4乘4?因?yàn)槲覀冊谌S坐標(biāo)系里而且我們需要附加的行和列來完成計(jì)算工作(本質(zhì)原因:在計(jì)算機(jī)圖形學(xué)里應(yīng)用的圖形變換,實(shí)際上是在仿射空間而不是向量空間中進(jìn)行的)。在二維坐標(biāo)系我們需要3乘3矩陣。著意味著我們在3D中有4個(gè)水平參數(shù)和4個(gè)垂直參數(shù),一共16個(gè)。例如:
4x4單位矩陣
|1000|
|0100|
|0010|
|0001|
因?yàn)槿魏纹渌仃嚺c之相乘都不改變,所以稱之為單位陣。又例如有矩陣如下:
|10
-72245|
|sin(a)cos(a)3432|
|-35
2817
6|
|45
-993216|
有關(guān)矢量和矩陣的操作
我們已經(jīng)介紹了一些非常簡單的基本概念,那么上面的知識與三維圖形有什么關(guān)系呢?
本節(jié)我們介紹3D變換的基本知識和其它的一些概念。它仍然是數(shù)學(xué)知識。我們要討論有關(guān)矢量和矩陣操作。讓我們從兩個(gè)矢量和開始:
(x1,y1,z1)+(x2,y2,z2)=(x1+x2,y1+y2,z1+z2)
幾何意義:等于兩個(gè)矢量組成的平行四邊形的長對角線的矢量兩個(gè)矢量減法的幾何意義:等于兩個(gè)矢量組成的三角形的另一條邊的矢量
很簡單,現(xiàn)在把矢量乘于系數(shù)(數(shù)乘):
k?(x,y,z)=(kx,ky,kz)幾何意義:將矢量進(jìn)行縮放點(diǎn)積如下表示:(點(diǎn)積是一個(gè)標(biāo)量)
(x1,y1,z1)?(x2,y2,z2)=x1x2+y1y2+z1z2
實(shí)際上,兩個(gè)矢量的點(diǎn)積被它們的模的乘積除,等于兩個(gè)矢量夾角的余弦(兩個(gè)矢量的模與其夾角的余弦之積稱為兩個(gè)矢量的數(shù)量積(或稱內(nèi)積、點(diǎn)積))。所以
cos(V^W)=V?W/|V||W|幾何意義:兩個(gè)矢量的點(diǎn)積衡量著兩個(gè)向量的角度關(guān)系。在物理上的意義如果兩個(gè)矢量分別代表力和位移,那么兩個(gè)矢量的點(diǎn)積就是功。
注意"^"并不表示指數(shù)而是兩個(gè)矢量的夾角。點(diǎn)積可以用來計(jì)算光線與平面的夾角,我們在計(jì)算陰影一節(jié)里會(huì)詳細(xì)討論。
現(xiàn)在討論叉乘:
(x1,y1,z1)X(x2,y2,z2)=(y1z2-z1y2,z1x2-x1z2,x1y2-y1x2)
幾何意義:叉乘的結(jié)果是一個(gè)偽向量,但是他的模是以此二向量為相鄰兩邊的平行四邊形面積
叉乘對于計(jì)算屏幕的法向量非常有用。
OK,我們已經(jīng)講完了矢量的基本概念。我們開始兩個(gè)矩陣的和。它與矢量相加非常相似,這里就不討論了。設(shè)I是矩陣的一行,J是矩陣的一列,(i,j)是矩陣的一個(gè)元素。我們討論與3D變換有關(guān)的重要的矩陣操作原理。矩陣相乘的幾何意義:把兩次線性變換合成一次兩個(gè)矩陣相乘,而且MxN<>NxM。例如:(矩陣乘法是有順序的)
AB4x4矩陣相乘公式
如果A=(aij)4x4,B=(bij)4x4,那么
AxB=
|S>a1jbj1S>a1jbj2S>a1jbj3S>a1jbj4|
||
|S>a2jbj1S>a2jbj2S>a2jbj3S>a2jbj4|
||
|S>a3jbj1S>a3jbj2S>a3jbj3S>a3jbj4|
||
|S>a4jbj1S>a4jbj2S>a4jbj3S>a4jbj4|
其中j=1,2,3,4
而且如果AxB=(cik)4x4那么我們可以在一行上寫下:
cik=S>4,j=1aijbjk
(a1,a2,a3)xB=
(Sum(aibi1)+b4,1,Sum(aibi2)+b4,2,Sum(aibi3)+b4,3)
現(xiàn)在,我們可以試著把一些矩陣乘以單位矩陣來了解矩陣相乘的性質(zhì)。我們把矩陣與矢量相乘結(jié)合在一起。下面有一個(gè)公式把3D矢量乘以一個(gè)4x4矩陣(得到另外一個(gè)三維矢量)如果B=(bij)4x4,那么:
(a1,a2,a3)xB=(S>aibi1+b4,1,S>aibi2+b4,2,S>aibi3+b4,3)>
這就是矢量和矩陣操作公式。從這里開始,代碼與數(shù)學(xué)之間的聯(lián)系開始清晰。
變換
我們已經(jīng)見過象這樣的公式:
t(tx,ty):(x,y)==>(x+tx,y+ty)
這是在二維笛卡兒坐標(biāo)系的平移等式。下面是縮放公式:
s(k):(x,y)==>(kx,ky)
旋轉(zhuǎn)等式:
r(q):(x,y)==>(xcos(q)-ysin(q),xsin(q)+ycos(q))
以上都是二維公式,在三維里公式的形式仍然很相近。
平移公式:
t(tx,ty,tz):(x,y,z)==>(x+tx,y+ty,z+tz)
縮放公式:
s(k):(x,y,z)==>(kx,ky,kz)
旋轉(zhuǎn)公式(圍繞Z軸):
r(q):(x,y,z)==>(xcos(q)-ysin(q),xsin(q)+ycos(q),z)
所以我們可以寫出像在二維中同樣的變換公式。我們通過乘以變換矩陣而得到新的矢量,新矢量將指向變換點(diǎn)。下面是所有三維變換矩陣:
平移(tx,ty,tz)的矩陣
|1000|
|0100|
|0010|
|txtytz1|
縮放(sx,sy,sz)的矩陣
|sz000|
|0sy00|
|00sx0|
|0001|
繞X軸旋轉(zhuǎn)角q的矩陣
|1000|
|0cos(q)sin(q)0|
|0-sin(q)cos(q)0|
|0001|
繞Y軸旋轉(zhuǎn)角q的矩陣:
|cos(q)0-sin(q)0|
|0100|
|sin(q)0cos(q)0|
|0001|
繞Z軸旋轉(zhuǎn)角q的矩陣:
|cos(q)sin(q)00|
|-sin(q)cos(q)00|
|0010|
|0001|
所以我們已經(jīng)可以結(jié)束關(guān)于變換的部分.通過這些矩陣我們可以對三維點(diǎn)進(jìn)行任何變換.
平面和法向量
平面是平坦的,無限的,指向特定方向的表面可以定義平面如下:
Ax+By+Cz+D=0
其中A,B,C稱為平面的法向量,D是平面到原點(diǎn)的距離。我們可以通過計(jì)算平面上的兩個(gè)矢量的叉積得到平面的法向量。為得到這兩個(gè)矢量,我們需要三個(gè)點(diǎn)。P1,P2,P3逆時(shí)針排列(轉(zhuǎn)入仿射空間了),可以得到:
矢量1=P1-P2
和
矢量2=P3-P2
計(jì)算法向量為:
法向量=矢量1X矢量2
把D移到等式的右邊得到:
D=-(Ax+By+Cz)
或
D=-(A??1.x+B??2.y+C??3.z)>
或更簡單:
D=-Normal?P1>
但是為計(jì)算A,B,C分量??梢院喕僮靼慈缦碌仁剑?/p>
A=y1(z2-z3)+y2(z3-z1)+y3(z1-z2)
B=z1(x2-x3)+z2(x3-x1)+z3(x1-x2)
C=x1(y2-y3)+x2(y3-y1)+x3(y1-y2)
D=-x1(y2z3-y3z2)-x2(y3z1-y1z3)-x3(y1z2-y2z1)
三維變換
存儲(chǔ)坐標(biāo)
實(shí)現(xiàn)矩陣系統(tǒng)
實(shí)現(xiàn)三角法系統(tǒng)
創(chuàng)建變換矩陣
如何創(chuàng)建透視
變換對象
存儲(chǔ)坐標(biāo)
首先可以編寫星空模擬代碼。那么我們基本的結(jié)構(gòu)是什么樣?每一個(gè)對象的描述是如何存儲(chǔ)的?為解決這個(gè)問題,首先我們思考另一個(gè)問題:我們需要的是什么樣的坐標(biāo)系?最明顯的答案是:
屏幕坐標(biāo)系:相對于顯示器的原點(diǎn)的2D坐標(biāo)系
本地坐標(biāo)系:相對于對象的原點(diǎn)的3D坐標(biāo)系
但是我們不要忘記變換中間用到的坐標(biāo)系,例如:
世界坐標(biāo)系:相對于3D世界的原點(diǎn)三維坐標(biāo)系
對齊(視點(diǎn))坐標(biāo)系:世界坐標(biāo)系的變換,觀察者的位置在世界坐標(biāo)系的原點(diǎn)。
下面是坐標(biāo)的基本結(jié)構(gòu):
//二維坐標(biāo)
typedefstruct
{
shortx,y;
}_2D;
//三維坐標(biāo)
typedefstruct
{
floatx,y,z;
}_3D;
這里,我們定義了稱為頂點(diǎn)的坐標(biāo)結(jié)構(gòu)。因?yàn)椤绊旤c(diǎn)”一詞指兩個(gè)或兩個(gè)以上菱形邊的交點(diǎn)。我們的頂點(diǎn)可以簡單地認(rèn)為是描述不同系統(tǒng)的矢量。
//不同的坐標(biāo)系的坐標(biāo)
typedefstruct
{
_3DLocal;
_3DWorld;
_3DAligned;
}Vertex_t;
實(shí)現(xiàn)矩陣系統(tǒng)
我們需要存儲(chǔ)我們的矩陣在4x4浮點(diǎn)數(shù)矩陣中。所以當(dāng)我們需要做變換是我們定義如下矩陣:
floatmatrix[4][4];
然后我們定義一些函數(shù)來拷貝臨時(shí)矩陣到全局矩陣:
voidMAT_Copy(floatsource[4][4],floatdest[4][4])
{
inti,j;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
dest[i][j]=source[i][j];
}
很簡單!現(xiàn)在我們來寫兩個(gè)矩陣相乘的函數(shù)。同時(shí)可以理解上面的一些有關(guān)矩陣相乘的公式代碼如下:
voidMAT_Mult(floatmat1[4][4],floatmat2[4][4],floatdest[4][4])
{
inti,j;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
dest[i][j]=mat1[i][0]*mat2[0][j]+mat1[i][1]*mat2[1][j]+mat1[i][2]*mat2[2][j]+mat1[i][3]*mat2[3][j];
}
//mat1矩陣1
//mat2矩陣2
//dest相乘后的新矩陣
現(xiàn)在你明白了嗎?現(xiàn)在我們設(shè)計(jì)矢量與矩陣相乘的公式。
voidVEC_MultMatrix(_3D*Source,floatmat[4][4],_3D*Dest)
{
Dest->x=Source->x*mat[0][0]+Source->y*mat[1][0]+Source->z*mat[2][0]+mat[3][0];
Dest->y=Source->x*mat[0][1]+Source->y*mat[1][1]+Source->z*mat[2][1]+mat[3][1];
Dest->z=Source->x*mat[0][2]+Source->y*mat[1][2]+Source->z*mat[2][2]+mat[3][2];
}
//Source源矢量(坐標(biāo))
//mat變換矩陣
//Dest目標(biāo)矩陣(坐標(biāo))
我們已經(jīng)得到了矩陣變換函數(shù),不錯(cuò)吧??!
//注意,這里的矩陣變換與我們學(xué)過的矩陣變換不同
//一般的,Y=TX,T為變換矩陣,這里為Y=XT,
//由于矩陣T為4x4矩陣
實(shí)現(xiàn)三角法系統(tǒng)
幾乎每一個(gè)C編譯器都帶有有三角函數(shù)的數(shù)學(xué)庫,但是我們需要簡單的三角函數(shù)時(shí),不是每次都使用它們。正弦和余弦的計(jì)算是階乘和除法的大量運(yùn)算。為提高計(jì)算速度,我們建立自己的三角函數(shù)表。首先決定你需要的角度的個(gè)數(shù),然后在這些地方用下面的值代替:
floatSinTable[256],CosTable[256];
然后使用宏定義,它會(huì)把每一個(gè)角度變成正值,并對于大于360度的角度進(jìn)行周期變換,然后返回需要的值。如果需要的角度數(shù)是2的冪次,那么我們可以使用"&"代替"%",它使程序運(yùn)行更快。例如256。所以在程序中盡量選取2的冪次。
三角法系統(tǒng):
#defineSIN(x)SinTable[ABS((int)x&255)]
#defineCOS(x)CosTable[ABS((int)x&255)]
一旦我們已經(jīng)定義了需要的東西,建立初始化函數(shù),并且在程序中調(diào)用宏。
voidM3D_Init()
{
intd;
for(d=0;d<256;d++)
{
SinTable[d]=sin(d*PI/128.0);
CosTable[d]=cos(d*PI/128.0);
}
}
建立變換矩陣
下面使用C編寫的變換矩陣代碼
floatmat1[4][4],mat2[4][4];
voidMAT_Identity(floatmat[4][4])
{
mat[0][0]=1;mat[0][1]=0;mat[0][2]=0;mat[0][3]=0;
mat[1][0]=0;mat[1][1]=1;mat[1][2]=0;mat[1][3]=0;
mat[2][0]=0;mat[2][1]=0;mat[2][2]=1;mat[2][3]=0;
mat[3][0]=0;mat[3][1]=0;mat[3][2]=0;mat[3][3]=1;
}
//定義單位陣
voidTR_Translate(floatmatrix[4][4],floattx,floatty,floattz)
{
floattmat[4][4];
tmat[0][0]=1;tmat[0][1]=0;tmat[0][2]=0;tmat[0][3]=0;
tmat[1][0]=0;tmat[1][1]=1;tmat[1][2]=0;tmat[1][3]=0;
tmat[2][0]=0;tmat[2][1]=0;tmat[2][2]=1;tmat[2][3]=0;
tmat[3][0]=tx;tmat[3][1]=ty;tmat[3][2]=tz;tmat[3][3]=1;
MAT_Mult(matrix,tmat,mat1);
MAT_Copy(mat1,matrix);
}
//tx,ty.tz平移參數(shù)
//matrix源矩陣和目標(biāo)矩陣
//矩陣平移函數(shù)
voidTR_Scale(floatmatrix[4][4],floatsx,floatsy,floatsz)
{
floatsmat[4][4];
smat[0][0]=sx;smat[0][1]=0;smat[0][2]=0;smat[0][3]=0;
smat[1][0]=0;smat[1][1]=sy;smat[1][2]=0;smat[1][3]=0;
smat[2][0]=0;smat[2][1]=0;smat[2][2]=sz;smat[2][3]=0;
smat[3][0]=0;smat[3][1]=0;smat[3][2]=0;smat[3][3]=1;
MAT_Mult(matrix,smat,mat1);
MAT_Copy(mat1,matrix);
}
//矩陣縮放
voidTR_Rotate(floatmatrix[4][4],intax,intay,intaz)
{
floatxmat[4][4],ymat[4][4],zmat[4][4];
xmat[0][0]=1;xmat[0][1]=0;xmat[0][2]=0;
xmat[0][3]=0;
xmat[1][0]=0;xmat[1][1]=COS(ax);xmat[1][2]=SIN(ax);
xmat[1][3]=0;
xmat[2][0]=0;xmat[2][1]=-SIN(ax);xmat[2][2]=COS(ax);xmat[2][3]=0;
xmat[3][0]=0;xmat[3][1]=0;xmat[3][2]=0;xmat[3][3]=1;
ymat[0][0]=COS(ay);ymat[0][1]=0;ymat[0][2]=-SIN(ay);ymat[0][3]=0;
ymat[1][0]=0;ymat[1][1]=1;ymat[1][2]=0;ymat[1][3]=0;
ymat[2][0]=SIN(ay);ymat[2][1]=0;ymat[2][2]=COS(ay);ymat[2][3]=0;
ymat[3][0]=0;ymat[3][1]=0;ymat[3][2]=0;ymat[3][3]=1;
zmat[0][0]=COS(az);zmat[0][1]=SIN(az);zmat[0][2]=0;zmat[0][3]=0;
zmat[1][0]=-SIN(az);zmat[1][1]=COS(az);zmat[1][2]=0;zmat[1][3]=0;
zmat[2][0]=0;zmat[2][1]=0;zmat[2][2]=1;zmat[2][3]=0;
zmat[3][0]=0;zmat[3][1]=0;zmat[3][2]=0;zmat[3][3]=1;
MAT_Mult(matrix,ymat,mat1);
MAT_Mult(mat1,xmat,mat2);
MAT_Mult(mat2,zmat,matrix);
}
//ax繞X軸旋轉(zhuǎn)的角度
//ay繞Y軸旋轉(zhuǎn)的角度
//az繞Z軸旋轉(zhuǎn)的角度
//矩陣旋轉(zhuǎn)
如何建立透視
如何建立對象的立體視覺,即顯示器上的一些事物看起來離我們很近,而另外一些事物離我們很遠(yuǎn)。透視問題一直是困繞我們的一個(gè)問題。有許多方法被使用。我們使用的3D世界到2D屏幕的投影公式:
P(f):(x,y,z)==>(f*x/z+XOrigin,f*y/z+YOrigin)
其中f是“焦點(diǎn)距離”,它表示從觀察者到屏幕的距離,一般在80到200厘米之間。XOrigin和YOrigin是屏幕中心的坐標(biāo),(x,y,z)在對齊坐標(biāo)系上。那么投影函數(shù)應(yīng)該是什么樣?
#defineFOCAL_DISTANCE200
//定義焦點(diǎn)距離
voidProject(vertex_t*Vertex)
{
if(!Vertex->Aligned.z)
Vertex->Aligned.z=1;
Vertex->Screen.x=
FOCAL_DISTANCE*Vertex->Aligned.x/Vertex->Aligned.z+XOrigin;
Vertex->Screen.y=
FOCAL_DISTANCE*Vertex->Aligned.y/Vertex->Aligned.z+YOrigin;
}
//得到屏幕上的投影坐標(biāo)
因?yàn)?不能做除數(shù),所以對z進(jìn)行判斷。
變換對象
既然我們已經(jīng)掌握了所有的變換頂點(diǎn)的工具,就應(yīng)該了解需要執(zhí)行的主要步驟。
一、初始化每一個(gè)頂點(diǎn)的本地坐標(biāo)
二、設(shè)置全局矩陣為單位陣
三、根據(jù)對象的尺寸縮放全局矩陣
四、根據(jù)對象的角度來旋轉(zhuǎn)全局矩陣
五、根據(jù)對象的位置移動(dòng)全局矩陣
六、把本地坐標(biāo)乘以全局矩陣來得到世界坐標(biāo)系
七、設(shè)置全局矩陣為單位陣
八、用觀測者的位置的負(fù)值平移全局矩陣
九、用觀測者的角度的負(fù)值旋轉(zhuǎn)全局矩陣
十、把世界坐標(biāo)系與全局矩陣相乘得到對齊坐標(biāo)系
十一、投影對齊坐標(biāo)系來得到屏幕坐標(biāo)
即:本地坐標(biāo)系-->世界坐標(biāo)系-->對齊坐標(biāo)系-->屏幕坐標(biāo)系
多邊形填充
多邊形結(jié)構(gòu)
發(fā)現(xiàn)三角形
繪制三角形
多邊形結(jié)構(gòu)
我們?nèi)绾未鎯?chǔ)我們的多邊形?首先,我們必須知道再這種狀態(tài)下多邊形是二維多邊形,而且由于初始多邊形是三維的,我們僅需要一個(gè)臨時(shí)的二維多邊形,所以我們能夠設(shè)置二維頂點(diǎn)的最大數(shù)為一個(gè)常量,而沒有浪費(fèi)內(nèi)存:
2D結(jié)構(gòu):
typedefstruct
{
_2DPoints[20];
intPointsCount;
intTexture;
}Polygon2D_t;
3D結(jié)構(gòu):
typedefstruct
{
intCount;
int*Vertex;
intTexture;
Vertex_tP,M,N;
}Polygon_t;
為什么頂點(diǎn)數(shù)組包含整數(shù)值呢?仔細(xì)思考一下,例如在立方體內(nèi),三個(gè)多邊形公用同一個(gè)
頂點(diǎn),所以在三個(gè)多邊形里存儲(chǔ)和變換同一個(gè)頂點(diǎn)會(huì)浪費(fèi)內(nèi)存和時(shí)間。我們更愿意存儲(chǔ)
它們在一個(gè)對象結(jié)構(gòu)里,而且在多邊形結(jié)構(gòu)里,我們會(huì)放置相應(yīng)頂點(diǎn)的索引。請看
下面的結(jié)構(gòu):
typedefstruct
{
intVertexCount;
intPolygonCount;
Vertex_t*Vertex;
Polygon_t*Polygon;
_3DScaling;
_3DPosition;
_3DAngle;
intNeedUpdate;
}Object_t;
發(fā)現(xiàn)三角形
因?yàn)槔L制一個(gè)三角形比繪制任意的多邊形要簡單,所以我們從把多邊形分割成
三頂點(diǎn)的形狀。這種方法非常簡單和直接:
voidPOLY_Draw(Polygon2D_t*Polygon)
{
_2DP1,P2,P3;
inti;
P1=Polygon->Points[0];
for(i=1;i<Polygon->PointsCount-1;i++)
{
P2=Polygon->Points[i];
P3=Polygon->Points[i+1];
POLY_Triangle(P1,P2,P3,Polygon->Texture);
}
}
//上面的算法,對于凹多邊形就不太適用
_____
|\|
|\|
|____\|
繪制三角形
現(xiàn)在怎樣得到三角形函數(shù)?我們怎樣才能畫出每一條有關(guān)的直線,并且如何發(fā)現(xiàn)
每一行的起始和結(jié)實(shí)的x坐標(biāo)。我們通過定義兩個(gè)簡單有用的宏定義開始來區(qū)別
垂直地兩個(gè)點(diǎn)和兩個(gè)數(shù):
#defineMIN(a,b)((a<b)?(a):(b))
#defineMAX(a,b)((a>b)?(a):(b))
#defineMaxPoint(a,b)((a.y>b.y)?a:b)
#defineMinPoint(a,b)((b.y>a.y)?a:b)
然后我們定義三個(gè)宏來區(qū)別三個(gè)點(diǎn):
#defineMaxPoint3(a,b,c)MaxPoint(MaxPoint(a,b),MaxPoint(b,c))
#defineMidPoint3(a,b,c)MaxPoint(MinPoint(a,b),MinPoint(a,c))
#defineMinPoint3(a,b,c)MinPoint(MinPoint(a,b),MinPoint(b,c))
你也許注意到MidPoint3宏不總是正常地工作,取決于三個(gè)點(diǎn)排列的順序,
例如,a<b&a<c那么由MidPoint3得到的是a,但它不是中間點(diǎn)。
我們用if語句來修正這個(gè)缺點(diǎn),下面為函數(shù)的代碼:
voidPOLY_Triangle(_2Dp1,_2Dp2,_2Dp3,charc)
{
_2Dp1d,p2d,p3d;
intxd1,yd1,xd2,yd2,i;
intLx,Rx;
首先我們把三個(gè)點(diǎn)進(jìn)行排序:
p1d=MinPoint3(p1,p2,p3);
p2d=MidPoint3(p2,p3,p1);
p3d=MaxPoint3(p3,p1,p2);
當(dāng)調(diào)用這些宏的時(shí)候?yàn)槭裁磿?huì)有點(diǎn)的順序的改變?(作者也不清楚)可能這些點(diǎn)被逆時(shí)針傳遞。試圖改變這些宏你的屏幕顯示的是垃圾!現(xiàn)在我們并不確定中間的點(diǎn),所以我們做一些檢查,
而且在這種狀態(tài)下,得到的中間點(diǎn)有似乎是錯(cuò)誤的,所以我們修正:
if(p2.y<p1.y)
{
p1d=MinPoint3(p2,p1,p3);
p2d=MidPoint3(p1,p3,p2);
}
這些點(diǎn)的排列順序看起來很奇怪,但是試圖改變他們那么所有的東西就亂套了。只有理解或
接受這些結(jié)論?,F(xiàn)在我們計(jì)算增量
xd1=p2d.x-p1d.x;
yd1=p2d.y-p1d.y;
xd2=p3d.x-p1d.x;
yd2=p3d.y-p1d.y;
OK,第一步已經(jīng)完成,如果有增量y:
if(yd1)
for(i=p1d.y;i<=p2d.y;i++)
{
我們用x的起始坐標(biāo)計(jì)算x值,在當(dāng)前點(diǎn)和起始點(diǎn)之間加上增量y,乘以斜率(x/y)
的相反值。
Lx=p1d.x+((i-p1d.y)*xd1)/yd1;
Rx=p1d.x+((i-p1d.y)*xd2)/yd2;
如果不在同一個(gè)點(diǎn),繪制線段,按次序傳遞這兩個(gè)點(diǎn):
if(Lx!=Rx)
VID_HLine(MIN(Lx,Rx),MAX(Lx,Rx),i,c);
}
現(xiàn)在我們重新計(jì)算第一個(gè)增量,而且計(jì)算第二條邊
xd1=p3d.x-p2d.x;
yd1=p3d.y-p2d.y;
if(yd1)
for(i=p2d.y;i<=p3d.y;i++)
{
Lx=p1d.x+((i-p1d.y)*xd2)/yd2;
Rx=p2d.x+((i-p2d.y)*xd1)/yd1;
if(Lx!=Rx)
VID_HLine(MIN(Lx,Rx),MAX(Lx,Rx),i,c);
}
}
以上我們已經(jīng)得到多邊形填充公式,對于平面填充更加簡單:
voidVID_HLine(intx1,intx2,inty,charc)
{
intx;
for(x=x1;x<=x2;x++)
putpixel(x,y,c);
}
Sutherland-Hodgman剪貼
概述
Z-剪貼
屏幕剪貼
概述
一般地,我們更愿意剪貼我們的多邊形。必須靠著屏幕的邊緣剪貼,但也必須在觀察的前方(我們不需要繪制觀察者后面的事物,當(dāng)z左邊非常小時(shí))。當(dāng)我們剪貼一個(gè)多邊形,并不考慮是否每一個(gè)點(diǎn)在限制以內(nèi),而我們更愿意增加必須的頂點(diǎn),所以我們需要一個(gè)第三個(gè)多邊形結(jié)構(gòu):
typedefstruct
{
intCount;
_3DVertex[20];
}CPolygon_t;
由于我們有附加的頂點(diǎn)來投影,我們不再投影頂點(diǎn),而是投影剪貼的3D多邊形到
2D多邊形。
voidM3D_Project(CPolygon_t*Polygon,Polygon2D_t*Clipped,intfocaldistance)
{
intv;
for(v=0;v<Polygon->Count;v++)
{
if(!Polygon->Vertex[v].z)Polygon->Vertex[v].z++;
Clipped->Points[v].x=Polygon->Vertex[v].x*focaldistance/Polygon->Vertex[v].z+Origin.x;
Clipped->Points[v].y=Polygon->Vertex[v].y*focaldistance/Polygon->Vertex[v].z+Origin.y;
}
Clipped->PointsCount=Polygon->Count;
}
Z-剪貼
首先我們定義計(jì)算在第一個(gè)點(diǎn)和第二個(gè)點(diǎn)之間以及在第一個(gè)點(diǎn)和最小z值的z增量的宏。
然后,我們計(jì)算比例,注意不要被零除。
WORDZMin=20;
#defineINIT_ZDELTASdold=V2.z-V1.z;dnew=ZMin-V1.z;
#defineINIT_ZCLIPINIT_ZDELTASif(dold)m=dnew/dold;
我們建立一個(gè)函數(shù),它主要剪貼多邊形指針的參數(shù)(它將記下作為結(jié)果的剪貼的頂點(diǎn)),第一個(gè)頂點(diǎn)(我們剪貼的邊的開始)和第二個(gè)頂點(diǎn)(最后):
voidCLIP_Front(CPolygon_t*Polygon,_3DV1,_3DV2)
{
floatdold,dnew,m=1;
INIT_ZCLIP
現(xiàn)在我們必須檢測邊緣是否完全地在視口里,離開或進(jìn)入視口。如果邊緣沒有完全地
在視口里,我們計(jì)算視口與邊緣的交線,用m值表示,用INIT_ZCLIP計(jì)算。
如果邊緣在視口里:
if((V1.z>=ZMin)&&(V2.z>=ZMin))
Polygon->Vertex[Polygon->Count++]=V2;
如果邊緣正離開視口:
if((V1.z>=ZMin)&&(V2.z<ZMin))
{
Polygon->Vertex[Polygon->Count].x=V1.x+(V2.x-V1.x)*m;
Polygon->Vertex[Polygon->Count].y=V1.y+(V2.y-V1.y)*m;
Polygon->Vertex[Polygon->Count++].z=ZMin;
}
如果邊緣正進(jìn)入視口:
if((V1.z<ZMin)&&(V2.z>=ZMin))
{
Polygon->Vertex[Polygon->Count].x=V1.x+(V2.x-V1.x)*m;
Polygon->Vertex[Polygon->Count].y=V1.y+(V2.y-V1.y)*m;
Polygon->Vertex[Polygon->Count++].z=ZMin;
Polygon->Vertex[Polygon->Count++]=V2;
}
這就是邊緣Z-剪貼函數(shù)
}
現(xiàn)在我們可以寫下完整的多邊形Z-剪貼程序。為了有代表性,定義一個(gè)宏用來
在一個(gè)對象結(jié)構(gòu)中尋找適當(dāng)?shù)亩噙呅雾旤c(diǎn)。
#defineVert(x)Object->Vertex[Polygon->Vertex[x]]
下面是它的函數(shù):
voidCLIP_Z(Polygon_t*Polygon,Object_t*Object,CPolygon_t*ZClipped)
{
intd,v;
ZClipped->Count=0;
for(v=0;v<Polygon->Count;v++)
{
d=v+1;
if(d==Polygon->Count)d=0;
CLIP_Front(ZClipped,Vert(v).Aligned,Vert(d).Aligned);
}
}
這個(gè)函數(shù)相當(dāng)簡單:它僅僅調(diào)用FrontClip函數(shù)來做頂點(diǎn)交換。
剪貼屏幕
剪貼屏幕的邊緣同Z-剪貼相同,但是我們有四個(gè)邊緣而不是一個(gè)。所以我們需要四個(gè)
不同的函數(shù)。但是它們需要同樣的增量初始化:
#defineINIT_DELTASdx=V2.x-V1.x;dy=V2.y-V1.y;
#defineINIT_CLIPINIT_DELTASif(dx)m=dy/dx;
邊緣是:
_2DTopLeft,DownRight;
為了進(jìn)一步簡化_2D和_3D結(jié)構(gòu)的使用,我們定義兩個(gè)有用的函數(shù):
_2DP2D(shortx,shorty)
{
_2DTemp;
Temp.x=x;
Temp.y=y;
returnTemp;
}
_3DP3D(floatx,floaty,floatz)
{
_3DTemp;
Temp.x=x;
Temp.y=y;
Temp.z=z;
returnTemp;
}
然后使用這兩個(gè)函數(shù)來指定視口:
TopLeft=P2D(0,0);
DownRight=P2D(319,199);
下面是四個(gè)邊緣剪貼函數(shù):
/*
=======================
剪貼左邊緣
=======================
*/
voidCLIP_Left(Polygon2D_t*Polygon,_2DV1,_2DV2)
{
floatdx,dy,m=1;
INIT_CLIP
//************OK************
if((V1.x>=TopLeft.x)&&(V2.x>=TopLeft.x))
Polygon->Points[Polygon->PointsCount++]=V2;
//*********LEAVING**********
if((V1.x>=TopLeft.x)&&(V2.x<TopLeft.x))
{
Polygon->Points[Polygon->PointsCount].x=TopLeft.x;
Polygon->Points[Polygon->PointsCount++].y=V1.y+m*(TopLeft.x-V1.x);
}
//********ENTERING*********
if((V1.x<TopLeft.x)&&(V2.x>=TopLeft.x))
{
Polygon->Points[Polygon->PointsCount].x=TopLeft.x;
Polygon->Points[Polygon->PointsCount++].y=V1.y+m*(TopLeft.x-V1.x);
Polygon->Points[Polygon->PointsCount++]=V2;
}
}
/*
=======================
剪貼右邊緣
=======================
*/
voidCLIP_Right(Polygon2D_t*Polygon,_2DV1,_2DV2)
{
floatdx,dy,m=1;
INIT_CLIP
//************OK************
if((V1.x<=DownRight.x)&&(V2.x<=DownRight.x))
Polygon->Points[Polygon->PointsCount++]=V2;
//*********LEAVING**********
if((V1.x<=DownRight.x)&&(V2.x>DownRight.x))
{
Polygon->Points[Polygon->PointsCount].x=DownRight.x;
Polygon->Points[Polygon->PointsCount++].y=V1.y+m*(DownRight.x-V1.x);
}
//********ENTERING*********
if((V1.x>DownRight.x)&&(V2.x<=DownRight.x))
{
Polygon->Points[Polygon->PointsCount].x=DownRight.x;
Polygon->Points[Polygon->PointsCount++].y=V1.y+m*(DownRight.x-V1.x);
Polygon->Points[Polygon->PointsCount++]=V2;
}
}
/*
=======================
剪貼上邊緣
=======================
*/
voidCLIP_Top(Polygon2D_t*Polygon,_2DV1,_2DV2)
{
floatdx,dy,m=1;
INIT_CLIP
//************OK************
if((V1.y>=TopLeft.y)&&(V2.y>=TopLeft.y))
Polygon->Points[Polygon->PointsCount++]=V2;
//*********LEAVING**********
if((V1.y>=TopLeft.y)&&(V2.y<TopLeft.y))
{
if(dx)
Polygon->Points[Polygon->PointsCount].x=V1.x+(TopLeft.y-V1.y)/m;
else
Polygon->Points[Polygon->PointsCount].x=V1.x;
Polygon->Points[Polygon->PointsCount++].y=TopLeft.y;
}
//********ENTERING*********
if((V1.y<TopLeft.y)&&(V2.y>=TopLeft.y))
{
if(dx)
Polygon->Points[Polygon->PointsCount].x=V1.x+(TopLeft.y-V1.y)/m;
else
Polygon->Points[Polygon->PointsCount].x=V1.x;
Polygon->Points[Polygon->PointsCount++].y=TopLeft.y;
Polygon->Points[Polygon->PointsCount++]=V2;
}
}
/*
=======================
剪貼下邊緣
=======================
*/
voidCLIP_Bottom(Polygon2D_t*Polygon,_2DV1,_2DV2)
{
floatdx,dy,m=1;
INIT_CLIP
//************OK************
if((V1.y<=DownRight.y)&&(V2.y<=DownRight.y))
Polygon->Points[Polygon->PointsCount++]=V2;
//*********LEAVING**********
if((V1.y<=DownRight.y)&&(V2.y>DownRight.y))
{
if(dx)
Polygon->Points[Polygon->PointsCount].x=V1.x+(DownRight.y-V1.y)/m;
else
Polygon->Points[Polygon->PointsCount].x=V1.x;
Polygon->Points[Polygon->PointsCount++].y=DownRight.y;
}
//********ENTERING*********
if((V1.y>DownRight.y)&&(V2.y<=DownRight.y))
{
if(dx)
Polygon->Points[Polygon->PointsCount].x=V1.x+(DownRight.y-V1.y)/m;
else
Polygon->Points[Polygon->PointsCount].x=V1.x;
Polygon->Points[Polygon->PointsCount++].y=DownRight.y;
Polygon->Points[Polygon->PointsCount++]=V2;
}
}
為了得到完整的多邊形剪貼函數(shù),我們需要定義一個(gè)附加的全局變量:
polygon2D_tTmpPoly;
voidCLIP_Polygon(Polygon2D_t*Polygon,Polygon2D_t*Clipped)
{
intv,d;
Clipped->PointsCount=0;
TmpPoly.PointsCount=0;
for(v=0;v<Polygon->PointsCount;v++)
{
d=v+1;
if(d==Polygon->PointsCount)d=0;
CLIP_Left(&TmpPoly,Polygon->Points[v],Polygon->Points[d]);
}
for(v=0;v<TmpPoly.PointsCount;v++)
{
d=v+1;
if(d==TmpPoly.PointsCount)d=0;
CLIP_Right(Clipped,TmpPoly.Points[v],TmpPoly.Points[d]);
}
TmpPoly.PointsCount=0;
for(v=0;v<Clipped->PointsCount;v++)
{
d=v+1;
if(d==Clipped->PointsCount)d=0;
CLIP_Top(&TmpPoly,Clipped->Points[v],Clipped->Points[d]);
}
Clipped->PointsCount=0;
for(v=0;v<TmpPoly.PointsCount;v++)
{
d=v+1;
if(d==TmpPoly.PointsCount)d=0;
CLIP_Bottom(Clipped,TmpPoly.Points[v],TmpPoly.Points[d]);
}
}
程序原理同Z-剪貼一樣,所以我們可以輕松地領(lǐng)會(huì)它。
隱面消除
Dilemna
底面消除
Z-緩沖
TheDilemna
三維引擎的核心是它的HSR系統(tǒng),所以我們必須考慮選擇那一種。一般來說,最流行
的幾種算法是:
畫筆算法
需要的時(shí)間增長更快
難以實(shí)現(xiàn)(尤其重疊測試)
不能準(zhǔn)確地排序復(fù)雜的場景
字節(jié)空間分區(qū)樹
特別快
難以實(shí)現(xiàn)
僅僅能排序靜態(tài)多邊形
需要存儲(chǔ)樹
Z-緩存
需要的時(shí)間隨多邊形的數(shù)目線性地增加
在多邊形大于5000后速度比畫筆算法快
能夠完美地渲染任何場景,即使邏輯上不正確
非常容易實(shí)現(xiàn)
簡單
需要大量的內(nèi)存
很慢
所以我們的選擇是Z-緩存。當(dāng)然也可以選擇其他算法。
底面消除
除了這些方法,我們可以很容易地消除多邊形的背面來節(jié)省大量的計(jì)算時(shí)間。首先
我們定義一些有用的函數(shù)來計(jì)算平面和法向量以及填充。然后,我們給這個(gè)函數(shù)增加
紋理和陰影計(jì)算。這些變量為全局變量:
floatA,B,C,D;
BOOLbackface;
下面是我們的引擎函數(shù),每一個(gè)坐標(biāo)都是浮點(diǎn)變量:
voidENG3D_SetPlane(Polygon_t*Polygon,Object_t*Object)
{
floatx1=Vert(0).Aligned.x;
floatx2=Vert(1).Aligned.x;
floatx3=Vert(2).Aligned.x;
floaty1=Vert(0).Aligned.y;
floaty2=Vert(1).Aligned.y;
floaty3=Vert(2).Aligned.y;
floatz1=Vert(0).Aligned.z;
floatz2=Vert(1).Aligned.z;
floatz3=Vert(2).Aligned.z;
然后我們計(jì)算平面等式的每一個(gè)成員:
A=y1*(z2-z3)+y2*(z3-z1)+y3*(z1-z2);
B=z1*(x2-x3)+z2*(x3-x1)+z3*(x1-x2);
C=x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y2);
D=-x1*(y2*z3-y3*z2)-x2*(y3*z1-y1*z3)-x3*(y1*z2-y2*z1);
再檢查是否它面朝我們或背朝:
backface=D<0;
}
Z-緩存
Z-緩存是把顯示在屏幕上的每一個(gè)點(diǎn)的z坐標(biāo)保持在一個(gè)巨大的數(shù)組中,并且當(dāng)我們
我們檢查是否它靠近觀察者或是否在觀察者后面。我們僅僅在第一種情況下繪制它。所以我們不得不計(jì)算每一個(gè)點(diǎn)的z值。但是首先,我們定義全局樹組和為他分配空間。
(內(nèi)存等于追至方向與水平方向的乘積):
typedeflongZBUFTYPE;
ZBUFTYPE*zbuffer;
zbuffer=(ZBUFTYPE*)malloc(sizeof(ZBUFTYPE)*MEMORYSIZE);
我們使用長整形作為z-緩存類型,因?yàn)槲覀円褂枚c(diǎn)數(shù)。我們必須記住設(shè)置每一個(gè)z坐標(biāo)來盡可能得到更快的速度:
intc;
for(c=0;c<MEMORYSIZE;c++)
zbuffer[c]=-32767;
下面是數(shù)學(xué)公式。如何才能發(fā)現(xiàn)z坐標(biāo)?我們僅僅已經(jīng)定義的頂點(diǎn),而不是多邊形的
每一個(gè)點(diǎn)。實(shí)際上,我們所需要做的是投影的反變換,投影公式是:
u=f?x/z
和
v=f?y/z
其中u是屏幕上x的坐標(biāo),最小值為XOrigin,v是屏幕上的y的坐標(biāo),最小值YOrigin。
平面公式是:
Ax+By+Cz+D=0
一旦我們已經(jīng)得到分離的x和y,有:
x=uz/f
和
y=vz/f
如果我們在平面等式中替代變量,公式變?yōu)椋?/p>
A(uz/f)+B(vz/f)+Cz=-D
我們可以提取z分量:
z(A(u/f)+B(v/f)+C)=-D
所以我們得到z:
z=-D/(A(u/f)+B(v/f)+C)
但是由于對于每一個(gè)像素我們需要執(zhí)行以上的除法,而計(jì)算1/z將提高程序的速度:
1/z=-(A(u/f)+B(v/f)+C)/D
1/z=-(A/(fD))u-(B/(fD))v-C/D
所以在一次像素程序運(yùn)行的開始:
1/z=-(A/(fD))u1-(B/(fD))v-C/D
對于每一個(gè)像素,增量為:
-(A/(fD))>
下面是程序:
#defineFIXMUL(1<<20)
intoffset=y*MODEINFO.XResolution+x1;
inti=x1-Origin.x,j=y-Origin.y;
floatz_,dz_;
ZBUFTYPEz,dz;
//初始化1/z值(z:1/z)
dz_=((A/(float)Focal_Distance)/-D);
z_=((dz_*i)+((B*j/(float)Focal_Distance)+C)/-D);
dz=dz_*FIXMUL;
z=z_*FIXMUL;
然后,對于每一個(gè)像素,我們簡單的計(jì)算:
if(z>ZBuffer[offset])
{
zbuffer[offset]=z;
SCREENBUFFER[offset]=color;
}
z+=dz;
3D紋理映射
概述
魔幻數(shù)字
紋理映射的透視修正
概述
在做紋理映射時(shí)首先考慮的是建立紋理數(shù)組和初始化3D紋理坐標(biāo)。紋理將存儲(chǔ)在:
#defineMAXTEXTURES16
bitmap_tTextures[MAXTEXTURES];
我們從PCX文件分配和加載紋理。這里假設(shè)紋理大小為64x64。我們使用polygon_t
結(jié)構(gòu)的紋理坐標(biāo):
vertex_tP,M,N;
我們在函數(shù)中初始化紋理,該函數(shù)在建立多邊形后被調(diào)用。P是紋理的原點(diǎn),M是
紋理的水平線末端,N是垂直線的末端。
voidTEX_Setup(Polygon_t*Polygon,Object_t*Object)
{
Polygon->P.Local=P3D(Vert(1).Local.x,Vert(1).Local.y,Vert(1).Local.z);
Polygon->M.Local=P3D(Vert(0).Local.x,Vert(0).Local.y,Vert(0).Local.z);
Polygon->N.Local=P3D(Vert(2).Local.x,Vert(2).Local.y,Vert(2).Local.z);
}
我們需要象任何其他對象的頂點(diǎn)一樣變換紋理坐標(biāo),所以我們需要建立世界變換和
一個(gè)對齊變換函數(shù):
voidTR_Object(Object_t*Object,floatmatrix[4][4])
{
intv,p;
for(v=0;v<Object->VertexCount;v++)
VEC_MultMatrix(&Object->Vertex[v].Local,matrix,&Object->Vertex[v].World);
for(p=0;p<Object->PolygonCount;p++)
{
VEC_MultMatrix(&Object->Polygon[p].P.Local,matrix,&Object->Polygon[p].P.World);
VEC_MultMatrix(&Object->Polygon[p].M.Local,matrix,&Object->Polygon[p].M.World);
VEC_MultMatrix(&Object->Polygon[p].N.Local,matrix,&Object->Polygon[p].N.World);
}
}
voidTR_AlignObject(Object_t*Object,floatmatrix[4][4])
{
intv,p;
for(v=0;v<Object->VertexCount;v++)
VEC_MultMatrix(&Object->Vertex[v].World,matrix,&Object->Vertex[v].Aligned);
for(p=0;p<Object->PolygonCount;p++)
{
VEC_MultMatrix(&Object->Polygon[p].P.World,matrix,&Object->Polygon[p].P.Aligned);
VEC_MultMatrix(&Object->Polygon[p].M.World,matrix,&Object->Polygon[p].M.Aligned);
VEC_MultMatrix(&Object->Polygon[p].N.World,matrix,&Object->Polygon[p].N.Aligned);
}
}
魔幻數(shù)
既然我們已經(jīng)得到了變幻的紋理坐標(biāo),我們的目標(biāo)是發(fā)現(xiàn)在紋理位圖上的像素
水平和垂直的坐標(biāo)在屏幕上如何繪制。紋理坐標(biāo)稱為u和v。下面的公式給出坐標(biāo):
u=a*TEXTURE_SIZE/c
和
v=b*TEXTURE_SIZE/c
a,b,c滿足下面的等式:
a=Ox+Vxj+Hxi
b=Oy+Vyj+Hyi
c=Oz+Vzj+Hzi
其中O,H,V數(shù)是魔幻數(shù)。它根據(jù)下面的公式由紋理坐標(biāo)計(jì)算得到:
Ox=NxPy-NyPx
Hx=NyPz-NzPy
Vx=NzPx-NxPz
Oy=MxPy-MyPx
Hy=MyPz-MzPy
Vy=MzPx-MxPz
Oz=MyNx-MxNy
Hz=MzNy-MyNz
Vz=MxNz-MzNx
這里,我們不解釋魔幻數(shù)的原因。它看起來像奇怪的叉積。
紋理映射透視修正
O,H,V數(shù)的計(jì)算需要一些修正,所以我們增加下面到ENG3D_SetPlane:
//用于修正當(dāng)數(shù)字變得太大時(shí)候的錯(cuò)誤
#defineFIX_FACTOR1/640
//初始化紋理矢量
P=Polygon->P.Aligned;
M=VEC_Difference(Polygon->M.Aligned,Polygon->P.Aligned);
N=VEC_Difference(Polygon->N.Aligned,Polygon->P.Aligned);
P.x*=Focal_Distance;
P.y*=Focal_Distance;
M.x*=Focal_Distance;
M.y*=Focal_Distance;
N.x*=Focal_Distance;
N.y*=Focal_Distance;
下面是VEC_Difference的實(shí)現(xiàn):
_3DVEC_Differen
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024銅棒工業(yè)應(yīng)用技術(shù)培訓(xùn)合同模板3篇
- 二零二五版汽車維修后舊件買賣合同3篇
- 2025年度海上船舶船員勞務(wù)派遣服務(wù)勞動(dòng)合同3篇
- 邛崍專業(yè)保潔合同范本
- 2025年度高端建筑材料采購合同質(zhì)量保障與驗(yàn)收3篇
- 2024瀝青混凝土路面工程
- 2025年度智能草花種苗購銷合同模板3篇
- 2025年度咖啡館餐廳承包管理合同3篇
- 2024物業(yè)清潔與綠化服務(wù)合同詳細(xì)
- 2024版行政崗位勞動(dòng)合同樣本
- 2025年度版權(quán)授權(quán)協(xié)議:游戲角色形象設(shè)計(jì)與授權(quán)使用3篇
- 2024年08月云南省農(nóng)村信用社秋季校園招考750名工作人員筆試歷年參考題庫附帶答案詳解
- 防詐騙安全知識培訓(xùn)課件
- 心肺復(fù)蘇課件2024
- 2024年股東股權(quán)繼承轉(zhuǎn)讓協(xié)議3篇
- 2024-2025學(xué)年江蘇省南京市高二上冊期末數(shù)學(xué)檢測試卷(含解析)
- 四川省名校2025屆高三第二次模擬考試英語試卷含解析
- 《城鎮(zhèn)燃?xì)忸I(lǐng)域重大隱患判定指導(dǎo)手冊》專題培訓(xùn)
- 湖南財(cái)政經(jīng)濟(jì)學(xué)院專升本管理學(xué)真題
- 考研有機(jī)化學(xué)重點(diǎn)
- 全國身份證前六位、區(qū)號、郵編-編碼大全
評論
0/150
提交評論