Unity 自制Cubemap及使用
首先,什么是Cubemap?
Cubemap是一個由六個獨立的正方形紋理組成的集合。通常被用來作為具有反射屬性物體的反射源。類似與MAX里的HDR屬性。
如何創建自己的Cubemap
- 我們需要創建一些元素來當做Cubemaps的光源。因此,我們需要在場景中放置一些幾何平面。你可以使用一個建模軟件,例如Maya或者Max,當然你也可以使用Unity自帶的plane。哪一種方式都行,這無所謂。你的場景應該可以像這樣:
- 場景中的物體如下所示:
其中,Position是一個空對象,它將僅僅作為觀察的位置將從該點觀察到的環境信息渲染到我們的Cubemap上。
實現
- 首先,我們需要創建一個新的腳本,但是由於我們想要一個彈出的編輯器窗口,因此我們必須把腳本放到一個叫做Editor的文件夾里。在你的Project面板中創建一個叫Editor的文件夾,然后再創建一個C#腳本,叫做GenerateStaticCubemap。
- 打開上述腳本,為了使用特定的函數,我們需要使用新的using指令:
3. using UnityEngine;
4. using UnityEditor;
using System.Collections
- 為了讓Unity認識到,這個腳本會是一個彈出的編輯器窗口,我們需要讓GenerateStaticCubemap類繼承ScriptableWizard類。這使得我們可以使用一些很好的底層函數。
public class GenerateStaticCubemap : ScriptableWizard {
- 然后,我們需要添加一些新的變量來存儲新的CubeMap以及它的位置(即上面提到的position空對象)。
- 第一個函數是Unity的內置函數OnWizardUpdate()。它在向導(wizard)第一次彈出或者當GUI被用戶改變時(如拖進去某些對象,輸入某些字符等)時被調用。因此,我們可以在這里檢查用戶已經向向導中填入我們需要的所有的資源。在這里,如果Cubemap或者它的位置(一個transform)沒有被填充,那么就設置內置變量isValid為false,直到拿到所有資源。
- 當isValid變量為true時,向導將調用OnWizardCreate()函數。在這個函數里,我們將創建一個新的攝像機,然后把它放到之前設置的transform的位置上,再調用RenderToCubemap函數得到最終的Cubemap。
- 最后,我們需要從Unit編輯器的菜單欄打開這個向導。這需要MenuItem關鍵詞。
public Transform renderPosition; public Cubemap cubemap; 9. void OnWizardUpdate() { 10. helpString = "Select transform to render" + 11. " from and cubemap to render into"; 12. if (renderPosition != null && cubemap != null) { 13. isValid = true; 14. } 15. else { 16. isValid = false; 17. } } 19. void OnWizardCreate() { 20. GameObject go = new GameObject("CubeCam", typeof(Camera)); 21. 22. go.transform.position = renderPosition.position; 23. go.transform.rotation = Quaternion.identity; 24. 25. go.camera.RenderToCubemap(cubemap); 26. 27. DestroyImmediate(go); } 29. [MenuItem("CookBook/Render Cubemap")] 30. static void RenderCubemap() { 31. ScriptableWizard.DisplayWizard("Render CubeMap", typeof(GenerateStaticCubemap), "Render!"); }
最后,整體代碼如下:
using UnityEngine; using UnityEditor; using System.Collections; public class GenerateStaticCubemap : ScriptableWizard { public Transform renderPosition; public Cubemap cubemap; void OnWizardUpdate() { helpString = "Select transform to render" + " from and cubemap to render into"; if (renderPosition != null && cubemap != null) { isValid = true; } else { isValid = false; } } void OnWizardCreate() { GameObject go = new GameObject("CubeCam", typeof(Camera)); go.transform.position = renderPosition.position; go.transform.rotation = Quaternion.identity; go.camera.RenderToCubemap(cubemap); DestroyImmediate(go); } [MenuItem("CookBook/Render Cubemap")] static void RenderCubemap() { ScriptableWizard.DisplayWizard("Render CubeMap", typeof(GenerateStaticCubemap), "Render!"); } }
此時,回到Unity編輯器頁面,點擊一下Unity菜單欄(有時需要點擊才會刷新)就會看到出現一個新的選項:CookBook/Render Cubemap,如下所示:
點擊它你將會打開一個向導界面,如下所示。它需要兩個資源,一個是Render Position,這將決定觀察位置(你可以理解成你將會在該位置放一面鏡子,這里需要注意的是由於代碼里設置的攝像機的rotation為初始值,這意味着攝像機將看向圖中藍色箭頭所指方向),一個是Cubemap,可以理解成就是鏡子反射的圖像。我的例子中設置如下。
點擊Render!按鈕后,查看你的Cubemap,就會看到類似下面這樣的情景。
可以發現,我們已經把圖像渲染到了一個立方體里了。恭喜你,你已經完成了自己的一個Cubemap!你可以嘗試在不同的場景中試驗。
解釋
還記得一開始我們繼承了ScriptableWizard類,這是為了告訴Unity3D我們想要制作一個新的彈出窗口類型的用戶編輯器,這也是為什么我們需要把腳本放到Editor文件夾里的原因。如果我們不這么做,Unity將不會把它識別為一個用戶編輯器類型的腳本。
接下來我們聲明的參數是用於存儲Cubemap的渲染位置,以及把新的渲染得到的Cubemap放到Project的哪里,例如上面的名為First的Cubemap就是我提前在工程文件下創建的一個Cubemap。有了這些我們就可以生成自己的Cubemap了。
然后我們使用了OnWizardUpdate()函數,它是由ScriptableWizard類提供給我們的。同樣的,isValid變量也是一個內置變量。它讓我們可以方便的打開或者關閉向導下方的Create按鈕(這里指的是Render!按鈕)。這樣可以防止用戶使用空的transform或者Cubemap進行下面的操作。
一旦我們確定用戶提供了正確的數據,我們就可以進入到OnWizardCreate()函數了。這是Cubemap真正被創建的地方。它首先創建了一個新的GameObject構造器,並把它的類型創建為Camera。然后把它放到用戶提供的transform的位置上。
到了這里,我們剩下要做的就是把用戶提供的Cubemap傳遞給RenderToCubemap函數,生成六張圖片。
最后,我們為向導創建了一個菜單選項,以便讓用戶可以從Unity頂部的菜單欄打開這個工具。除了需要[MenuItem("CookBook/Render Cubemap")]關鍵詞以外,我們還需要將函數聲明為static函數。

至此,我們就完成了一個簡單的工具,可以用於在Unity編輯器中直接生成Cubemaps!
