




版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、第二章 幾何和變換幾乎所有圖形軟件都以幾何類(lèi)(geometric classes, 這里指c+類(lèi))為基礎(chǔ).這些類(lèi)表示了諸如點(diǎn),向量,光線等等的數(shù)學(xué)構(gòu)件. 由于我們?cè)谙到y(tǒng)中會(huì)到處用到這些類(lèi), 良好的抽象和有效的實(shí)現(xiàn)至關(guān)重要. 本章會(huì)講解pbrt的幾何基礎(chǔ)的接口和實(shí)現(xiàn).幾何類(lèi)見(jiàn)文件 core/geometry.h 和core/geometry.cpp.變換矩陣見(jiàn)文件 core/transform.h 和core/transform.cpp.2.1 坐標(biāo)系統(tǒng)pbrt用三個(gè)浮點(diǎn)數(shù)坐標(biāo)值x,y,z來(lái)表示三維點(diǎn),向量和法向量. 當(dāng)然,這些值只有在一個(gè)給定的坐標(biāo)系下才有意義: 給定一個(gè)原點(diǎn)和三個(gè)定義x,y,
2、z軸的向量,就定義了這個(gè)坐標(biāo)系(frame).在n維空間中, 坐標(biāo)系的原點(diǎn)P0和其n個(gè)線性無(wú)關(guān)的基向量定義了n維仿射空間(affine space).所有空間中的向量V可以被表達(dá)成為基向量(V1,V2, ., Vn)的線性組合: V = s1V1 + s2V2 + . + snVn (s1, s2, . sn是唯一存在的一組純量, 被稱(chēng)為V關(guān)于基(V1,V2.Vn)的表達(dá)).同樣地, 對(duì)與點(diǎn)P而言, 它可用原點(diǎn)P0和基向量(V1,V2, ., Vn)表達(dá): P = P0 +s1V1 + s2V2 + . + snVn以上討論有點(diǎn)循環(huán)定義的味道: 要定義坐標(biāo)系我們需要定義一個(gè)點(diǎn)和一組向量, 而點(diǎn)
3、和向量只有在給定的一個(gè)坐標(biāo)系下才有意義. 因此,我們需要一個(gè)標(biāo)準(zhǔn)坐標(biāo)系, 其原點(diǎn)是(0,0,0), 基向量為(1,0,0), (0,1,0) 和(0,0,1).2.1.1 左/右手坐標(biāo)系我們知道坐標(biāo)系分左手坐標(biāo)系和右手坐標(biāo)系,pbrt用左手坐標(biāo)系.2.2 向量 = class COREDLL Vector public: ;一個(gè)向量表達(dá)了三維空間內(nèi)的一個(gè)方向, 它由三個(gè)浮點(diǎn)數(shù)定義:= float x, y, z; x,y,z被定義為公共成員, 不太符合C+的封裝原則, 但我們這樣做是為了代碼的清晰和效率.缺省情況下, (x,y,z)被設(shè)成0. 用戶(hù)可以選擇給定任意值:= Vector(floa
4、t _x = 0, float _y = 0, float _z = 0) : x(_x), y(_y), z(_z) 2.2.1 向量運(yùn)算向量加法運(yùn)算: += Vector operator+(const Vector &v) const return Vector(x+v.x, y + v.y, z + v.z); Vector& operator+=(const Vector &v) const x += v.x;y += v.y;z += v.z; return *this; 向量減法運(yùn)算與上類(lèi)似, 略.2.2.2 比例運(yùn)算比例運(yùn)算是純量乘法, 即是將向量每個(gè)分量乘以一個(gè)純量, 從而改
5、變了它的長(zhǎng)度. += Vector operator*(float f) const return Vector(f*x,f*y, f*z); Vector& operator*=(const Vector &v) const x*=f;y *= f;z*= f; return *this; = inline Vector operator*(float f, const Vector &v) return v*f; 類(lèi)似地,我們可以定義純量除法 operator/ 和 operator /=, 此略. Vector類(lèi)還有一個(gè)取負(fù)值的單操作符定義, 用來(lái)返回一個(gè)方向相反的向量: += Vect
6、or operator-() const return Vector(-x, -y, -z); 下面兩個(gè)函數(shù)可以用索引值0,1,2方便地使用向量的各個(gè)分量: v0得到x值, v1得到y(tǒng)值, v2得到z值. += float operator(int i) const Assert(i = 0 & i = 0 & i = 2); return (&x); 2.2.3 點(diǎn)積和叉積對(duì)于兩個(gè)向量V和W, 它們的點(diǎn)積(V . W)定義為:Vx*Wx + Vy*Wy + Vy*Wy. = inline float Dot(const Vector &v1, const Vector &v2) return
7、 v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; 點(diǎn)積跟兩向量的夾角關(guān)系是: (v.w) = |v| |w| cos 如果兩個(gè)非退化(即非(0,0,0)的向量相互垂直, 則(v.w)為零; 反之,也成立. 兩個(gè)或多個(gè)相互垂直的向量被稱(chēng)為是正交的(orthogonal), 一組正交的單位向量被稱(chēng)為規(guī)格化正交的(orthonormal).假定u,v,w是向量, s是純量, 則有下列性質(zhì): (u . v) = (v . u) (su . v) = s(v . u) (u . (v + w) = (u . v) + (u . w)我們常常要計(jì)算點(diǎn)積的絕對(duì)值, 故有如下
8、函數(shù): = inline float AbsDot(const Vector &v1, const Vector &v2) return fabsf(Dot(v1,v2); 叉 積是另一個(gè)很有用的向量操作. 給定三維空間的兩個(gè)向量, 叉積v w 是垂直于兩者的向量. 注意這個(gè)新向量的朝向是由坐標(biāo)系的左右手定則(handedness)決定的.給定兩個(gè)正交的向量 v 和 w, 那么, (v, w, v w) 就按照給定的左右手定則形成一個(gè)坐標(biāo)系.在左手系中, 叉積定義為: (v w)x = vy wz vz wy (v w)y = vz wx vx wz (v w)z = vx wy vy wx有
9、一個(gè)幫助記憶的公式是計(jì)算下面矩陣的行列式值:其中i, j, k分別代表軸(1, 0, 0), (0, 1, 0), (0, 0, 1). 注意這只是一個(gè)記憶工具, 而不是嚴(yán)格的數(shù)學(xué)表達(dá), 因?yàn)榫仃嚢鸭兞亢拖蛄炕旌显谝黄鹩昧? += inline Vector Cross(const Vector &v1, const Vector &v2) return Vector(v1.y * v2.z) (v1.z * v2.y), (v1.z * v2.x) (v1.x * v2.z), (v1.x* v2.y) (v1.y * v2.z);從叉積的定義中,我們得出: | v w | =| v | |
10、w| sin (是v和w的夾角)從上式可以看出, 兩個(gè)相互垂直的單位向量的叉積也是一個(gè)單位向量. 如果兩個(gè)向量平行, 則它們的叉積是個(gè)退化的向量.另外, 可以看出, 以?xún)蓚€(gè)向量v1 , v2為邊的平行四變形面積是| v1 v2 |.2.2.4 向量正規(guī)化把一個(gè)向量變換成具有相同方向的單位向量就是向量的正規(guī)化, 方法是將向量的各個(gè)分量除以向量的長(zhǎng)度: += float LengthSquared() const return x * x + y * y + z * z; float Length() const return sqrtf(LengthSquared(); += inline Ve
11、ctor Normalize (const Vector &v) Return v / v.Length(); 2.2.5 由一個(gè)向量建立的坐標(biāo)系我們會(huì)經(jīng)常用一個(gè)向量構(gòu)造一個(gè)坐標(biāo)系. 由于叉積跟兩個(gè)向量垂直, 我們可以通過(guò)兩次叉積(該向量跟任意一個(gè)向量叉積得到第二個(gè)向量, 第一和第二向量叉積得到第三個(gè)向量) 來(lái)得到三個(gè)相互垂直的向量,因而得到一個(gè)坐標(biāo)系.Pbrt所用的方法是: 給定一個(gè)正規(guī)化的向量v1, 把該向量其中一個(gè)分量置零并互換另外兩個(gè)分量的值, 然后對(duì)之正規(guī)化,就得到第二個(gè)向量 v2 (可以驗(yàn)證v1和v2相互垂直), 再由v1和v2的叉積得到第三個(gè)向量: += inline void
12、CoordinateSystem(const Vector &v1, Vector *v2, Vector *v3) if(fabsf(v1.x) fabsf(v1.y) Float invLen = 1.f/sqrtf(v1.x * v1.x + v1.z * v1.z); *v2 = Vector(-v1.z * invLen, 0.f, v1.x * invLen); else Float invLen = 1.f/sqrtf(v1.y * v1.y + v1.z * v1.z); *v2 = Vector(0.f,v1.z * invLen, -v1.y* invLen); *v3 =
13、 Cross(v1, *v2); 2.3 點(diǎn) += Class COREDLL Point Public: ;點(diǎn)是三維空間的位置.雖然它跟向量一樣也是用(x,y,z)三個(gè)坐標(biāo)值表示, 但由于它們本質(zhì)上的不同, 處理它們的方式也是不同的. = Float x, y, z;跟Vector的構(gòu)造器一樣, Point構(gòu)造器也是用可選的參數(shù)設(shè)置x,y,z的坐標(biāo)值: = Point(float _x = 0, float _y = 0, float _z = 0) : x(_x), y(_y), z(_z) 有一些Point的函數(shù)返回一個(gè)Vector,或者用一個(gè)Vector作為參數(shù). 比如, 把一個(gè)向量加
14、到一個(gè)點(diǎn)上, 就是相當(dāng)于將它在給定的方向上偏移而得到一個(gè)新的向量. 同樣地, 兩個(gè)點(diǎn)相減,得到它們之間的向量: += Point operator+ (const Vector &v) const return Point(x + v.x, y + v.y, z + v.z); Point &operator += (const Vector &v) x += v.x; y += v.y; z += v.z; return *this; += Vectoroperator- (const Point &p) const return Vector(x - p.x, y -p.y, z- p.z
15、); Point operator- (const Vector &v) const return Point(x -v.x, y - v.y, z - v.z); Point &operator -= (const Vector &v) x -= v.x; y -= v.y; z -= v.z; Return *this;下面是求兩點(diǎn)之間距離的函數(shù): += inline float Distance(const Point &p1, const Point &p2) return (p1- p2).Length(); inline float DistanceSquared(const Po
16、int &p1, const Point &p2) return (p1- p2).LengthSquared(); 雖然點(diǎn)乘以純量不具數(shù)學(xué)意義, 但是Point類(lèi)仍然支持純量乘的定義, 用以求多個(gè)點(diǎn)的加權(quán)和. 其實(shí)現(xiàn)跟Vector中的實(shí)現(xiàn)類(lèi)似, 從略.2.4 法向量+= class COREDLL Normal public: ;法向量是在給定點(diǎn)上垂直于表面的向量。它可以被定義成兩個(gè)互相不平行的表面切向量的叉積。雖然法向量跟向量很相似,但是應(yīng)知它們的不同:因?yàn)榉ㄏ蛄渴歉鶕?jù)它跟特定的曲面來(lái)定義的,在某些情況下跟向量是不同的,特別是使用變換的時(shí)候。(見(jiàn)第2.8節(jié))。Normal和Vector的實(shí)
17、現(xiàn)很相似,都是用三個(gè)浮點(diǎn)數(shù)x,y,z表示,并定義了法向量之間的加,減,純量乘,正規(guī)化等運(yùn)算。但是,法向量不能跟一個(gè)點(diǎn)相加,也不能取兩個(gè)法向量的叉積。還有,法向量不一定是正規(guī)化的。Normal提供了由一個(gè)Vector初始化一個(gè)Normal的構(gòu)造器。由于Normal和Vector有細(xì)微的差別,我們不希望它們之間有隱性的轉(zhuǎn)換。為此,C+的explicit關(guān)鍵詞可以保證它們之間顯性的轉(zhuǎn)換。 = explict Normal(const Vector &v) : x(v.x),y(v.y), z(v.z) += explict Vector(const Normal &n); += inline Vec
18、tor:Vector(const Normal &n) :x(n.x), y(n.y), z(n.z) 這樣一來(lái),如果聲明了Vectorv; Normaln; 那么 n = v 就是非法的,必須用顯式的轉(zhuǎn)換:n = Normal(v).我們還重載了Dot()和AbsDot() 函數(shù)來(lái)覆蓋求法向量和向量之間的求點(diǎn)積的各種組合情況, 另外,其它跟Vector類(lèi)似的函數(shù)都不提及了。2.5 光線 += class COREDLL Ray public: ;光線是一條由其原點(diǎn)和方向定義的射線。pbrt用Ray類(lèi)來(lái)表達(dá)光線,其中用一個(gè)Point成員變量表示其原點(diǎn),用一個(gè)Vector表示其方向: = Poi
19、nto; Vector d;光線的參數(shù)化形式是一個(gè)關(guān)于純量t的方程: r(t) = o + t d 0t Ray 類(lèi)還包含兩個(gè)值mint和maxt,把光線限定在r(mint), r(maxt)區(qū)間之間。 它們聲明為mutable, 這意味著即使它們所在的Ray是const, 也是可以被改變的。其目的就是方便光線/物體的求交, 因?yàn)樵谶@過(guò)程中,總是要記錄最近的交點(diǎn)所對(duì)應(yīng)的t值。 += mutable float mint, maxt;為了模擬運(yùn)動(dòng)模糊效果, 每條光線還需要一個(gè)時(shí)間值: += float time;Ray的構(gòu)造器很簡(jiǎn)單明了: = Ray() : mint(RAY_EPSILON),
20、 maxt(INFINITY), time(0.f) Ray(const Point &origin, const Vector &direction, float start = RAY_EPSION, float end = INFINITY, float t = 0.f) : o(origin), d(direction), mint(start), maxt(end), time(t) = #defineRAY_EPSILON 1e-3f注意我們用一個(gè)極小的數(shù)(RAY_EPSILON)來(lái)初始化mint, 而不是用0, 原因是避免因浮點(diǎn)計(jì)算精度而引起的自相交的錯(cuò)誤, 這是一個(gè)在光線追蹤中
21、的很典型的手法。我們還重載函數(shù)操作符“()”, 來(lái)求和參數(shù)t對(duì)應(yīng)的點(diǎn): += Point operator() (float t) const return o + d * t;這樣,我們可以很方便地寫(xiě)類(lèi)似下面的代碼: Ray r(Point(0,0,0), Vector(1,2,3); Point p = r(1.7);2.5.1 光線微分為 了更好地利用第11章定義的紋理函數(shù)進(jìn)行反走樣,pbrt對(duì)每條被追蹤的光線都保持著一些附加的信息。 在第11.1節(jié), 這些信息用在Texture類(lèi)中估算一小部分的場(chǎng)景在圖像平面上的投影面積。這樣,Texture類(lèi)就可以計(jì)算出紋理在這個(gè)面積上的平均值,從而
22、得到更好 的圖像。RayDifferential是Ray的子類(lèi), 并包含兩條輔助光線的附加信息。 這兩條光線表示從主光線向x和y方向分別偏置一個(gè)像素而得到的相機(jī)光線。確定了這三條光線投射到被著色物體上的區(qū)域,Texture就可以估算出用于反走樣的平均值。 += class COREDLL RayDifferential : public Ray public: ; = RayDifferential() hasDifferentials = false; RayDifferential(const Point &org, const Vector &dir) : Ray(org, dir) h
23、asDifferentials = false; 注意我們用到關(guān)鍵字explicit,防止不經(jīng)意的Ray到RayDifferential的轉(zhuǎn)換。 變量hasDifferentials被初始化為false, 表示相鄰的兩條光線還是未知的。 += explicit RayDifferential(const Ray &ray) : Ray(ray) hasDifferentials = false; = bool hasDifferentials; Ray rx, ry;2.6 三維包圍盒 += class COREDLL BBOX public: ;pbrt所要渲染的場(chǎng)景經(jīng)常包含計(jì)算很費(fèi)時(shí)的物體
24、。 一個(gè)包含整個(gè)物體的三維包圍體對(duì)很多操作而言都會(huì)非常有用。比如, 如果光線沒(méi)有穿過(guò)包圍盒, 就不必求光線和其中所包圍的物體的交點(diǎn)了。包圍體的有效性跟兩個(gè)因素有關(guān):計(jì)算包圍體的時(shí)間化費(fèi)和包圍盒包圍物體的緊密程度。如果哦包圍體太“寬松”了,就會(huì)浪費(fèi)很多不必要的計(jì)算;反過(guò)來(lái), 如果強(qiáng)求非常緊密的包圍體,那么包圍體很可能變得太復(fù)雜,時(shí)間耗費(fèi)也會(huì)不菲。包 圍體有很多種, pbrt用到沿軸的包圍盒(axis-aligned bounding boxes, AABB). 其他的常見(jiàn)的選擇包括沿方向的包圍盒(oriented bounding boxes, OBB)和包圍球。AABB可以由一個(gè)頂點(diǎn)和分別沿x
25、,y,z軸方向的三個(gè)長(zhǎng)度值來(lái)表示, 也可以由包圍盒上兩個(gè)相對(duì)的頂點(diǎn)來(lái)表示。pbrt就是用兩點(diǎn)表示的,一個(gè)點(diǎn)的坐標(biāo)是x,y,z的最小值,另一個(gè)是x,y,z的最大值。BBOX缺省構(gòu)造器把包圍盒的范圍定義成退化的 : pMin.x pMax.x, 即是空包圍盒。 = BBox() pMin = Point (INFINITY, INFINITY, INFINITY); pMax= Point (-INFINITY, -INFINITY, -INFINITY); ; = Point pMin, pMax;有時(shí)我們用到包含一個(gè)點(diǎn)的包圍盒: += BBox(const Point &p) : pMin(p
26、), pMax(p) 我們還可以用兩個(gè)點(diǎn)p1, p2來(lái)構(gòu)造BBOX, p1和p2不必滿(mǎn)足p1.x = p2.x等條件, 構(gòu)造器可以計(jì)算出最大、最小值: += BBox(const Point &p1,const Point &p2 ) pMin = Point(min(p1.x, p2.x), min(p1.y, p2.y), min(p1.z, p2.z); pMax = Point(max(p1.x, p2.x), max(p1.y, p2.y), max(p1.z, p2.z); 給定一個(gè)包圍盒和一個(gè)點(diǎn),BBox:Union()計(jì)算并返回一個(gè)包含該點(diǎn)和原包圍盒的新包圍盒: = CORED
27、LL BBox Union(const BBOX &b, const Point &p) BBox ret = b; ret.pMin.x = min(b.pMin.x, p.x); ret.pMin.y = min(b.pMin.y, p.y); ret.pMin.z = min(b.pMin.z, p.z); ret.pMax.x = min(b.pMax.x, p.x); ret.pMax.y = min(b.pMax.y, p.y); ret.pMax.z = min(b.pMax.z, p.z); return ret; 同樣地,我們可以構(gòu)造一個(gè)包含兩個(gè)包圍盒的包圍盒: += fri
28、end COREDLL BBox Union(const BBox &b, const BBox &b2);很容易判定兩個(gè)包圍盒是否重疊: += bool Overlaps(const BBox &b) bool x = (pMax.x = b.pMin.x) & (pMin.x = b.pMin.y) & (pMin.y = b.pMin.z)& (pMin.z = b.pMax.z); return (x & y & z); 下面函數(shù)判定一個(gè)點(diǎn)是否在包圍盒內(nèi): += boolInside(const Point &pt)const return (pt.x = pMin.x & pt.x
29、= pMin.y & pt.y= pMin.z & pt.z= pMax.z); BBox:Expand()用來(lái)擴(kuò)張包圍盒, BBox:Volume()用來(lái)計(jì)算包圍盒的體積: += void Expand(float delta) pMin -= Vector(delta, delta, delta); pMax += Vector(delta, delta, delta); += float Volume() const Vector d = pMax - pMin; return d.x * d.y * d.z; BBox:MaximumExtent()返回最長(zhǎng)的那個(gè)軸。在建造kd樹(shù)時(shí),我
30、們用它決定沿那個(gè)軸劃分。 += int BBox:MaximumExtent() const Vector diag = pMax - pMin; if (diag.x diag.y & diag.x diag.z) return 0; else if (diag.y diag.z) return 1; else return 2; BBox:BoundingSphere()返回包含該包圍盒的球的中心和半徑。 雖然包圍球比對(duì)應(yīng)的包圍盒要寬松得多, 但有時(shí)仍是很有用的。在第15章, 我們用它得到包含整個(gè)場(chǎng)景的包圍球,用以生成可能跟場(chǎng)景相交的隨機(jī)光線。 += int BBox:BoundingSp
31、here(Point *c, float *rad) const *c = 0.5f * pMin + 0.5*pMax; *rad = Distance(*c, pMax); 2.7 變換一般地說(shuō), 變換T是從點(diǎn)到點(diǎn)或從向量到向量的映射: p = T(p) v = T(v).變換可以是任意的,但我們只考慮滿(mǎn)足下面條件的變換:1. 線性: 如果T是任意一個(gè)線性變換, s是任意純量,那么 T(sv) = sT(v), T(v1+v2) = T(v1)+T(v2).這兩個(gè)性質(zhì)可以極大地簡(jiǎn)化變換的推導(dǎo)。2. 連續(xù)性: 粗略地講, T把p或v的鄰近點(diǎn)或向量變換后,結(jié)果仍與p或v鄰近。3. 一一對(duì)應(yīng)和可
32、逆性:對(duì)每個(gè)點(diǎn)p, T把p映射到唯一的點(diǎn)p.近一步地,存在可逆變換T-1把p變換回p.我們常常要計(jì)算一個(gè)點(diǎn),向量或法向量在另一個(gè)坐標(biāo)系下的坐標(biāo)值。由線性代數(shù)中的知識(shí)可以知道,4乘4矩陣可以表達(dá)點(diǎn)或向量從一個(gè)坐標(biāo)系到另一個(gè)坐標(biāo)系的線性變換。另外,這樣的4乘4矩陣可以表達(dá)同一坐標(biāo)系下的所有關(guān)于點(diǎn)或向量的線性變換。所以,有下面關(guān)于矩陣的不同解釋?zhuān)?坐標(biāo)系內(nèi)的變換: 給定一個(gè)點(diǎn),矩陣可以表達(dá)如何計(jì)算在同一坐標(biāo)系下變換(比如平移)后的新點(diǎn)。 坐標(biāo)系之間的變換: 一個(gè)矩陣可以表達(dá)在原坐標(biāo)系下的點(diǎn)或向量在新坐標(biāo)系下的坐標(biāo)。一 般說(shuō)來(lái),變換可以讓我們利用最方便的坐標(biāo)系。舉例來(lái)說(shuō),我們定義一個(gè)虛擬相機(jī),它位于原
33、點(diǎn),朝向z軸,y軸指向上方,x軸指向右方。這可以極大地方便了相 機(jī)的實(shí)現(xiàn)。然后,我們可以把相機(jī)放在場(chǎng)景的任何位置,令其朝向任何方向,我們只需定義一個(gè)變換,把在場(chǎng)景坐標(biāo)系下的點(diǎn)映射到相機(jī)坐標(biāo)系。2.7.1 齊次坐標(biāo)給 定有(p,v1,v2,v3)定義的坐標(biāo)系,用(x,y,z)既可以表示點(diǎn)(Px,Py,Pz)也可以表示向量(Vx,Vy,Vz)。為了避免這兩種表示 的模糊性,我們利用本章開(kāi)始介紹的點(diǎn)和向量的表達(dá)方式,把點(diǎn)寫(xiě)成內(nèi)積形式S1 S2 S3 1 v1 v2 v3 P0T, 把向量寫(xiě)成內(nèi)積形式S1 S2 S3 0 v1 v2 v3 P0T。S1 S2 S3 1被稱(chēng)為點(diǎn)的齊次表示,S1 S2 S
34、3 0 被稱(chēng)為向量的齊次表示。第四個(gè)坐標(biāo)值有時(shí)被稱(chēng)作權(quán)值。對(duì)于點(diǎn)而言,任何非零的純量都可以做為權(quán)值:齊次點(diǎn)(1,3,-2,1)和(-2,-6,4,-2) 代表同一個(gè)點(diǎn)(1,3,-2). 一般地說(shuō),齊次點(diǎn)和所表達(dá)的三維點(diǎn)有下列關(guān)系: (x, y, z, w) = (x/w, y/w, z/w)有了上面的基礎(chǔ),我們可以考察一個(gè)變換矩陣如何把點(diǎn)和向量變換到另一個(gè)坐標(biāo)系的。設(shè)有矩陣M描述從一個(gè)坐標(biāo)系到另一個(gè)坐標(biāo)系的變換: 將M作用到x軸向量(1, 0, 0),有: M 1 0 0 0T = m00 m10 m20 m30T所以,矩陣的四列就是變換下面的x,y,z軸和原點(diǎn)的結(jié)果: x = 1 0 0 0
35、T y = 0 1 0 0T z = 0 0 1 0T p = 0 0 0 1T我們不顯式地使用齊次坐標(biāo),沒(méi)有Homogeneous類(lèi)。然而,下節(jié)定義的變換例程會(huì)隱式地把點(diǎn),向量,法向量轉(zhuǎn)換成齊次坐標(biāo)形式,然后使用齊次坐標(biāo)變換,再轉(zhuǎn)換回三維坐標(biāo)形式。這樣,所有的齊次坐標(biāo)處理都集中在一個(gè)地方(即在變換的實(shí)現(xiàn)中)。 = class COREDLL Transform public: private: ;一個(gè)變換由一個(gè)對(duì)Matrix4x4對(duì)象的引用m來(lái)表示。底層的Matrix4x4定義在A.3.2節(jié)。m是以行為主(row-major)的形式存儲(chǔ)的,就是說(shuō), 元素mj對(duì)應(yīng)第i行,第j列的元素。為了方便
36、,Transform類(lèi)還保持了m的逆陣mInv.使用引用計(jì)數(shù)的模版類(lèi)Reference在A.2.2節(jié)中有所介紹。它自動(dòng)記錄有多少個(gè)對(duì)象保持對(duì)該對(duì)象的引用,并當(dāng)引用計(jì)數(shù)為零時(shí)自動(dòng)釋放內(nèi)存。Transform 類(lèi)保持對(duì)矩陣的引用,而不是直接存儲(chǔ)矩陣本身,這樣多個(gè)Transform對(duì)象可以指向同一個(gè)矩陣。這意味著一個(gè)Transform的實(shí)例占很少的內(nèi)存空 間。如果場(chǎng)景中有非常多的形體,且擁有同一個(gè)物體/世界變換,那么它們都有自己的Transform對(duì)象,但是卻共享同一個(gè)矩陣,這樣所節(jié)省的空間是很可 觀的。當(dāng)然,這也會(huì)犧牲一些靈活性, 特別是矩陣一但建立就無(wú)法修改。在實(shí)際情況中,這不是問(wèn)題,因?yàn)閳?chǎng)景中
37、的變換通常是當(dāng)pbrt讀場(chǎng)景描述文件時(shí)建立的,以后在渲染過(guò)程中也不需要修改。在這樣的設(shè)計(jì)決定下,每次修改矩陣,都要?jiǎng)?chuàng)建一個(gè)新的矩陣。 = Reference m, mInv;2.7.2 基本操作新變換被創(chuàng)建時(shí),被初始化為單位變換:把點(diǎn)和向量映射到它自己的變換。這種變換用單位矩陣表示(即對(duì)角線上的元素都為1,而其它元素為0的矩陣)。 = Transform() m = mInv = new Matrix4x4; 一個(gè)變換也可以由一個(gè)矩陣來(lái)初始化: += Transform(float mat44) m = new Matrix4x4(mat00, mat01, mat02, mat03, mat
38、10, mat11, mat12, mat13, mat20, mat21, mat22, mat23, mat30, mat31, mat32, mat33); mInv = m-Inverse(); += Transform(const Reference &mat) m = mat; mInv = m-Inverse(); 最后,我們給出最常用的Transform的構(gòu)造器: 其參數(shù)是一個(gè)變換矩陣和已經(jīng)計(jì)算好的逆陣。這種方法很不錯(cuò),因?yàn)楹芏嗟膸缀巫儞Q的矩陣的逆陣是很簡(jiǎn)單的,我們就省去了計(jì)算逆陣的通用算法所產(chǎn)生的開(kāi)銷(xiāo)。當(dāng)然,調(diào)用者要保證所提供的逆陣是正確的。 += Transform(con
39、st Reference &mat, const Reference &minv) m = mat; mInv = minv; += Transform GetInverse() const return Transform(mInv, m); 2.7.3 平移變換平移變換是最簡(jiǎn)單的變換之一。對(duì)于點(diǎn)P,平移變換把點(diǎn)的坐標(biāo)平移x, y, z, 記作T(x, y, z). 比如, T(2,2,1)(x,y,z) = (x+2, y+2, z+1). 平移變換有下列性質(zhì): T(0,0,0) = I T(x1,y1,z1) T(x2,y2,z2) = T(x1+x2, y1+y2, z1+z2) T(
40、x1,y1,z1) T(x2,y2,z2) = T(x2,y2,z2) T(x1,y1,z1) T-1(x,y,z) = T(-x, -y, -z)寫(xiě)成矩陣形式就是: 如果對(duì)點(diǎn)(x,y,z,1)進(jìn)行平移變換,則有:如果對(duì)向量(x,y,z,0)進(jìn)行平移變換,則有:可以看出,平移的結(jié)果跟原向量相同,因?yàn)槠揭撇⒉桓淖兿蛄康姆较?,故也不改變向量的值。平移變換的實(shí)現(xiàn)很簡(jiǎn)單: = COREDLL Transform Translate(const Vector &delta) Matrix4x4 *m, *minv; m = new Matrix4x4(1, 0, 0, deltax, 0,1,0, de
41、ltay, 0,0,1,deltaz, 0,0,0,1); mInv = new Matrix4x4(1, 0, 0, -deltax, 0,1,0, -deltay, 0,0,1,-deltaz, 0,0,0,1); return Transform(m, minv);2.7.4 比例變換L另一個(gè)基本變換是比例變換, S(sx,sy,sz).其變換的結(jié)果等同于把點(diǎn)或向量的分量分別乘以比例因子. 比如: S(2, 2, 1)(x, y, z) = (2x, 2y, z).它有如下基本性質(zhì): S(1, 1, 1) = I S(x1, y1, z1) S(x2, y2, z2) = S(x1x1,
42、 y1y2, z1z2) S-1(x,y,z) = S(1/x, 1/y, 1/z)比例變換分等比例變換(uniform scaling, 三個(gè)比例因子相等), 和非等比例變換(nonuniform scaling, 三個(gè)比例因子不相等). 矩陣形式如下: 比例變換的實(shí)現(xiàn)很簡(jiǎn)單: += COREDLL Transform Scale(float x, float y, float z) Matrix4x4 *m, *minv; m = new Matrix4x4( x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1); minv = new Matrix4x4(1.0f/x, 0, 0, 0, 0,1.0f/y,0, 0, 0,0,1.0f/z,0, 0,0,0,1);return Transform(m, minv); 2.7.5 繞x,y,z軸的旋轉(zhuǎn)變換另一個(gè)變換類(lèi)型是旋轉(zhuǎn)變換R。一般地說(shuō),我們可以定義繞一個(gè)起始于原點(diǎn)的任意方向上的任意軸旋轉(zhuǎn)任意角度的變換。最常用的變換還是繞x,y或z軸的變換,我們分別記為Rx(), Ry(), Rz(). 繞任意軸(x,y,z)旋轉(zhuǎn)的變換記為R(
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 園林建設(shè)專(zhuān)項(xiàng)施工方案
- 2024年廣東省中考滿(mǎn)分作文《當(dāng)好自己故事的主角》3
- 合作商超協(xié)議合同范本
- 胃造口術(shù)后護(hù)理
- 農(nóng)莊永久出售合同范例
- 交運(yùn)股合同范例
- 制定高效的日常生產(chǎn)計(jì)劃
- 加強(qiáng)知識(shí)管理的有效方式計(jì)劃
- 品牌數(shù)字化轉(zhuǎn)型的路徑與挑戰(zhàn)計(jì)劃
- 項(xiàng)目管理的最佳實(shí)踐計(jì)劃
- HYT 0332-2022 海洋大數(shù)據(jù)標(biāo)準(zhǔn)體系(正式版)
- 全新供土協(xié)議
- 發(fā)電機(jī)組檢修方案技術(shù)指導(dǎo)
- 第2課《讓美德照亮幸福人生》第2框《做守家庭美德的好成員》-【中職專(zhuān)用】《職業(yè)道德與法治》同步課堂課件
- 條件概率與全概率公式高二下學(xué)期數(shù)學(xué)人教A版(2019)選擇性必修第三冊(cè)
- (正式版)JBT 10437-2024 電線電纜用可交聯(lián)聚乙烯絕緣料
- 法律知識(shí)圖譜構(gòu)建及應(yīng)用
- 八卦的基本介紹及其科學(xué)內(nèi)涵
- 內(nèi)科護(hù)理學(xué)慢性腎衰竭
- (建筑制圖)課程綜合自測(cè)題3(試卷和答案)
- 公司商業(yè)模式策劃案關(guān)鍵合作伙伴
評(píng)論
0/150
提交評(píng)論