Houdini學(xué)習(xí)筆記021_VEX函數(shù)和循環(huán)語句
今天講解的內(nèi)容分為兩個部分:(一)自定義函數(shù);(二)for循環(huán)語句。
案例是石墨烯的六邊形蜂窩狀平面,因為我本職工作是科學(xué)可視化,所以在案例選擇上會更偏向于自然和科學(xué)。在筆記005中我已經(jīng)用Copy節(jié)點制作過一遍,今天我們嘗試用VEX來解決。


(一)自定義函數(shù)
什么是函數(shù)?
數(shù)學(xué)中的函數(shù)是指一個量隨著另一個量的變化而變化,前者叫因變量,后者叫自變量。計算機(jī)中的函數(shù)與之類似,有一個輸入端和一個輸出端,從輸入端輸入初始值,輸出端就會返回對應(yīng)的結(jié)果。因此,函數(shù)可以看做是可實現(xiàn)特定功能的封裝模塊,比如sin(π/6) = 0.5,就是求正弦值的函數(shù)。而函數(shù)的英文單詞function本身也有功能的含義。
Houdini的VEX中提供了大量的函數(shù),在幫助文檔中搜索VEX Functions就可以看到全部的函數(shù)用法。一般寫法為函數(shù)名(變量1; 變量2...)。

比如在這里,我們先要創(chuàng)建一個正六邊形,用到的是創(chuàng)建點和面的函數(shù)。新建一個Point Wrangle節(jié)點(/obj/geo節(jié)點內(nèi)創(chuàng)建,別把最開始學(xué)的基礎(chǔ)忘了),在VEXpression欄輸入“addpoint(”就會彈出如下提示(我試了下,一般是在輸入左半邊括號后彈出)。這是一個添加點的節(jié)點,我們用的是int addpoint(int geohandle, vector pos)的寫法。geohandle是輸入端口號,pos就是坐標(biāo)。

三維空間中的坐標(biāo)顯然是一個矢量型數(shù)據(jù),用VEX書寫時可以用花括號括起來,如{0,0,0}。例如,在VEXpression中輸入如下所示的語句,就可以在視圖中顯示新增的點。注意這里Run Over方式要選擇Detail(only once),因為當(dāng)前只有一個Point Wrangle節(jié)點,我們是將其作為一個獨立的對象來看待的。

學(xué)會了單個點的創(chuàng)建,那么創(chuàng)建六邊形的六個頂點自然不在話下。假設(shè)我們要創(chuàng)建的六邊形邊長為1,其六個點的坐標(biāo)如下所示——

我們從點(1,0,0)開始逆時針依次創(chuàng)建點,注意當(dāng)坐標(biāo)表示中有其他函數(shù)(如sqrt)時,則不能用花括號的寫法,而應(yīng)該用set函數(shù)來設(shè)置矢量。

寫完之后,視圖窗口中就會看到六個點——

接下來是面的創(chuàng)建,使用的是addprim函數(shù)。在其介紹中我們可以看到pt0、pt1、pt2…的字樣,數(shù)據(jù)類型為int(整數(shù)型)。其實這些就是點的序號,按照一定順序排列就能圍成一個面。

在Geometry Spreadsheet窗口查看,可以看到使用addpoint函數(shù)創(chuàng)建點的時候,點的編號自動就有了(0~5)。

所以這里我們可以定義一個數(shù)組points[],其類型為int。數(shù)組中就是0, 1, 2, 3, 4, 5六個編號。然后addprim函數(shù)就可以直接按照這個數(shù)組內(nèi)的編號順序來生成面,寫法如下("poly"是生成的primitive類型,這里為多邊形面)——

至此,一個六邊形面就得到了。

如果要創(chuàng)建蜂窩狀排列的石墨烯,一個六邊形還遠(yuǎn)遠(yuǎn)不夠,得批量生產(chǎn)。為了避免每次生產(chǎn)都要寫這么一大串代碼,我們可以把它封裝起來。比如現(xiàn)在我只給定一個坐標(biāo)點位置,就能夠以其為中心生成一個六邊形。假設(shè)這個給定點坐標(biāo)為pos,那么在計算六邊形點坐標(biāo)時加上pos就可以了。
下面是封裝函數(shù)的寫法,函數(shù)的主體部分用花括號{ }括起來。前面的void hexagon(vector pos)就是定義函數(shù),void是VEX中定義函數(shù)的方式,意思是空返回值。在幫助文檔中查看已有的函數(shù)會發(fā)現(xiàn)前面都有個默認(rèn)的void,只是省略了書寫而已。hexagon是自定義的函數(shù)名稱,括號內(nèi)是函數(shù)的變量,記住要聲明變量的數(shù)據(jù)類型(這里位置坐標(biāo)pos是矢量,所以是vector類型)。

比如現(xiàn)在我要在{1,0,0}位置生成一個邊長為1的六邊形,只需要將pos設(shè)為{1,0,0},然后直接調(diào)用hexagon函數(shù)即可。

以上都是準(zhǔn)備工作,下面我們進(jìn)入第二部分。

(二)for循環(huán)語句
根據(jù)蜂窩的排列方式,我們可以將其拆解為兩個方向上的六邊形重復(fù)排列。如下圖所示,橫向上呈現(xiàn)上下交錯的排列方式,縱向則是直接平移堆疊的方式。

假設(shè)0號六邊形的中心點坐標(biāo)為{0,0,0},只需要計算出其他六邊形的中心點坐標(biāo),然后執(zhí)行hexagon函數(shù)即可。我們先單獨看縱向,每次向上移動的距離為sqrt(3),初始的vector pos = {0,0,0},每次都移動一定的距離可以用for循環(huán)語句來實現(xiàn)。寫法為:
for(int i = 0; i < total_number; i++)
{
}
如果你學(xué)過C語言,對這些應(yīng)該都很熟悉。i就是循環(huán)的次數(shù)編號,類型為整數(shù),從0開始。i < total_number是循環(huán)發(fā)生的條件,這里的意思是達(dá)到total_number次循環(huán)就終止。最后的i++意思是每次循環(huán)后,i的值加1。{ }內(nèi)部就是每次循環(huán)執(zhí)行的內(nèi)容,這里需要每次讓pos坐標(biāo)值沿z軸正向移動sqrt(3)的距離,即pos += set(0,0,sqrt(3));再執(zhí)行hexagon(pos)函數(shù)。

但是執(zhí)行后我們發(fā)現(xiàn)只有初始位置出現(xiàn)了六邊形面,其他位置只出現(xiàn)了頂點。發(fā)生了什么呢?
其實在寫代碼的過程中出現(xiàn)這種現(xiàn)象是很正常的。因為我們不可能一開始就把所有的注意事項都考慮到,通常是在操作過程中不斷去完善。如果你現(xiàn)在查看節(jié)點信息可以看到,其實是有5個Primitives生成的,只不過現(xiàn)在都是在原點位置處生成的。

原因是hexagon函數(shù)中,我們定義的points數(shù)組就是0~5的編號,所以每次創(chuàng)建六邊形時用的都是0~5號點。而隨著循環(huán)的不斷進(jìn)行,新生成的點的序號也是在不斷增加的。所以需要把數(shù)組中的編號也要重新設(shè)置下。這里我采用的辦法是直接讓addpoint函數(shù)返回點的編號值,因為幫助文檔中有這么一句:

Return就是返回的意思,返回的內(nèi)容是所創(chuàng)建點的點序號,如果無法創(chuàng)建點則返回-1。于是我在每個addpoint函數(shù)前加了個int型變量來接受這個返回值,從pt0到pt6,如下圖所示。同時,points[]數(shù)組的寫法也要做出些變化,同樣是參考的幫助文檔。


現(xiàn)在執(zhí)行循環(huán)就有沒問題了~

縱向的解決了,我們再來看看橫向的,情況要稍微復(fù)雜些,因為有上下交錯的變化。這里可分為兩種情況,當(dāng)循環(huán)次數(shù)為奇數(shù)時,z方向坐標(biāo)會移動sqrt(3)/2,;當(dāng)循環(huán)次數(shù)為偶數(shù)時,z方向坐標(biāo)又移動-sqrt(3)/2。此外,x方向每次移動的距離都是1.5。
我的寫法是,在sqrt(3)/2前面乘上一個系數(shù),pow(-1,i+1)意思是-1的i+1次冪。

將上面兩個for循環(huán)結(jié)合起來,先沿著縱向每次生成第一個六邊形,然后分別沿橫向生成交錯的六邊形。具體代碼如下,我就不作詳細(xì)解釋了。

運行結(jié)果如下——

最后,你可以把循環(huán)條件中的限定次數(shù)設(shè)為可調(diào)節(jié)的參數(shù)(方法參照上兩篇VEX筆記),通過滑塊就可以調(diào)節(jié)生成的石墨烯大小。

今天的學(xué)習(xí)就到這里吧,感謝各位的閱讀,下回見~
Tips:書寫代碼時為了便于閱讀,同一層級的代碼最好使用相同的縮進(jìn)值。VEXpression中按【Tab】鍵是縮進(jìn),【Shift+Tab】是縮退。養(yǎng)成良好的書寫習(xí)慣,修正代碼會更加高效。