Wave Function Collapse(一)


遊戲中若想創造無限延伸又變化多端的世界該怎麼做?這令人想到 Minecraft,會依據玩家的移動,即時地生成新區塊,因而世界沒有盡頭。

Minecraft 的世界生成是基於 3D 版本的〈Perlin 雜訊〉,從玩家位置計算雜訊值,生成隨機又連續的自然地形。

不過在這個世界中,若想加入較多的限制呢?例如,生成過程中若要有屋頂、牆面、階梯等,而且彼此銜接必須合理,不致於產生破碎的房屋、斷掉的階梯呢?Minecraft 顯然就有這類問題。在 Minecraft 中,看到房屋被嵌在山壁上或切了一半,不是稀奇的事情。

若想體會一下這類無限延伸的世界,是怎麼一回事,我們可以玩玩 Marian Kleineberg 寫的無限城,這座城沒有盡頭,看似變化多端,然而其實是有限的建築元素,彼此之間隨機然而又合理地銜接,在往外走的同時,新的建築又會繼續銜接生成。

Marian Kleineberg 的無限城頁面標題寫著 Wave Function Collapse,這是 2016 年遊戲設計師 Maxim Gumin 提出的演算法,Maxim Gumine 在 Github 上設置了 WaveFunctionCollapse,說明並示範了該演算法於 2D/3D 的應用,Marian Kleineberg 的無限城,是波函數塌縮(Wave Function Collapse)演算的實現之一。

Wave Function Collapse 名稱取自量子力學 Wave Function 的概念,看似神秘,不過理解演算之後,其實會發現它跟量子力學沒太大關係,純綷只是用 Wave Function Collapse 來比喻拼接元素的選擇過程罷了。

在談到量子力學 Wave Function Collapse 時,常會看到「薛丁格的貓」,這是個描述 Wave Function Collapse 概念的思想實驗:

箱子裏有隻貓,以及毒氣瓶與放射性物質,若偵測到放射性物質衰變,毒氣瓶就會被打破,機率是 50%,在還沒有打開箱子前,以機率觀點來說,貓可能活著,也可能在死亡狀態,然而從波函數的觀點來看,貓是同時處於活狀態與死狀態(既是活的也是死的),直到你打開箱子,波函數塌縮為活或死的其中一個狀態,成為你觀察到的結果。

這就是你理解 Wave Function Collapse 演算時,需要從量子力學 Wave Function Collapse 中借喻的全部了。

先來看個簡單的拼接地圖,例如:

Wave Function Collapse(一)

若藍色代表海洋,綠色代表陸地,棕色代表海岸,這個拼接地圖就像是海洋中散落的島嶼,它實際上是由以下的拼接塊隨機生成:

Wave Function Collapse(一)

為了便於識別以及後續程式實作方便,為每個拼接塊加上標示,C0 到 C3 是海岸的四個角落,CW 到 CN 是海岸的四個邊,L 與 S 分別是陸地與海洋,每個拼接塊只考量上下左右四個鄰居的相容性。

越多的拼接塊或鄰接考量,可以生成越複雜的地形,不過這邊就用這幾個簡單的拼接塊示範就可以了(畫圖好累)。

如果要使用這些拼接塊隨機生成地圖,該怎麼做呢?若某個位置隨機選了 C0,那它的四個鄰居一定不能是 L 或 S,也就是像積木一樣,必須選擇相容的拼接塊,直覺地想法是透過列舉來選取鄰居,然而除了自身之外,在選取新鄰居時,還要考量已經確定的鄰居間的相容性,這會令你難以列舉各種可能性(容易出錯),而且還要減少後續在大部份的拼接塊都已確定下,某位置卻因為彼此相容性問題,無法選擇拼接塊的可能性。

若採取 Wave Function Collapse 演算,在還沒有處理某位置前,你不會知道該位置會是哪個拼接塊,該位置同時間會有十個拼接塊,例如:

Wave Function Collapse(一)

現在隨機選擇某位置的一張拼接塊,這就像是從十個狀態塌縮成你觀察到的一個狀態:

Wave Function Collapse(一)

然後塌縮開始傳播,也就是周圍位置抽掉不相容的拼接塊:

Wave Function Collapse(一)

由於周圍位置塌縮了,更外圍的可能拼接塊也會減少,例如,左下角的拼接塊塌縮,會令其上方拼接塊減少:

Wave Function Collapse(一)

接著最初塌縮位置的上方也進行傳播:

Wave Function Collapse(一)

最初塌縮位置的右方進行傳播:

Wave Function Collapse(一)

被傳播塌縮的位置,也會繼續往外傳播塌縮,就像水波一樣,直到塌縮傳播至每個位置,就算完成了一次 Wave Function Collapse 的迭代,每個位置的拼接塊與鄰居的拼接塊,依舊都是維持著相容的可能性。

接著再度隨機選取某個位置…等一下!在首次傳播之後,其實可以觀察到,每個位置的拼接塊數量各不相同了,有些甚至是塌縮至只剩一個拼接塊了,位置的選取顯然要考量一下這個部份…

已經塌縮成剩一個拼接塊的位置,當然不在選取的考量內;基本上,若某位置的拼接塊數量少,其周圍的拼接塊數量也不會多,選擇該位置塌縮成一個拼接塊,在傳播時對各位置的拼接塊衝擊較小,也就是傳播過程,各位置的拼接塊可以保有較多的選擇性,後續選擇拼接塊,發生死胡同的可能性會比較低。

拼接塊數量是選擇位置時的一個考量,另外在生成拼接地圖時,通常也會考慮拼接塊的權重,例如,或許會想要陸地廣一些或是海岸線長一些之類的,若是如此,選擇位置的依據或許是某個權重結果。

想依拼接塊數量、某個權重計算結果,或是其他因素,甚至是計算結果再加上些隨機,據以選擇位置,基本上都是看生成的地圖風格,是否能夠符合需求。

實際上,拼接塊彼此之間的相容性,也不必自行建立,你可以用一張圖作為輸入,由程式自動剖析圖片,輸出風格相近的圖片,或者更確地根據 Maxim Gumin 的定義,輸出區域相似(Local similarity)的圖片。

根據 Maxim Gumin 的區域相似定義,若想使用了 NxN 個圖案模式來拼接出圖片,那麼輸入的圖片必須包含這 NxN 個拼接塊,輸入的圖片在分布上的資訊,能做為計算的依據,令輸出的圖片能產生類似的分佈。

那麼實際上程式該如何實作呢?這就放到下篇文件再來探討了…