之前美術mm想做一個唯美天空盒,將某相機在球形模型中的視野渲染到cubemap上,然后賦給天空盒.她想再次編輯cubemap中某一面的貼圖,無奈無法獲得這些貼圖.機會來了!!
自己寫了個工具類,可以渲染,可以保存某一張圖片,也可以一次性全部導出這些貼圖,問題解決.
/// <summary> /// 將某相機所看渲染到CubeMap,並選擇性的保存此CubeMap貼圖 /// </summary> using UnityEngine; using UnityEditor; /// <summary> /// CubeMap 六個面 /// </summary> enum CubeMapFaces { right = 0, left, top, bottom, front, back } public class RenderToCubeMapTool : EditorWindow { [MenuItem ("Window/CubeMapTool %m")] static void AddWindow () { //創建窗口 Rect wr = new Rect (0, 0, 100, 100); RenderToCubeMapTool window = (RenderToCubeMapTool)EditorWindow.GetWindowWithRect (typeof(RenderToCubeMapTool), wr, false, "CubeMapTool"); window.Show (); } private Camera renderCamera; private Cubemap renderCubeMap; CubeMapFaces faces; private CubemapFace offcialFaces; void OnGUI () { GUILayout.BeginVertical (); GUILayout.Space (10f); renderCamera = EditorGUILayout.ObjectField ("渲染相機", renderCamera, typeof(Camera), true) as Camera; GUILayout.Space (10f); renderCubeMap = EditorGUILayout.ObjectField ("CubeMap", renderCubeMap, typeof(Cubemap), false) as Cubemap; GUILayout.Space (10f); if (GUILayout.Button ("渲染") && CheckCamAndCubeMap ()) { if (renderCamera.RenderToCubemap (renderCubeMap)) { Debug.LogWarningFormat ("渲染完成!"); CheckSingleCubeMapFaces (); } else { Debug.LogWarningFormat ("渲染失敗!"); } } GUILayout.Space (10f); GUILayout.BeginHorizontal (); faces = (CubeMapFaces)EditorGUILayout.EnumPopup ("保存CubeMap某個面", faces); if (GUILayout.Button ("保存") && CheckCamAndCubeMap ()) { CheckSingleCubeMapFaces (); SaveSingelTexture (); } GUILayout.EndHorizontal (); GUILayout.Space (10f); if (GUILayout.Button ("全部保存")) { SaveMultiTexture (); } GUILayout.Space (10f); EditorGUILayout.HelpBox ("請務必確認要渲染的CubeMap可以讀寫!\n (屬性面板中的 Readable 勾上即可)", MessageType.Warning); GUILayout.EndVertical (); } bool CheckCamAndCubeMap () { if (renderCamera == null || renderCubeMap == null) { Debug.LogError ("渲染相機和CubeMap不能為空!"); return false; } else { return true; } } #region 檢測單張保存時對應cubemap面 /// <summary> /// Checks the cube map faces. /// </summary> void CheckSingleCubeMapFaces () { switch (faces) { case CubeMapFaces.right: offcialFaces = CubemapFace.PositiveX; break; case CubeMapFaces.left: offcialFaces = CubemapFace.NegativeX; break; case CubeMapFaces.top: offcialFaces = CubemapFace.PositiveY; break; case CubeMapFaces.bottom: offcialFaces = CubemapFace.NegativeY; break; case CubeMapFaces.front: offcialFaces = CubemapFace.PositiveZ; break; case CubeMapFaces.back: offcialFaces = CubemapFace.NegativeZ; break; } } #endregion #region 檢測多張保存時對應的cubemap面 /// <summary> /// Checks the multi cube map faces. /// </summary> void CheckMultiCubeMapFaces (int index) { switch (index) { case (int)CubeMapFaces.right: offcialFaces = CubemapFace.PositiveX; break; case (int)CubeMapFaces.left: offcialFaces = CubemapFace.NegativeX; break; case (int)CubeMapFaces.top: offcialFaces = CubemapFace.PositiveY; break; case (int)CubeMapFaces.bottom: offcialFaces = CubemapFace.NegativeY; break; case (int)CubeMapFaces.front: offcialFaces = CubemapFace.PositiveZ; break; case (int)CubeMapFaces.back: offcialFaces = CubemapFace.NegativeZ; break; } } #endregion #region 由cubemap 朝向反推前后左右方向 string CheckDir () { string name = "TestTexture"; switch (offcialFaces) { case CubemapFace.PositiveX: name = "right"; break; case CubemapFace.NegativeX: name = "left"; break; case CubemapFace.PositiveY: name = "top"; break; case CubemapFace.NegativeY: name = "bottom"; break; case CubemapFace.PositiveZ: name = "front"; break; case CubemapFace.NegativeZ: name = "back"; break; } return name; } #endregion #region 保存單張圖片 /// <summary> ///保存單張圖片 /// </summary> private void SaveSingelTexture () { Texture2D screenShot = new Texture2D (renderCubeMap.width, renderCubeMap.height, TextureFormat.ARGB32, false); for (int i = 0; i< renderCubeMap.width; i++) { for (int j = 0; j < renderCubeMap.height; j++) { screenShot.SetPixel (i, j, renderCubeMap.GetPixel (offcialFaces, renderCubeMap.width - i, renderCubeMap.height - j)); } } screenShot.Apply (); byte[] bytes = screenShot.EncodeToPNG (); string path = EditorUtility.SaveFilePanel ("圖片保存", Application.dataPath, faces.ToString (), "png"); System.IO.File.WriteAllBytes (path, bytes); AssetDatabase.Refresh (); } #endregion #region 保存多張圖片 /// <summary> ///保存單張圖片 /// </summary> private void SaveMultiTexture () { Texture2D screenShot; string path = Application.dataPath + "/" + "CubeMapTextures/"; for (int k = 0; k< 6; k++) { screenShot = new Texture2D (renderCubeMap.width, renderCubeMap.height, TextureFormat.ARGB32, false); CheckMultiCubeMapFaces (k); for (int i = 0; i< renderCubeMap.width; i++) { for (int j = 0; j < renderCubeMap.height; j++) { screenShot.SetPixel (i, j, renderCubeMap.GetPixel (offcialFaces, renderCubeMap.width - i, renderCubeMap.height - j)); } } screenShot.Apply (); byte[] bytes = screenShot.EncodeToPNG (); if (!System.IO.Directory.Exists (path)) { System.IO.Directory.CreateDirectory (path); } System.IO.File.WriteAllBytes (path + CheckDir () + ".png", bytes); } AssetDatabase.Refresh (); Debug.LogWarning ("六張圖片已經保存在 " + path + " 下!"); } #endregion void OnInspectorUpdate () { this.Repaint (); } void OnFocus () { //Debug.Log("當窗口獲得焦點時調用一次"); } void OnLostFocus () { //Debug.Log("當窗口丟失焦點時調用一次"); Caching.CleanCache (); } }
注意
1 cubemap 需保持可讀寫狀態(勾選readable)
2 cubemap尺寸較大時可能需要稍稍等待下(和mm喝咖啡的時間來了 ^^)