資源來(lái)自 http://219.131.221.85/bbs/viewthread.php?tid=384&extra=page%3D1
VRML創(chuàng)作工具很多是“所見(jiàn)即所得”式的,通過(guò)圖形界面可以方便地創(chuàng)作虛擬境界,但VRML不僅僅是普通的三維設(shè)計(jì),盡管這些工具很容易上手,卻往往屏蔽掉了VRML標(biāo)準(zhǔn)的具體細(xì)節(jié),因?yàn)槿绻肷钊胝莆誚RML,還需要全面了解節(jié)點(diǎn)、域、檢測(cè)器等技術(shù)細(xì)節(jié),而達(dá)成此目的的最好方法就是用編寫(xiě)文本文件的方式創(chuàng)作VRML境界。本教程提供了六個(gè)典型例子,這些例子并不復(fù)雜,也不精彩,但涵蓋了VRML的關(guān)鍵內(nèi)容。
在開(kāi)始創(chuàng)作之前,應(yīng)作好下面的準(zhǔn)備。
文本編輯器 隨便你喜歡的文本編輯器,如Win95下的NotePad,Dos下的Edit等等。
VRML瀏覽器 若用的Web瀏覽器是Netscape4.0一下版本,可下載CosmoPlayer(http://cosmo.sgi.com);若用的是Netscape4.0或更高版本,則已內(nèi)置CosmoPlayer2.0,只是安裝Netscape時(shí)請(qǐng)注意是否選中了相應(yīng)選項(xiàng);若用的是Internet Explore4.0,則有可能已經(jīng)內(nèi)置了VRML2.0瀏覽器,判斷是否內(nèi)置的方法很簡(jiǎn)單,就是看它能否打開(kāi)VRML文件(*.wrl,*.wrz),如果不行,可以從http://www.microsoft.com/vrml/下載VRML瀏覽器插件,對(duì)于IE3.x,還需要下載一些輔助插件。當(dāng)然在開(kāi)始之前應(yīng)基本熟悉VRML瀏覽器的操作方法。
硬件 VRML和硬件平臺(tái)無(wú)關(guān),只要能提供VRML瀏覽器。在下面的教程中,我們假定硬件平臺(tái)是微機(jī),輸出設(shè)備是圖形窗口,輸入設(shè)備為鼠標(biāo)器和鍵盤(pán)。當(dāng)然,如果有更先進(jìn)的虛擬現(xiàn)實(shí)設(shè)備和支持它的VRML瀏覽軟件效果會(huì)更好。對(duì)于我們將要?jiǎng)?chuàng)作的境界,微機(jī)就足夠了。
資料 本站就是最全面的資料,遇到新概念時(shí)可查閱本站相關(guān)資料。
第一節(jié) "Hello,World!"
按照慣例,我們以"Hello,World!"作為我們的第一個(gè)虛擬境界,它由立方體、圓錐和球體組成,你可能已經(jīng)注意到,VRML的標(biāo)志正是由這三個(gè)幾何形狀構(gòu)成的。輸入的第一行文字是:
#VRML V2.0 utf8
這是VRML文件的標(biāo)志,所有2.0版本的VRML文件都以這行文字打頭,VRML97是由VRML2.0版修訂而成的,符合VRML97規(guī)范的VRML文件也以這行文字打頭。其中“#”表示這是一個(gè)注釋。而utf8表示此文件采用的是utf8編碼方案,這在標(biāo)準(zhǔn)中有詳細(xì)說(shuō)明。
先加入一個(gè)Group節(jié)點(diǎn)(組節(jié)點(diǎn)):
Group {
組節(jié)點(diǎn)的花括號(hào)之內(nèi)的所有內(nèi)容視為一個(gè)整體,利用組節(jié)點(diǎn)可以把虛擬場(chǎng)景組織成條理清晰的樹(shù)形分支結(jié)構(gòu)。下面定義組節(jié)點(diǎn)的children域(孩子域):
children [
在children后的方括號(hào)內(nèi)定義Group節(jié)點(diǎn)的所有孩子對(duì)象,第一個(gè)孩子是一個(gè)Shape節(jié)點(diǎn)(形態(tài)節(jié)點(diǎn)),它描述一個(gè)幾何形狀及其顏色等特征:
Shape {
在Shape 節(jié)點(diǎn)內(nèi)定義一個(gè)幾何體Box(方盒節(jié)點(diǎn)):
geometry Box {}
注意我們沒(méi)有為Box定義任何域,這意味著它的尺寸和坐標(biāo)位置等特性取缺省值(單位立方體)。隨后補(bǔ)齊各右括號(hào):
}
]
}
至此,我們已經(jīng)成功地制作了第一個(gè)虛擬境界,把它保存為Hello World.wrl,下面是完整的文件:
#VRML V2.0 utf8
Group {
children [
Shape {
geometry Box {}
}
]
}
用瀏覽器打開(kāi)這個(gè)文件,你會(huì)看到一個(gè)灰色的立方體,盡管不太好看,但你還是可以通過(guò)改變視點(diǎn)位置從不同方位觀察它,初步體驗(yàn)“三維交互”的感覺(jué)。
下面定義立方體的外觀,這只需改變Shape節(jié)點(diǎn)的appearance域(外觀),appearance 域是一個(gè)Appearance 節(jié)點(diǎn),此Appearance節(jié)點(diǎn)的material域(材質(zhì))定義為一個(gè)Material 節(jié)點(diǎn):
appearance Appearance {
material Material {}
}
這樣,上面的Shape節(jié)點(diǎn)變成了:
Shape {
appearance Appearance {
material Material {}
}
geometry Box {}
}
這是定義幾何造型的基本格式。現(xiàn)在立方體還是灰色的,這是因?yàn)槠渲械腗aterial節(jié)點(diǎn)采用的還是缺省值,下面修改它的diffuseColor域(漫射色),VRML的顏色說(shuō)明采用的是RGB顏色模型,所以要定義紅色的立方體,漫射色應(yīng)該是{1 0 0},三個(gè)數(shù)字依次表示紅色、綠色和藍(lán)色,取值范圍都是0到1:
material Material {diffuseColor 1 0 0 }
現(xiàn)在我們生成了第二個(gè)場(chǎng)景,完整的代碼是:
#VRML V2.0 utf8
Group {
children [
Shape {
appearance Appearance {
material Material { diffuseColor 1 0 0 }
}
geometry Box {}
}
]
}
在這個(gè)場(chǎng)景中,紅色的立方體位于屏幕的中心,它的中心坐標(biāo)為{0 0 0 }。若想把它移動(dòng)一個(gè)位置,可以通過(guò)為它外套一個(gè)Transform(變換節(jié)點(diǎn))來(lái)實(shí)現(xiàn):
Transform {
translation 5 0 0
children [
Shape {
appearance Appearance {
material Material {}
}
geometry Box {}
}
]
}
在VRML中,Transform節(jié)點(diǎn)除了可以引進(jìn)平移、旋轉(zhuǎn)和縮放變換以外,其作用和Group節(jié)點(diǎn)的作用一樣。把Transform 節(jié)點(diǎn)的translation域(平移)設(shè)置為5 0 0,意味著Transform節(jié)點(diǎn)所在的坐標(biāo)系相對(duì)于其上層坐標(biāo)系向右平移(即x軸方向)5個(gè)單位,在其它兩個(gè)方向不移動(dòng),VRML的距離單位是米,5個(gè)單位相當(dāng)于5米。我們第三個(gè)場(chǎng)景的完整代碼是:
#VRML V2.0 utf8
Group {
children [
Transform {
translation 5 0 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 1 0 0 }
}
geometry Box {}
}
]
}
]
}
接下來(lái)我們把方塊所在的Transform節(jié)點(diǎn)復(fù)制三份,并把各自包含的幾何形狀依次定義為方塊、球體和圓錐:
Group {
children [
Transform {
translation 5 0 0
children [
Shape { .... geometry Box {} }
]
}
Transform {
translation 0 0 0
children [
Shape { ... geometry Sphere {} }
]
}
Transform {
translation -5 0 0
children [
Shape { ... geometry Cone {} }
]
}
]#end of Group children
}
你可能已經(jīng)感覺(jué)到,VRML文件中有許多括號(hào)(花括號(hào)“{}”和方括號(hào)“[]”),所以務(wù)請(qǐng)注意括號(hào)的配對(duì),建議采用本教程的縮進(jìn)風(fēng)格。注意上面的VRML文件中三個(gè)Transform節(jié)點(diǎn)的平移量是不同的,因而三個(gè)幾何體的位置也就不同。另外,還可以修改三個(gè)幾何體的顏色:球面Sphere為綠色(0 1 0),圓錐為藍(lán)色( 0 1 0 )。最后,為了以后引用方便,分別給這三個(gè)Transform 節(jié)點(diǎn)指定一個(gè)名稱(chēng):
DEF box Transform {...}
DEF sphere Transform {...}
DEF cone Transform {...}
#VRML V2.0 utf8
Group {
children [
DEF box Transform {
translation 5 0 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 1 0 0 }
}
geometry Box {}
}
]
}
DEF sphere Transform {
translation 0 0 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 0 1 0 }
}
geometry Sphere {}
}
]
}
DEF cone Transform {
translation -5 0 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 0 0 1 }
}
geometry Cone { }
}
]
}
]# end of Group children
}
把此文件保存為helloworld.wrl,用VRML瀏覽器打開(kāi)這個(gè)文件,通過(guò)調(diào)整視點(diǎn)從多個(gè)方位瀏覽自己的作品。
小結(jié):在這一節(jié),我們創(chuàng)建了第一個(gè)虛擬境界,涉及到如何用幾何體構(gòu)建境界,以及如何設(shè)定幾何體的顏色與材質(zhì)。盡管這個(gè)由方塊、圓錐和球體組成的場(chǎng)景圖比較簡(jiǎn)單,但已經(jīng)反映了VRML的基本功能。當(dāng)然,除了可用鼠標(biāo)改變視點(diǎn)外,這還只是一個(gè)靜態(tài)世界,在下一節(jié),我們將引進(jìn)VRML的動(dòng)態(tài)特征。
第二節(jié) 增加交互能力
上一節(jié)我們學(xué)習(xí)了用幾何體建立虛擬境界以及為幾何體賦予色彩和材質(zhì)的方法,這樣建立的虛擬境界是靜態(tài)的。這一節(jié)我們將使一個(gè)幾何體(為了更具一般性,下面我們稱(chēng)之為對(duì)象)能夠根據(jù)用戶動(dòng)作做出反應(yīng),即交互能力,這是VRML2.0最突出的特征。
1。檢測(cè)器
在VRML中,檢測(cè)器(Sensor)節(jié)點(diǎn)是交互能力的基礎(chǔ)。檢測(cè)器節(jié)點(diǎn)共九種。在場(chǎng)景圖中,檢測(cè)器節(jié)點(diǎn)一般是以其它節(jié)點(diǎn)的子節(jié)點(diǎn)的身份而存在的,它的父節(jié)點(diǎn)稱(chēng)為可觸發(fā)節(jié)點(diǎn),觸發(fā)條件和時(shí)機(jī)由檢測(cè)器節(jié)點(diǎn)類(lèi)型確定。
接觸檢測(cè)器( TouchSensor)是最常用的檢測(cè)器之一,最典型的應(yīng)用例子是開(kāi)關(guān)。其它檢測(cè)器將在后續(xù)教程中陸續(xù)介紹。這里我們定義一個(gè)開(kāi)關(guān)節(jié)點(diǎn)lightSwitch(這是一個(gè)組節(jié)點(diǎn)),并定義一個(gè)接觸檢測(cè)器作為它的子節(jié)點(diǎn):
DEF lightSwitch Group {
children [
各幾何造型子節(jié)點(diǎn)...
DEF touchSensor TouchSensor {}
]
}
這樣開(kāi)關(guān)節(jié)點(diǎn)lightSwitch就是一個(gè)可觸發(fā)節(jié)點(diǎn)。當(dāng)然,檢測(cè)器存在的理由是它被觸發(fā)時(shí)能夠引起某種變化,所以在更深入討論開(kāi)關(guān)節(jié)點(diǎn)之前,我們先討論一下場(chǎng)景變化。 2.視點(diǎn)
最常見(jiàn)的變化是視點(diǎn)的變化,在我們的第一個(gè)境界中你可能已經(jīng)體驗(yàn)到視點(diǎn)變化:當(dāng)你拖動(dòng)鼠標(biāo)或按動(dòng)箭頭鍵時(shí)(按照VRML術(shù)語(yǔ),稱(chēng)為航行),虛擬境界就會(huì)旋轉(zhuǎn)或縮放,這實(shí)際上是在調(diào)整你的視點(diǎn)位置或視角。在虛擬場(chǎng)景的重要位置可以定義視點(diǎn)節(jié)點(diǎn)(ViewPoint),它們是境界作者給用戶推薦的上佳觀賞方位,在CosmoPlayer瀏覽器中,用戶就可以通過(guò)鼠標(biāo)右鍵選擇作者推薦的各個(gè)視點(diǎn)。這里我們定義兩個(gè)視點(diǎn)節(jié)點(diǎn):
DEF view1 Viewpoint {
position 0 0 20
description "View1"
}
DEF view2 Viewpoint {
position 5 0 20
description "view2"
}
我們的潛在目的是使用戶可以通過(guò)觸發(fā)開(kāi)關(guān)節(jié)點(diǎn)來(lái)切換視點(diǎn)。現(xiàn)在先研究一下這兩個(gè)視點(diǎn)節(jié)點(diǎn),其中的坐標(biāo)表示視點(diǎn)在場(chǎng)景中的位置,坐標(biāo)的單位是米,這在前面已經(jīng)提到過(guò),視點(diǎn)的名稱(chēng)將會(huì)在瀏覽器菜單中提示出來(lái)供用戶選擇。把上述視點(diǎn)說(shuō)明加入helloworld.wrl中(放在Group節(jié)點(diǎn)之前),并把其中的方塊節(jié)點(diǎn)修改成可觸發(fā)節(jié)點(diǎn):
DEF box Tranform {
children [
Shape { .... Box ...}
DEF touchBox TouchSensor {}
]
}
把修改過(guò)的文件另存為“touchme.wrl”。
3。事件傳遞
下面我們把觸發(fā)(用鼠標(biāo)箭頭按動(dòng)方塊)和場(chǎng)景變化(視點(diǎn)切換)這兩件事情聯(lián)系起來(lái),在場(chǎng)景圖中,除節(jié)點(diǎn)構(gòu)成的層次體系外,還有一個(gè)“事件體系”,事件體系由相互通訊的節(jié)點(diǎn)組成。能夠接收事件的節(jié)點(diǎn)都應(yīng)具有事件入口(eventIn),如果它要接收多種類(lèi)型的事件(稱(chēng)為入事件),它就應(yīng)該具有多個(gè)事件入口,也就是說(shuō),事件入口象節(jié)點(diǎn)的域一樣是有類(lèi)型的。同樣,發(fā)送事件的節(jié)點(diǎn)應(yīng)有事件出口(eventOut),事件出口也是有類(lèi)型的。例如ViewPoint節(jié)點(diǎn)就有一個(gè)事件入口set_bind,當(dāng)向此事件送入一個(gè)值“TRUE”(即所謂的入事件)時(shí),該viewpoint節(jié)點(diǎn)成為當(dāng)前視點(diǎn)。又如,接觸檢測(cè)器TouchSensor有一個(gè)事件出口isActive,當(dāng)受到用戶觸發(fā)后它就從此出口送出一個(gè)“TRUE”(即所謂的出事件),補(bǔ)充一句,在下一個(gè)事件發(fā)送之前,此事件一直保存在事件出口中(作為記錄)。
事件出口和事件入口通過(guò)路徑相連,這就是VRML文件中除節(jié)點(diǎn)以外的另一基本組成部分:ROUTE 語(yǔ)句。ROUTE語(yǔ)句把事件出口和事件入口聯(lián)系在一起,從而構(gòu)成事件體系。在這里,我們是把接觸檢測(cè)器touchBox的事件出口isActive連接到視點(diǎn)節(jié)點(diǎn)view2的事件入口set_bind:
ROTUE touchBox.isActive TO view2.set_bind
好了!現(xiàn)在我們得到的VRML文件是:
#VRML V2.0 utf8
DEF view1 Viewpoint {
position 0 0 20
description "view1"
}
DEF view2 Viewpoint {
position 5 0 20
description "view2"
}
Group {
children [
DEF box Transform {
translation 5 0 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 1 0 0}
}
geometry Box {}
}
DEF touchBox TouchSensor {}
]
}
DEF sphere Transform {
translation 0 0 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 0 1 0}
}
geometry Sphere {}
}
]
}
DEF cone Transform {
translation -5 0 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 0 0 1 }
}
geometry Cone {}
}
]
}
] #end of Group children
}
ROUTE touchBox.isActive TO view2.set_bind
把這個(gè)文件調(diào)入瀏覽器,然后把鼠標(biāo)指向方塊并按下左鈕(先別松開(kāi)!),可以看到視點(diǎn)已經(jīng)變?yōu)関iew2,內(nèi)部的機(jī)制我們已經(jīng)很清楚:左鈕按下時(shí)方塊節(jié)點(diǎn)的接觸檢測(cè)器被觸發(fā),接著接觸檢測(cè)器從事件出口isActive送出一個(gè)事件“TRUE”,這個(gè)事件通過(guò)路由進(jìn)入視點(diǎn)節(jié)點(diǎn)view2的事件入口set_bind,view2收到“TRUE”后成為當(dāng)前視點(diǎn),所以在我們眼前場(chǎng)景發(fā)生了變化。
現(xiàn)在松開(kāi)左鈕,可以看到場(chǎng)景恢復(fù)到原來(lái)方位,這種功能稱(chēng)為視點(diǎn)回跳,其原因是松開(kāi)左鈕后接觸檢測(cè)器向view2發(fā)送了一個(gè)“FASLE”事件,這樣view2當(dāng)前的地位被解除,原來(lái)的視點(diǎn)成為系統(tǒng)視點(diǎn)棧的棧頂節(jié)點(diǎn)(即當(dāng)前視點(diǎn)),詳細(xì)說(shuō)明可參見(jiàn)標(biāo)準(zhǔn)中對(duì)視點(diǎn)節(jié)點(diǎn)的專(zhuān)門(mén)論述。如果我們不想視點(diǎn)回跳,就想停留在view2視點(diǎn),那該怎么辦呢?這種非系統(tǒng)缺省功能要自己來(lái)定義。
4。 利用腳本編寫(xiě)自定義行為 在VRML中,利用Script節(jié)點(diǎn)(腳本節(jié)點(diǎn))定義用戶自定義行為,所謂定義即用腳本描述語(yǔ)言(Scripting Language)編寫(xiě)腳本的過(guò)程。VRML97支持的腳本描述語(yǔ)言目前有兩種:Java和EMCAScript(這是JavaScript標(biāo)準(zhǔn)化后的名稱(chēng)),關(guān)于這兩種語(yǔ)言本身,請(qǐng)參考相應(yīng)參考書(shū),VRML97標(biāo)準(zhǔn)中定義了它們和VRML的接口方法。應(yīng)提請(qǐng)注意的是:VRML是基于節(jié)點(diǎn)的語(yǔ)言,所以腳本也是封裝在Script這個(gè)特殊節(jié)點(diǎn)中的。這里我們不過(guò)多討論腳本描述語(yǔ)言的細(xì)節(jié),主要討論把腳本集成到VRML文件中的方法。
上面我們?cè)呀佑|檢測(cè)器touchBox 和視點(diǎn)view2直接通過(guò)路徑連接起來(lái),現(xiàn)在要定義我們指定的行為,就需要在二者之間插入一個(gè)腳本節(jié)點(diǎn),也就是讓路徑繞個(gè)彎: ROUTE touchBox.isActive TO touchScript.touchBoxIsActive
ROUTE touchScript.bindView2 TO view2.set_bind
其中的腳本節(jié)點(diǎn)touchScript有一個(gè)事件人口touchBoxIsActive和一個(gè)事件出口bind_View2,前者接收來(lái)自接觸檢測(cè)器touchBox的事件,然后經(jīng)自己的腳本處理后,把結(jié)果發(fā)送給視點(diǎn)節(jié)點(diǎn)view2:
DEF touchScript Script {
eventIn SFBool touchBoxIsActive
eventOut SFBool bindView2
url"javescript:
function touchBoxIsActive(active) {
bindView2= TRUE;
}"
}
關(guān)于這個(gè)Script節(jié)點(diǎn),請(qǐng)注意一下幾點(diǎn):(1)它的事件入口touchBoxIsActive和事件出口bindView2是自定義的,其它VRML節(jié)點(diǎn)的域和事件都是固定的。(2)事件入口touchBoxIsActive(即入事件)和事件出口bindView2(即出事件)的類(lèi)型都是SFBool(單值布爾型),touchBox的事件出口isActive和view2的事件入口set_bind的類(lèi)型也是相同的。(3)“url”是腳本節(jié)點(diǎn)的一個(gè)域,可以直接包含腳本,也可以包含一個(gè)或多個(gè)用URL地址指示的腳本,若有多個(gè)地址,則按照先后次序獲取第一個(gè)可得到的腳本。(4)腳本是以函數(shù)(function)的形式給出的,函數(shù)名touchBoxIsActive 與事件入口的名稱(chēng)相同,這是和ECMAScript語(yǔ)言的接口約定,表示相應(yīng)事件入口收到事件后調(diào)用此函數(shù)進(jìn)行處理。
5.事件流程與小結(jié)
下面我們整理一下事件流程:
(1)用戶在方塊上按下鼠標(biāo)左鍵。
(2)接觸檢測(cè)器發(fā)出一個(gè)“TRUE”事件。
(3)此事件進(jìn)入腳本節(jié)點(diǎn)touchScript的事件入口touchBoxIsActive.
(4)調(diào)用腳本函數(shù)touchBoxIsActive(注意函數(shù)并沒(méi)有判斷入事件的值)。
(5)函數(shù)向touchScript的事件出口bindView2發(fā)送一個(gè)“TRUE”事件(還可以進(jìn)行其它判斷或執(zhí)行其它事件)。
(6)view2節(jié)點(diǎn)收到“TRUE”事件,成為當(dāng)前視點(diǎn)。按照VRML約定,“認(rèn)為”上述事件是同時(shí)發(fā)生的,也就是這些事件的時(shí)間戳相同。
(7)若用戶松開(kāi)鼠標(biāo)左鍵,則接觸檢測(cè)器發(fā)出一個(gè)“FALSE”事件,此事件同樣引起腳本函數(shù)調(diào)用并發(fā)送“TRUE”事件,所以view2仍然保持為當(dāng)前視點(diǎn)。
本節(jié)的完整代碼是:
#VRML V2.0 utf8
DEF view1 Viewpoint {
position 0 0 20
description "view1"
}
DEF view2 Viewpoint {
position 5 0 20
description "view2"
}
Group {
children [
DEF box Transform {
translation 5 0 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 1 0 0 }
}
geometry Box {}
}
DEF touchBox TouchSensor {}
]
}
DEF sphere Transform {
translation 0 0 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 0 1 0}
}
geometry Sphere {}
}
]
}
DEF cone Tranform {
transltion -5 0 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 0 0 1 }
}
geometry Cone {}
}
]
}
] #end of Group children
}
DEF touchScript Script {
eventIn SFBool touchBoxIsActive
eventOut SFBool bindView2
url "javascript :
function touchBoxIsActive (active) {
bindView2 = TRUE;
}"
}
ROUTE touchBox.isActive TO touchScript.touchBoxIsActive
ROUTE touchScript.bindView2 TO view2.set_bind
小結(jié):本節(jié)建立的虛擬境界并不復(fù)雜,但涉及到了VRML2.0最基礎(chǔ)性的功能和概念:利用檢測(cè)器產(chǎn)生事件、利用路由傳遞事件以及利用腳本編寫(xiě)自定義行為,掌握了這些內(nèi)容也就掌握了VRML2.0的核心。在后面的幾節(jié)中,我們將探索一些專(zhuān)題性的有趣功能,而本節(jié)是基礎(chǔ),因而必須透徹理解。
第三節(jié) 鄰近檢測(cè)器
本節(jié)討論鄰近檢測(cè)器(proximitySensor),當(dāng)用戶進(jìn)入或離開(kāi)鄰近檢測(cè)器所劃定的區(qū)域時(shí)就會(huì)觸發(fā)它。正如你在標(biāo)準(zhǔn)中可以查到的那樣,ProximitySensor節(jié)點(diǎn)定義為:
ProximitySensor {
exposedField SFVec3f center 0 0 0
exposedField SFVec3f size 0 0 0
exposedField SFBool enabled TRUE
eventOut SFBool isActive
eventOut SFVec3f position_changed
eventOut SFRotation orientation_changed
eventOut SFTime enterTime
eventOut SFTime exitTime
}
這里稍作介紹。ProximitySensor節(jié)點(diǎn)共有三個(gè)外露域(exposedField)和五個(gè)出事件(eventOut).出事件我們已經(jīng)熟悉,是節(jié)點(diǎn)狀態(tài)發(fā)生改變時(shí)用來(lái)通知其它節(jié)點(diǎn)的,這里的出事件isActive 用于ProximitySensor通報(bào)自己已被激活。enterTime和exitTime通報(bào)用戶(代表用戶的用戶化身或指示器)進(jìn)入和退出ProximitySensor檢測(cè)區(qū)的時(shí)刻。若用戶已在檢測(cè)器之內(nèi),則當(dāng)用戶的位置或方位發(fā)生變化時(shí),送出position_changed和orientation_changed出事件這五個(gè)出事件聯(lián)合起來(lái),就定義了鄰近檢測(cè)器的功能。外露域則集域(Field)、入事件(eventIn)和出事件(eventOut)三者的功能于一身,也就是說(shuō),它既象域一樣描述了節(jié)點(diǎn)的當(dāng)前狀態(tài),又可以作為入事件由其它節(jié)點(diǎn)修改這種狀態(tài),并作為出事件把這種改變通知其它節(jié)點(diǎn)。這里的enabled外露域是布爾型的,用于ProximitySensor的啟用和停用,center和size定義形為長(zhǎng)方體的鄰近檢測(cè)區(qū)。
我們的出發(fā)點(diǎn)是第一節(jié)中建造的境界helloworld,它是由方塊、球體和圓柱這三個(gè)物體構(gòu)成的靜態(tài)世界,現(xiàn)在在球體周?chē)黾右粋€(gè)鄰近檢測(cè)區(qū):
DEF sphere Transform {
translation 0 0 0
children [
Shape {....}
DEF comeClose ProximitySensor {
center 0 0 0
size 4 4 4
}
]
}
ProximitySensor的名字為comeCloser,鄰近區(qū)的中心和球體的球心重合,形狀為正方體,邊長(zhǎng)為4米,是球體直徑的兩倍。當(dāng)用戶走進(jìn)球體時(shí)就會(huì)觸發(fā)這個(gè)鄰近檢測(cè)器,檢測(cè)器發(fā)出isActive事件,我們把這個(gè)事件出口通過(guò)路由指向Script節(jié)點(diǎn)(用來(lái)綁定視點(diǎn)2):
DEF comeCloserScript Script {
eventIn SFBool enterProximitySensorIsActive
eventOut SFBool bindView2
url " javascript :
function enterProximitySensorIsActive (active) {
bindView2=TRUE;
} "
}
隨后,我們?cè)卩徑鼨z測(cè)器的出事件isActive和腳本節(jié)點(diǎn)comeCloserScript的入事件enterProximitySensorIsActive之間建立路由,后者收到事件后執(zhí)行函數(shù)enterProximitySensroIsActive,函數(shù)發(fā)出bindView2出事件,這個(gè)出事件通過(guò)路由連接到視點(diǎn)節(jié)點(diǎn)View2:
ROUTE comeCloser.isActive TO comeCloserScript.enterProximitySensorIsActive
ROUTE comeCloserScript.bindView2 TO view2.set_bind
也就是說(shuō),一旦用戶進(jìn)入鄰近區(qū),境界的當(dāng)前視點(diǎn)將轉(zhuǎn)換成View2.這個(gè)由兩個(gè)視點(diǎn)、三個(gè)物體、一個(gè)鄰近檢測(cè)器和一個(gè)腳本節(jié)點(diǎn)組成的境界的完整代碼如下:
#VRML V2.0 utf8
DEF view1 Viewpoint {
position 0 0 20
description "view1"
}
DEF view2 Viewpoint {
position 0 0 20
description "view2"
}
Group {
children [
DEF box Transform {
translation 5 0 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 1 0 0 }
}
geometry Box {}
}
]
}
DEF sphere Transform {
translation 0 0 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 0 1 0 }
}
geometry Sphere {}
}
DEF comeCloser ProximitrySensor {
center 0 0 0
size 4 4 4
}
]
}
DEF cone Transform {
translation -5 0 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 0 0 1}
}
geometry Cone {}
}
]
}
]#end of Group children
}
DEF comeCloserScript Script {
eventIn SFBool enterProximitySensorIsActive
eventOut SFBool bindView2
url "javascript :
function enterProximitySensorIsActive(active) {
bindView2=TRUE;
}"
}
ROUTE comeCloser.isActive TO comeCloserScript.enterProximitySensorIsActive
ROUTE comeCloserScript.bindView2 TO view2.set_bind
啟動(dòng)VRML瀏覽器進(jìn)入境界,面向球體一直走過(guò)去,當(dāng)你剛剛感到靠近球體時(shí),會(huì)突然感到自己后退了一大步(或者說(shuō)物體跳到前方更遠(yuǎn)的地方),這表明鄰近檢測(cè)器已經(jīng)檢測(cè)到你的靠近,它把這件事通知腳本節(jié)點(diǎn),腳本節(jié)點(diǎn)把視點(diǎn)View2綁定成當(dāng)前視點(diǎn),從而使你感到視點(diǎn)突然改變。
再稍稍修改一下鄰近檢測(cè)器,把它的中心位置向右移了2米:
DEF comeCloser ProximitySensor {
center 2 0 0
size 4 4 4
}
這樣你就可以從左邊(方塊那一邊)走進(jìn)球體(視點(diǎn)不跳),但不能從右邊(圓錐那一邊)走近它(視點(diǎn)跳轉(zhuǎn))。
總之,ProximitySensor能夠檢測(cè)用戶是否進(jìn)入或離開(kāi)檢測(cè)器指定的空間區(qū)域,典型用法是當(dāng)用戶走進(jìn)房間時(shí)開(kāi)啟燈光,當(dāng)用戶離開(kāi)時(shí)關(guān)閉燈光,從而建立功能豐富的“智能”空間。
第四節(jié) 連續(xù)動(dòng)畫(huà)
在第二節(jié)中我們已經(jīng)使用過(guò)接觸檢測(cè)器,當(dāng)我們把鼠標(biāo)指針?lè)诺椒綁K(這個(gè)幾何節(jié)點(diǎn)包含接觸檢測(cè)器)上面時(shí),指針形狀發(fā)生變化,這意味著我們已經(jīng)進(jìn)入檢測(cè)區(qū),如果按下鼠標(biāo)左鈕,則按照我們的定義,當(dāng)前視點(diǎn)會(huì)發(fā)生變化。
這一節(jié)仍然制作這樣一個(gè)對(duì)接觸有反應(yīng)的方塊,只是接觸后它會(huì)連續(xù)不斷地轉(zhuǎn)動(dòng),動(dòng)畫(huà)行為可以用時(shí)間檢測(cè)器(TimeSensor)驅(qū)動(dòng),而不斷變化的旋轉(zhuǎn)值可用腳本節(jié)點(diǎn)或朝向插補(bǔ)器(orientationInterpolator)給出。
1。接觸檢測(cè)器
作為開(kāi)始的基本代碼是:
#VRML V2.0 utf8
DEF cube Transform {
rotation 1 1 1 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 1 0 0 }
}
geometry Box {}
}
DEF TouchS TouchSensor {}
]
}
DEF revolver Script {
eventIn SFBool startRevolving
eventOut SFRotation revolve
field SFFloat angle 0
url "javascript :
function startRevolving () {
revolve[0]=1;
revolve[1]=1;
revolve[2]=1;
revolve[3]=angle;
angle+=0.1;
}"
}
ROUTE TouchS.isOver TO revolver.startRevolving
ROUTE revolver.revolve TO cube.set_rotation
其中,方塊cube包含兩個(gè)子節(jié)點(diǎn),前者定義了它的形態(tài)(紅色的單位立方體),后者把它定義成接觸檢測(cè)器。注意,cube的類(lèi)型是Transform節(jié)點(diǎn),它的rotation 域是外露域,指定本組相對(duì)于上層坐標(biāo)系的旋轉(zhuǎn)值,這里指定的初始值是“1 1 1 0 ”,其中前三個(gè)數(shù)值定義旋轉(zhuǎn)軸,最后一個(gè)值定義旋轉(zhuǎn)角。由于它是外露域,因而可以通過(guò)入事件(名為set_rotation)進(jìn)行修改,下面定義的動(dòng)態(tài)行為就是這樣實(shí)現(xiàn)的。
Script節(jié)點(diǎn)revolver的核心是內(nèi)聯(lián)的ECMAScript腳本函數(shù)。它給定一個(gè)不斷變化的旋轉(zhuǎn)值。當(dāng)鼠標(biāo)指針移動(dòng)到方塊之上時(shí),接觸檢測(cè)器發(fā)出isOver,和第一節(jié)中采用的isActive事件不同,isOver只有在鼠標(biāo)左鈕按下時(shí)才會(huì)發(fā)出。isOver事件通過(guò)路由傳遞給腳本節(jié)點(diǎn)的事件入口startRevolving,從而啟動(dòng)函數(shù)startRevolving,函數(shù)將一個(gè)新的旋轉(zhuǎn)值發(fā)往事件出口revolve,這個(gè)旋轉(zhuǎn)值通過(guò)路由進(jìn)入cube的外露域rotation,修改了方塊的旋轉(zhuǎn)角,引起它的朝向變化。鼠標(biāo)指針在cube上面的每次方位變化都引起isOver事件發(fā)送一次,從而導(dǎo)致方塊旋轉(zhuǎn)一次。
2。時(shí)間檢測(cè)器
為了使方塊能夠連續(xù)旋轉(zhuǎn),需要引進(jìn)等間隔連續(xù)發(fā)送的時(shí)間序列,這正是時(shí)間檢測(cè)器的用武之地。時(shí)間檢測(cè)器隨著時(shí)間推移不斷產(chǎn)生事件,可用于多種目的,包括: a. 驅(qū)動(dòng)連續(xù)性的仿真和動(dòng)畫(huà)
b. 控制周期性的活動(dòng)(如每分鐘一次)
c. 初始化單獨(dú)事件,如報(bào)警鐘
下面是我們要用的時(shí)間檢測(cè)器和修改后的路由關(guān)系:
DEF ticker TimeSensor {
cleInterval 0.1
loop TRUE
enabled FALSE
}
ROUTE TouchS.isOver TO ticker.set_enabled
ROUTE ticker.cycleTime TO revolver.startRevolving
ROUTE revolver.revolve TO cube.set_rotation
enabled用于啟用和停用時(shí)間檢測(cè)器,開(kāi)始時(shí)它處于停用狀態(tài),以后由接觸檢測(cè)器的isOver事件修改這一狀態(tài)。啟用的時(shí)間檢測(cè)器每隔0.1秒送出一個(gè)cycleTime事件,并用它來(lái)觸發(fā)revolver的startRevolving事件,注意,cycleTime事件的類(lèi)型為SFTime,而路由兩端事件的類(lèi)型必須匹配,所以盡管這里我們不關(guān)心這個(gè)事件表示的具體時(shí)刻,還是把revolver的startRevolving事件類(lèi)型也改為SFTime.這樣,revolver的函數(shù)startRevolving()就會(huì)每0.1秒調(diào)用一次,從而驅(qū)動(dòng)方塊連續(xù)旋轉(zhuǎn)。完整的代碼是:
#VRML V2.0 utf8
DEF cube Transform {
rotation 1 1 1 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 1 0 0 }
}
geoemtry Box {}
}
DEF TouchS TouchSensor {}
]
}
DEF revolver Script {
eventIn SFTime startRevolving
eventOut SFRotation revolve
field SFFloat angle 0
url "vrmlscript :
function startRevolving () {
revolve[0]=1;
revolve[1]=1;
revolve[2]=1;
revolve[3]=angle;
angle+=0.1;
}"
}
DEF ticker TimeSensor {
cycleInterval 0.1
loop TRUE
enabled FALSE
}
ROUTE TouchS.isOver TO ticker.set_enabled
ROUTE ticker.cycleTime TO revolver.startRevolving
ROUTE revolver.revolve TO cube.set_rotation
上述腳本節(jié)點(diǎn)的功能比較簡(jiǎn)單,只是不斷送出調(diào)整的旋轉(zhuǎn)值,它是關(guān)鍵幀動(dòng)畫(huà)的一種。由于關(guān)鍵幀動(dòng)畫(huà)十分常用,故VRML專(zhuān)門(mén)定義了插補(bǔ)器節(jié)點(diǎn)來(lái)實(shí)現(xiàn)它。
3。 朝向插補(bǔ)器
插補(bǔ)器節(jié)點(diǎn)可認(rèn)為是VRML內(nèi)置的腳本節(jié)點(diǎn),它們執(zhí)行簡(jiǎn)單的動(dòng)態(tài)計(jì)算,通常和時(shí)間檢測(cè)器或者能夠使對(duì)象產(chǎn)生動(dòng)作的節(jié)點(diǎn)結(jié)合在一起使用,生成線性關(guān)鍵幀動(dòng)畫(huà)。插補(bǔ)器節(jié)點(diǎn)實(shí)際上是一個(gè)由關(guān)鍵點(diǎn)和對(duì)應(yīng)關(guān)鍵值定義的分段線形函數(shù)。根據(jù)插值類(lèi)型的不同,VRML共定義六個(gè)插補(bǔ)器節(jié)點(diǎn):ColorInterpolator(顏色插補(bǔ)器)、CoordinateInterpolator(坐標(biāo)插補(bǔ)器)、NormalInterpolator(法線插補(bǔ)器)、OrientationInterpolator(朝向插補(bǔ)器)、positionInterpolator(位置插補(bǔ)器)、ScalarInterpolator(標(biāo)量插補(bǔ)器)。
所有插補(bǔ)器的域和事件都是類(lèi)似的:
eventIn SFFloat set_fruction
exposedField MFFloat key [...]
exposedField MF keyValue [.....]
eventOut [S|M]F value_changed
關(guān)鍵值域keyValue的類(lèi)型決定了插補(bǔ)器的類(lèi)型(例如,OrientationInterpolator的keyValue域的類(lèi)型是MFFloat).入事件set_fraction接收SFFloat型的事件,插補(bǔ)器隨即根據(jù)它進(jìn)行插值,并通過(guò)出事件value_changed送出插值結(jié)果。
這里我們把時(shí)間檢測(cè)器的fraction_changed事件作為插補(bǔ)器的輸入,這個(gè)事件是一個(gè)[0,1]區(qū)間的值,每個(gè)時(shí)間步都送出一次,表示當(dāng)前周期內(nèi)已過(guò)去的時(shí)間相對(duì)于整個(gè)周期的比例,是插補(bǔ)器常用的輸入源之一。與此對(duì)應(yīng),我們把插補(bǔ)器關(guān)鍵幀的取值也定義在[0,1]范圍內(nèi)。與0和1這兩個(gè)關(guān)鍵幀對(duì)應(yīng)的關(guān)鍵值的旋轉(zhuǎn)軸是相同的,只是旋轉(zhuǎn)角度不同(0,3.14159),這樣方位插補(bǔ)器輸出的旋轉(zhuǎn)值的旋轉(zhuǎn)軸固定不變,旋轉(zhuǎn)角從0遞增到3.14159,然后不斷重復(fù)。
#VRML V2.0 utf8
DEF cube Transform {
rotation 1 1 1 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 1 0 0 }
}
geometry Box {}
}
DEF TouchS TouchSensor {}
]
}
DEF revolver OrientationInterpolator {
key [0,1]
keyValue [ 0.5 0.5 0.5 0,0.5 0.5 0.5 3.14149]
}
DEF ticker TimeSensor {
cycleInterval 2
loop TRUE
enabled FALSE
}
ROUTE TouchS.isOver TO ticker.set_enabled
ROUTE ticker.fraction_changed TO revolver.set_fraction
ROUTE revolver.value_changed TO cube.set_rotation
小結(jié):本節(jié)實(shí)現(xiàn)連續(xù)動(dòng)畫(huà),動(dòng)畫(huà)由接觸檢測(cè)器啟動(dòng),由時(shí)間檢測(cè)器驅(qū)動(dòng),動(dòng)畫(huà)本身比較簡(jiǎn)單,就是不斷地旋轉(zhuǎn)。產(chǎn)生不斷變化的旋轉(zhuǎn)值的方法有兩種:自己編寫(xiě)腳本,或者利用插補(bǔ)器節(jié)點(diǎn)。
第五節(jié) 動(dòng)態(tài)修改場(chǎng)景圖
場(chǎng)景圖是描述境界結(jié)構(gòu)的基本概念,節(jié)點(diǎn)是構(gòu)成場(chǎng)景圖的基本單元。組節(jié)點(diǎn)是能夠包含字節(jié)點(diǎn)的節(jié)點(diǎn),組節(jié)點(diǎn)本身還可作為其它組節(jié)點(diǎn)的子節(jié)點(diǎn),從而形成層次性體系結(jié)構(gòu)。VRML中的組節(jié)點(diǎn)包含Anchor(錨)、 Billboard(布告牌)、 Collision(碰撞)、Group (組)、Inline (內(nèi)聯(lián))、LOD(細(xì)節(jié)層次)、 Switch(開(kāi)關(guān))、Transform(變換)共八種,除Inline、LOD、Switch這幾個(gè)具有特殊功能外,它們都定義了入事件addChildren 和removeChildren ,前者用于向組節(jié)點(diǎn)的子節(jié)點(diǎn)域children 中增加新的子節(jié)點(diǎn),后者用于從中刪除子節(jié)點(diǎn),這樣就可以動(dòng)態(tài)修改場(chǎng)景圖的結(jié)構(gòu)。
下面是我們這一節(jié)要建立的境界,開(kāi)始的時(shí)候球體位于左邊紅色方塊的內(nèi)部,在按動(dòng)底部的綠色方塊后,球體進(jìn)入右邊藍(lán)色方塊之內(nèi)。
首先定義三個(gè)方塊:
#VRML V2.0 utf8
Viewpoint { position 0 0 15 }
DEF leftBox Transform {
translation -5 0 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 1 0 0 }
}
geometry Box {}
}
]
}
DEF rightBox Transform {
translation 5 0 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 0 0 1 }
}
geometry Box {}
}
]
}
DEF onoff Transform {
translation 0 -5 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 0 1 0 }
}
geometry Box {}
}
]
}
其中左邊的方塊為紅色,右邊的方塊為藍(lán)色,下邊的方塊為綠色,都是Transform類(lèi)型,三者都位于場(chǎng)景圖的最高層,都是場(chǎng)景圖的根節(jié)點(diǎn),都包含一個(gè)Box幾何體作為子節(jié)點(diǎn)。下面為紅方塊增加一個(gè)球體子節(jié)點(diǎn):
DEF leftBox Transform {
translation -5 0 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 1 0 0 }
}
geometry Box {}
}
DEF SphereChild Shape {
appearance Appearance {
material Material { diffuseColor 1 0 1 }
}
geometry Sphere { radius 1.2 }
}
]
}
為了以后引用方便,這里還為球體子節(jié)點(diǎn)起了名字:SphereChild .為了讓用戶能夠增刪這個(gè)兒子,把綠方塊定義成接觸檢測(cè)器:
DEF onoff Transform {
translation 0 -5 0
children [
Shape {
appearance Appearance {
material Material {diffuseColor 0 1 0 }
}
geometry Box {}
}
DEF TS TouchSensor {}
]
}
子節(jié)點(diǎn)增刪的具體任務(wù)由Script節(jié)點(diǎn)來(lái)完成:
DEF S Script {
eventIn SFBool isActive
eventOut MFNode child
field MFNode testNode USE SphereChild
url"javascript :
function isActive (value) {
if (value)child = testNode;
}"
}
注意它的出事件child的類(lèi)型是MFNode,也就是說(shuō)通過(guò)這個(gè)事件送出的是節(jié)點(diǎn)。節(jié)點(diǎn)S的testNode域是對(duì)球體SphereChild引用(USE語(yǔ)句),引用不復(fù)制該節(jié)點(diǎn),而是把同一節(jié)點(diǎn)再次插入場(chǎng)景圖,從而導(dǎo)致SphereChild擁有多個(gè)父親,所以場(chǎng)景圖僅僅是層次結(jié)構(gòu),而不是樹(shù)形結(jié)構(gòu)。加上下面的路由語(yǔ)句,建立事件聯(lián)系:
ROUTE TS.isActive TO S.isActive
ROUTE S.child TO leftBox.removeChildren
ROUTE S.child TO rightBox.addChildren
接觸檢測(cè)器TS的激活事件isActive連接到腳本節(jié)點(diǎn)S的isActive,這樣用戶一旦按動(dòng)綠方塊,就會(huì)啟動(dòng)腳本節(jié)點(diǎn)的事件處理函數(shù)isActive(),此函數(shù)把testNode節(jié)點(diǎn)(即球體節(jié)點(diǎn)SphereChild )送至出事件S.child.根據(jù)路由,左邊紅方塊的事件入口leftBox.removeChildren 收到此事件,按照removeChildren的語(yǔ)義,球體節(jié)點(diǎn)SphereChild從leftBox的子節(jié)點(diǎn)列表中刪除。與此同時(shí),右邊藍(lán)方塊的事件入口rightBox.addChildren也收到S.child出事件,根據(jù)addChildren的語(yǔ)義,球體節(jié)點(diǎn)SphereChild加入 rightBox的子節(jié)點(diǎn)列表。通過(guò)這個(gè)過(guò)程,球體節(jié)點(diǎn)SphereChild的父節(jié)點(diǎn)從leftBox更換成rightBox.
第六節(jié) 擴(kuò)充節(jié)點(diǎn)類(lèi)型
VRML提供了54種節(jié)點(diǎn)類(lèi)型,稱(chēng)為內(nèi)部節(jié)點(diǎn)類(lèi)型。然而實(shí)際應(yīng)用種可能要求新的節(jié)點(diǎn)類(lèi)型,原型(prototype)是VRML實(shí)現(xiàn)節(jié)點(diǎn)類(lèi)型擴(kuò)充的基本機(jī)制。新節(jié)點(diǎn)類(lèi)型是根據(jù)已定義的(內(nèi)部的或原型的)節(jié)點(diǎn)類(lèi)型定義的,一旦定義,原型節(jié)點(diǎn)類(lèi)型就可以象內(nèi)部節(jié)點(diǎn)類(lèi)型一樣在場(chǎng)景圖中實(shí)例化。原型可以在當(dāng)前文件中定義并使用,也可以在其它文件中定義,即外部原型,外部原型提供了一種使節(jié)點(diǎn)類(lèi)型能夠跨越網(wǎng)絡(luò)的機(jī)制。本節(jié)的原型例子取自VRML97標(biāo)準(zhǔn),它定義的是一個(gè)桌子類(lèi)型,這個(gè)原型為:
#VRML V2.0 utf8
PROTO TwoColor Table [ field SFColor legColor 0.8 0.4 0.7
field SFColor topColor 0.6 0.6 0.1 ]
}
Transform {
children [
Transform {
translation 0.0 0.6 0.0
children [
Transform {
appearance Appearance {
material Material { diffuseColor IS topColor }
}
geometry Box { size 1.2 0.2 1.2 }
}
}
Transform {
translation -0.5 0 -0.5
children [
DEF Leg Shape {
appearance Appearance { diffuseColor IS legColor }
}
geometry Cylinder { height 1 radius 0.1 }
}
]
}
Transform { #另一條桌腿
translation 0.5 0 -0.5
children USE Leg
}
Transform { #另一條桌腿
translation -0.5 0 0.5
children USE Leg
}
Transform { #另一條桌腿
translation 0.5 0 0.5
children USE Leg
}
]#根節(jié)點(diǎn)Transform的兒子結(jié)束
}#根Transform 結(jié)束
}#原型結(jié)束
原型語(yǔ)句PROTO分為原型接口聲明和原型定義兩部分、接口聲明包括原型的入事件和出事件的類(lèi)型和名稱(chēng),以及原型的域的類(lèi)型、名稱(chēng)和缺省值。這里的接口聲明為:
PROTO TwoColorTable [ field SFColor legColor 0.8 0.4 0.7
field SFColor topColor 0.6 0.6 0.1 ]
這個(gè)原型的類(lèi)型名稱(chēng)為“TwoColorTable"(雙色桌),它有兩個(gè)域:legColor(桌腿色)和topColor(桌面色)。作為節(jié)點(diǎn)類(lèi)型,TwoColorTable的用法和其它內(nèi)部節(jié)點(diǎn)類(lèi)型一樣,例如下面的語(yǔ)句定義一個(gè)TwoColorTable類(lèi)型的節(jié)點(diǎn),它的桌腿為紅色,桌面為綠色:
TwoColor Table {
legColor 1 0 0 topColor 0 1 0
}
接口聲明之后是原型的主體,稱(chēng)為原型定義。原型定義實(shí)際上是一個(gè)場(chǎng)景圖,由一個(gè)或多個(gè)根節(jié)點(diǎn)、嵌入的PROTO語(yǔ)句和ROUTE語(yǔ)句構(gòu)成,其中的第一個(gè)節(jié)點(diǎn)類(lèi)型確定原型實(shí)例在VRML文件中的使用方法。例如,如果原型定義中的第一個(gè)節(jié)點(diǎn)是Material節(jié)點(diǎn),則只要可以使用Material節(jié)點(diǎn)的地方,原型實(shí)例都可以使用。原型定義中定義的其它節(jié)點(diǎn)及其附帶的場(chǎng)景圖都不進(jìn)入原型實(shí)例所在的變換層系,但可以被原型定義中的ROUTE語(yǔ)句或Script節(jié)點(diǎn)引用。TwoColorTable原型中的第一個(gè)節(jié)點(diǎn)是Transform組節(jié)點(diǎn),它決定了TwoColorTable型節(jié)點(diǎn)在場(chǎng)景圖中的方法,在場(chǎng)景圖中添加一個(gè)TwoColorTable型節(jié)點(diǎn),相當(dāng)于增加Transform.
原型定義中節(jié)點(diǎn)的域、入事件、出事件可以通過(guò)IS語(yǔ)句和接口聲明中的域、入事件、出事件建立關(guān)聯(lián),關(guān)聯(lián)實(shí)際上相當(dāng)于把原型定義中的這些域和事件公開(kāi)作為原型的域和事件。關(guān)聯(lián)的基本規(guī)則是域和域、入事件和入事件、出事件和出事件對(duì)應(yīng)關(guān)聯(lián),原型定義中的外露域可以和接口聲明中的域、入事件、出事件或外露域關(guān)聯(lián)。本例中的關(guān)聯(lián)有兩個(gè):桌面diffuseColor 域和接口聲明中的topColor域,桌腿的diffuseColor域和接口聲明中的legColor域之間。也就是說(shuō),TwoColorTable型節(jié)點(diǎn)中的topColor和legColor值實(shí)際上分別確定了桌面和桌腿的漫反射色diffuseColor.
第七節(jié) 結(jié)束語(yǔ)
本教程創(chuàng)建了六個(gè)典型VRML境界,介紹了VRML的主要功能。這些例子的側(cè)重點(diǎn)在于VRML的交互式特征,而不是營(yíng)造境界的造型特征,后者可參見(jiàn)一般的三維圖形工具,把這二者結(jié)合起來(lái),可以創(chuàng)建更加精彩的交互式3D境界。本章根據(jù)需要介紹了部分VRML節(jié)點(diǎn)的基本用法,以后將對(duì)VRML節(jié)點(diǎn)進(jìn)行分類(lèi)和較為全面的評(píng)論。
當(dāng)然,VRML功能十分豐富,要成為VRML專(zhuān)家,一方面需要研讀VRML97標(biāo)準(zhǔn),以求全面深入的掌握和應(yīng)用,另一方面,要經(jīng)常研讀成功的作品,獲取創(chuàng)作靈感。