胡平平, 劉建明, 王晶杰
(1. 北京信息科技大學(xué),北京 100192;2. 國網(wǎng)信息通信有限公司,北京 100761)
OpenGL是一個著名的開放式三維圖形開發(fā)函數(shù)庫,它提供的函數(shù)不僅可以創(chuàng)建基本的 3D物體原形還能夠很方便地對 3D物體進(jìn)行各種操作和變換,得到逼真的 3D顯示效果,這使它成為一個非常流行的 3D圖形瀏覽開發(fā)工具。但OpenGL卻不適于創(chuàng)建復(fù)雜的不規(guī)則 3D物體。3DS是Autodesk公司存儲3D模型數(shù)據(jù)的一種文件格式,許多流行的 3D建模軟件和圖形格式轉(zhuǎn)換工具都能夠生成 3D模型的 3DS文件,將OpenGL技術(shù)和3DS模型數(shù)據(jù)結(jié)合實(shí)現(xiàn)對3D物體的瀏覽便成為一種廣泛使用的方法。
由于3DS文件的數(shù)據(jù)格式?jīng)]有官方的正式說明文檔,盡管很多文獻(xiàn)[1-3]對用OpenGL讀取、顯示和控制3DS模型數(shù)據(jù)的方法進(jìn)行了介紹,但都側(cè)重于OpenGL編程實(shí)現(xiàn)方法,只涉及了基本的靜態(tài)3DS模型數(shù)據(jù)的使用。各種資源上能夠得到的讀取3DS數(shù)據(jù)的方法基本上一樣,都沒有對動態(tài)數(shù)據(jù)進(jìn)行處理,也沒有考慮數(shù)據(jù)轉(zhuǎn)換的性能。用這些方法顯示含動畫信息的3DS模型時會出現(xiàn)物體位置錯位的現(xiàn)象,即使是靜態(tài)數(shù)據(jù),當(dāng)模型復(fù)雜且數(shù)據(jù)量較大時,其處理速度常常慢到不能實(shí)用的地步。
本文從OpenGL顯示3D模型的數(shù)據(jù)處理過程出發(fā),結(jié)合3DS文件數(shù)據(jù)的組織結(jié)構(gòu),扼要介紹了如何由3DS文件構(gòu)造OpenGL顯示數(shù)據(jù)的方法。重點(diǎn)討論了法線向量在OpenGL顯示中的兩種應(yīng)用效果和由3DS數(shù)據(jù)生成法線向量的方法,給出了一種高效的法線向量求解算法,該算法的速度比現(xiàn)有方法快一至兩個數(shù)量級,極大地提高了數(shù)據(jù)轉(zhuǎn)換的性能。本文還介紹了3DS模型中和動畫有關(guān)的關(guān)鍵幀數(shù)據(jù)的使用方法,解決了現(xiàn)有方法在顯示帶動畫信息的3DS模型時物體位置不正確的問題。
OpenGL顯示三維物體的一般過程是:保存當(dāng)前變換矩陣、設(shè)置新的視景體和新的變換矩陣、設(shè)置每一個三維物體的數(shù)據(jù)、恢復(fù)當(dāng)前變換矩陣。設(shè)置三維物體數(shù)據(jù)的過程是由一系列嵌入在glBegin()和glEnd()調(diào)用之間的OpenGL函數(shù)調(diào)用構(gòu)成的,其過程如下所示:
對每一個三維物體:
若該物體沒有紋理或各面有相同紋理映射則glBegin();
對物體的每一個面:
若該面有紋理映射則設(shè)置紋理;
若該物體不同面有不同的紋理則glBegin();
設(shè)置以面為單位的面法線向量;
設(shè)置紋理或材質(zhì)的顏色;
對每一個面的所有頂點(diǎn):
設(shè)置以頂點(diǎn)為單位的頂點(diǎn)法線向量;
若該面有紋理則設(shè)置頂點(diǎn)的紋理坐標(biāo);
設(shè)置頂點(diǎn)坐標(biāo);
若該物體不同面有不同的紋理則glEnd();
若該物體沒有紋理或各面有相同紋理映射glEnd();
在上面的過程中,以面為單位的法線向量設(shè)置和以頂點(diǎn)為單位的法線向量設(shè)置是兩種不同的方式,下文將詳細(xì)介紹。如果物體沒有紋理映射或各面的紋理映射相同,則 glBegin()和 glEnd()以物體為單位,否則應(yīng)該以每一個面為單位,這樣不同的面才能顯示不同的紋理。
關(guān)于3DS文件的格式,文獻(xiàn)[4]和文獻(xiàn)[5]有較詳細(xì)的介紹,此處僅列出和OpenGL顯示數(shù)據(jù)有關(guān)部分的組織結(jié)構(gòu):
3DS文件數(shù)據(jù):
文件版本數(shù)據(jù)(ID=0x0002);
編輯數(shù)據(jù)(ID=0x3D3D):
. 材質(zhì)數(shù)據(jù)(ID=0xAFFF);
. . 材質(zhì)名稱數(shù)據(jù)(ID=0xAFFF);
. . 材質(zhì)顏色數(shù)據(jù)(ID=0xA010/20/30);
. . 材質(zhì)紋理數(shù)據(jù)(ID=0xA300);
. 物體數(shù)據(jù)(ID=0x4000):
. 物體名稱數(shù)據(jù);
. 物體頂點(diǎn)數(shù)據(jù)(ID=0x4110);
. 物體面數(shù)據(jù)(ID=0x4120);
. . 面材質(zhì)數(shù)據(jù)(ID=0x4130);
. . 面材質(zhì)名稱;
. . 同材質(zhì)面索引數(shù)據(jù);
. 物體轉(zhuǎn)換矩陣數(shù)據(jù)(ID=0x4160);
關(guān)鍵幀數(shù)據(jù)(ID=0xB000):
關(guān)鍵幀名稱(ID=0xB00A);
關(guān)鍵幀起止幀數(shù)據(jù)(ID=0xB008);
物體關(guān)鍵幀數(shù)據(jù)(ID=0xB002);
物體關(guān)鍵幀標(biāo)識數(shù)據(jù)(ID=0xB030);
物體關(guān)鍵幀名稱和層次數(shù)據(jù)
(ID=0xB010/11);
物體關(guān)鍵幀支點(diǎn)坐標(biāo)數(shù)據(jù)(ID=0xB013);
物體關(guān)鍵幀移動數(shù)據(jù)(ID=0xB020);
物體關(guān)鍵幀轉(zhuǎn)動數(shù)據(jù)(ID=0xB021);
物體關(guān)鍵幀縮放數(shù)據(jù)(ID=0xB022);
從上面的介紹可知,OpenGL顯示處理是以物體為單位進(jìn)行的,3DS的數(shù)據(jù)則按照類型組織,對應(yīng)于OpenGL中同一個物體的數(shù)據(jù)要從3DS文件中不同類型的數(shù)據(jù)獲得,具體方法是:
物體的頂點(diǎn)和面數(shù)據(jù):直接來自3DS的物體頂點(diǎn)數(shù)據(jù)(x,y,z坐標(biāo)值)和物體面數(shù)據(jù)(該面所用頂點(diǎn)的索引號和方向)。
物體的材質(zhì)數(shù)據(jù):由3DS物體面數(shù)據(jù)的面材質(zhì)名稱數(shù)據(jù)得到材質(zhì)名稱,再由該材質(zhì)名稱到3DS材質(zhì)數(shù)據(jù)中找到對應(yīng)的材質(zhì)顏色和紋理數(shù)據(jù)。而物體面數(shù)據(jù)的同材質(zhì)面索引數(shù)據(jù)則指出哪些面使用這個材質(zhì)。
物體的位置數(shù)據(jù):該數(shù)據(jù)由物體頂點(diǎn)數(shù)據(jù)、物體轉(zhuǎn)換矩陣數(shù)據(jù)和關(guān)鍵幀數(shù)據(jù)共同決定,前兩種數(shù)據(jù)從物體數(shù)據(jù)中直接得到,后一個則來自物體對應(yīng)的關(guān)鍵幀數(shù)據(jù)。具體生成方法,在本文后面介紹。
物體的法線向量數(shù)據(jù):由上述得到的物體頂點(diǎn)和面數(shù)據(jù)通過計(jì)算得到,具體方法見下文。
OpenGL顯示三維物體時,需要為每一個頂點(diǎn)設(shè)置法線向量,法線向量不僅為OpenGL光照處理確定每一個面的正反提供依據(jù),而且還影響到物體的顯示效果[6-7]。OpenGL法向量的設(shè)置有兩種方法:以面為單位和以頂點(diǎn)為單位,前一種方法每個面的所有頂點(diǎn)使用相同的法向量,后一種方法每個面的不同頂點(diǎn)有不同的法向量(又稱平均法向量)。圖1和圖2是同一個3D模型用兩種法向量設(shè)置方法的顯示效果圖。
從上面的效果圖可以看出,以頂點(diǎn)為單位的法向量方法顯示的物體比較平滑,這也是OpenGL顯示3DS模型時最常用的方法。
圖1 法向量以面為單位的顯示效果
圖2 法向量以頂點(diǎn)為單位的顯示效果
3DS模型并沒有直接提供OpenGL顯示用的法向量數(shù)據(jù),這些數(shù)據(jù)要從3DS模型的面數(shù)據(jù)中求解得到。3DS模型的面是由3個頂點(diǎn)和一個方向碼構(gòu)成的三角面片,讀入3DS模型的面數(shù)據(jù)后,首先要統(tǒng)一各個面的方向(必要時要根據(jù)方向碼調(diào)整3個頂點(diǎn)的順序),然后按特點(diǎn)順序由三角形的兩個邊向量的叉積求出該三角形的法向量。如果采用以面為單位的法向量方法,則構(gòu)成三角形的3個頂點(diǎn)的法向量就是該三角形的法向量;如果采用以頂點(diǎn)為單位的法向量方法,由于同一個頂點(diǎn)可能為不同的三角形共用,此時一個頂點(diǎn)的法向量就是使用該頂點(diǎn)的所有三角形法向量的平均向量?,F(xiàn)有的頂點(diǎn)法向量求解方法如下所示:
對每一個三維物體:
對物體的每一個面:
計(jì)算該面的法向量并歸一化;
對物體的每一個頂點(diǎn):
在物體所有面中尋找用到該頂點(diǎn)的面并將該面的法向量疊加;
疊加法向量平均并歸一化后作為該頂點(diǎn)的法向量;
該方法在求某一個頂點(diǎn)的法向量時,要遍歷物體所有面的3個頂點(diǎn),其運(yùn)算量是:頂點(diǎn)數(shù)量X面數(shù)量X3,由于是先求出所有面的法向量然后再使用,因此,計(jì)算過程中還要為每一個面設(shè)置一個法向量存儲空間。
3D物體模型中,一個頂點(diǎn)可能被多個面使用,但用到某一個頂點(diǎn)的面占全部面的比例往往很小。一般情況下,簡單模型的比例最大約10%,復(fù)雜大型模型的比例最大約1%,可見現(xiàn)有算法為求某一個頂點(diǎn)的法向量而遍歷所有面的處理大部分是不必要的。鑒于此,可以對頂點(diǎn)法向量的求解方法改進(jìn)為如下所示:
對每一個三維物體:
將該物體所有頂點(diǎn)的法向量設(shè)置為0;
對物體的每一個面:
計(jì)算該面的法向量;
將該面的法向量疊加到該面 3個頂點(diǎn)的法向量中;
對物體的每一個頂點(diǎn):
將頂點(diǎn)的法向量除以其疊加次數(shù)并歸一化;
上述新方法在求頂點(diǎn)的法向量時避免了對不使用該頂點(diǎn)的面的判斷處理,且新方法不用為每一個面設(shè)置一個法向量存儲空間,只需為每一個頂點(diǎn)設(shè)置一個疊加計(jì)數(shù)器變量,由于計(jì)數(shù)器變量所使用的空間只是法向量的1/6〔計(jì)數(shù)器為兩字節(jié)整數(shù),法向量為3個浮點(diǎn)數(shù),用12字節(jié)〕,而頂點(diǎn)的數(shù)量最多是面數(shù)量的3倍,故新方法使用的變量空間最多是現(xiàn)有方法的1/2。
新頂點(diǎn)法向量的求解方法在使用變量空間上至少節(jié)省一半,而計(jì)算量的節(jié)省情況則和 3D模型的具體情況有關(guān)。只有當(dāng)使用每一個頂點(diǎn)的面的數(shù)量和面的總數(shù)量相等時,現(xiàn)有算法才不存在無用的處理,此時兩種算法的處理量幾乎相等,但該情況意味著每一個頂點(diǎn)被所有的面共用,這是不可能存在的,因此新算法的處理量總是小于現(xiàn)有方法。
表1是兩種處理方法對幾種典型模型的實(shí)際處理時間對比,可以看出,模型的數(shù)據(jù)量越大,尤其是頂點(diǎn)被共用的面的數(shù)量相對于面的總數(shù)量越小,新方法的改進(jìn)效果就越明顯。對于一般模型數(shù)據(jù),新方法的速度平均是現(xiàn)有方法的幾倍至幾十倍,而對大型模型,速度提高的效果非常明顯,約為幾百倍。
表1 兩種法向量求解方法的計(jì)算時間比較
不帶動畫信息的3DS模型中各物體的頂點(diǎn)數(shù)據(jù)可以直接作為這些物體的實(shí)際位置坐標(biāo)數(shù)據(jù),但當(dāng)3DS模型帶動畫信息時,模型中各物體的實(shí)際位置一般由頂點(diǎn)數(shù)據(jù)和關(guān)鍵幀數(shù)據(jù)共同決定,如果僅按頂點(diǎn)數(shù)據(jù)顯示物體,則模型中的物體可能會顯示在錯誤的位置上。
3DS模型中每一個物體的關(guān)鍵幀信息由物體的名稱或關(guān)鍵幀的層次結(jié)構(gòu)決定。關(guān)鍵幀的層次關(guān)系是一個樹狀結(jié)構(gòu),當(dāng)某一個物體是根節(jié)點(diǎn)或根節(jié)點(diǎn)的葉節(jié)點(diǎn)時,其位置信息僅由它自己的關(guān)鍵幀數(shù)據(jù)決定;否則該物體的位置信息要由它自己的關(guān)鍵幀數(shù)據(jù)和它所有父節(jié)點(diǎn)的關(guān)鍵幀數(shù)據(jù)共同決定。
3DS模型中各種關(guān)鍵幀數(shù)據(jù)塊的結(jié)構(gòu)在文獻(xiàn)[4]和文獻(xiàn)[5]中有一定的介紹,但這些數(shù)據(jù)的具體含義和使用方法都沒有介紹。下面僅對與物體位置有關(guān)的關(guān)鍵幀數(shù)據(jù)含義和使用方法做一個簡單介紹,至于如何利用關(guān)鍵幀數(shù)據(jù)在OpenGL環(huán)境中顯示可以運(yùn)動的3DS模型,由于篇幅所限,此處不做介紹。
3DS模型中每一個關(guān)鍵幀數(shù)據(jù)都有自己的名稱,該名稱在名稱層次塊(0xB010)中,如果其中的名稱不是“DUMMY”,則該關(guān)鍵幀數(shù)據(jù)就屬于名稱與之匹配的物體,否則,該關(guān)鍵幀數(shù)據(jù)屬于一個虛物體,該物體是一個父節(jié)點(diǎn),它的實(shí)際名稱在后面的虛名稱塊(0xB011)中,而該父節(jié)點(diǎn)的關(guān)鍵幀數(shù)據(jù)為其所有子節(jié)點(diǎn)共用,它所含子節(jié)點(diǎn)的關(guān)鍵幀數(shù)據(jù)位于其后,由子節(jié)點(diǎn)關(guān)鍵幀數(shù)據(jù)的層次號決定。
在關(guān)鍵幀數(shù)據(jù)中,每一個物體的關(guān)鍵幀數(shù)據(jù)(0xB002)都有一個位于名稱層次塊(0xB010)中的層次號,層次號是由該關(guān)鍵幀的層次關(guān)系和它在關(guān)鍵幀數(shù)據(jù)中出現(xiàn)的位置決定的,其編排方法是:根關(guān)鍵幀的層次號為-1,其它關(guān)鍵幀的層次號由0開始遞增或保持不變。層次號保持不變,說明這些關(guān)鍵幀屬于同一個層次;層次號增加則表示該關(guān)鍵幀是上一個節(jié)點(diǎn)的子節(jié)點(diǎn);層次號減小,則說明該關(guān)鍵幀是一個新的父節(jié)點(diǎn),該節(jié)點(diǎn)的層次同前面與之層次號相同的節(jié)點(diǎn)一樣,每一個父節(jié)點(diǎn)之后新子節(jié)點(diǎn)關(guān)鍵幀的層次號就是該關(guān)鍵幀數(shù)據(jù)出現(xiàn)的實(shí)際位置的順序號(新子節(jié)點(diǎn)關(guān)鍵幀的層次號一定是一個沒有出現(xiàn)過的值)。以后各關(guān)鍵幀的層次號依上述規(guī)律編排,直到所有的關(guān)鍵幀數(shù)據(jù)編排完畢。
3DS模型中物體的位置由以下數(shù)據(jù)決定:物體的頂點(diǎn)坐標(biāo)數(shù)據(jù)(0x4110)、轉(zhuǎn)換矩陣數(shù)據(jù)(0x4160)中的源點(diǎn)坐標(biāo)和物體關(guān)鍵幀數(shù)據(jù)中的支點(diǎn)坐標(biāo)數(shù)據(jù)(0xB013)、移動數(shù)據(jù)(0xB020)、旋轉(zhuǎn)數(shù)據(jù)(0xB021)及縮放數(shù)據(jù)(ID=0xB022)。
3DS模型中不帶動畫信息的物體也有一幀關(guān)鍵幀數(shù)據(jù),只不過其關(guān)鍵幀數(shù)據(jù)的支點(diǎn)坐標(biāo)和旋轉(zhuǎn)關(guān)鍵幀數(shù)據(jù)都是0,縮放數(shù)據(jù)是1。帶動畫信息的物體則按它具有關(guān)鍵幀的多少帶有指定數(shù)量的關(guān)鍵幀數(shù)據(jù)。物體轉(zhuǎn)換矩陣數(shù)據(jù)中的源點(diǎn)坐標(biāo)表示該物體當(dāng)前位置和它被創(chuàng)建時原始位置的位移,該數(shù)據(jù)和物體關(guān)鍵幀數(shù)據(jù)第0幀的移動數(shù)據(jù)相同。物體的頂點(diǎn)坐標(biāo)實(shí)際上就是由物體被創(chuàng)建時的初始位置和大小按照其第0幀關(guān)鍵幀數(shù)據(jù)做變換(平移、旋轉(zhuǎn)和縮放)生成的。而物體關(guān)鍵幀數(shù)據(jù)中的支點(diǎn)坐標(biāo)則是頂點(diǎn)坐標(biāo)表示的位置到當(dāng)前位置的平移量,因此,為了顯示物體正確的當(dāng)前位置,應(yīng)該將物體的頂點(diǎn)坐標(biāo)進(jìn)行調(diào)整,方法是:將物體各頂點(diǎn)坐標(biāo)減去其關(guān)鍵幀數(shù)據(jù)的支點(diǎn)坐標(biāo)。
本文給出的由3DS模型數(shù)據(jù)求解頂點(diǎn)法向量的新方法和關(guān)鍵幀數(shù)據(jù)的使用均成功地應(yīng)用到作者開發(fā)的多屏同步 3D顯示系統(tǒng)中,不僅實(shí)現(xiàn)了大型3DS模型的快速裝入和帶動畫信息的3DS模型物體位置的正確顯示,還實(shí)現(xiàn)了帶動畫信息的3DS模型物體的運(yùn)動顯示,效果良好。
[1]趙文廣, 李仲學(xué), 李翠平. 面向工程可視化仿真的VC++, OpenGL與3DS集成技術(shù)[J]. 北京科技大學(xué)學(xué)報, 2001, 23(6): 563-565.
[2]殷素峰, 高雪強(qiáng), 楊勝強(qiáng). 在 OpenGL環(huán)境下開發(fā)3DS文件瀏覽器[J]. 工程圖學(xué)學(xué)報, 2005, 26(6):22-25.
[3]尹士偉, 張光年, 郭新宇. 一種控制3DS模型的新方法的研究與實(shí)現(xiàn)[J]. 微計(jì)算機(jī)信息, 2007, 23(3-2):307-308.
[4]Martin van Velsen.3D-Studio File Format(.3ds) [EB/OL].http://www.the-labs.com/Blender/3dsspec.html,1998.10.5.
[5]Jeff Lewis.The Unofficial 3DStudio 3DS File Format[EB/OL].http://www.the-labs.com/Blender/3DS-details.html,1998.10.5.
[6]喬 林, 費(fèi)廣正, 林 杜, 等. OpenGL程序設(shè)計(jì)[M].北京: 清華大學(xué)出版社, 2000. 194-202.
[7][美]Dave Shrieiner, Mason Woo等. OpenGL編程指南(第 4版)[M]. 鄧鄭祥譯.北京:人民郵電出版社,2005. 44-45.