Unity3.4.2升级3.5.2之lightmap数据

问题描述:

  Unity3.5的升级日志中,描述了不少新的功能和一些纠错,所以想将项目的引擎版本从之前的3.4.2升级到3.5.2。

  但是,从U3D2.4.2升级到U3D3.5.2之后,发现游戏场景物体出现了大量的贴图错乱,下图所示为升级前后,场景中一棵树的效果图对比,感觉问题挺严重的,头疼啊~

问题原因:

  引用一段Unity官方升级日志,可以解释这个问题出现的原因:

  "Fixed the "Generate Lightmap UVs" reducing the difference between mac/win. Not there yet, but should be fixed for the most part. Please reimport meshes and rebake lightmaps."

  "Even more fixes to "Generate Lightmap UVs" to make it generate same UVs across Mac/Win."

  翻译过来就是,3.5的Generate Lightmap UVs的算法变了。

  之前在3.4.2中工作时,发现在Mac上打的lightmap,放到Win32上后,效果不太一样,原因就是在3.4版本中,Mac和Win32上生成光照贴图UV的算法不一样。话说,虽然不一样,但最终效果差别也不是太大,整体效果还是不错的。

  在3.5中,Generate Lightmap UVs的算法变了,以使得mac和win平台上的生成算法基本一致(不是完全一致), 这样,在mac中打的lightmap在win上就能直接使用,并且效果一样了。这是升级所带来的好处,但同时带来的坏处就是,3.4中打的lightmap,在3.5中将不能用了,悲情啊,并且官方给的建议是"rebake lightmaps"(太不负责人了)。

  PS:升级后并不是所有资源的光照贴图都错乱了,只有那些在模型导入设置中勾选了 "Generate Lightmap UVs" 的模型有问题。换句话说,就是所有依赖Unity生成光照贴图UV的模型都悲剧了。所以在此建议,正在使用3.4开发的项目组,让美术不要依赖此选项生成UV2,而是在max中直接做好,再导入Unity。

 

解决方案:

  最简单的解决方案就是rebake lightmaps(官方建议)。但考虑到美术工作量的问题,我用了另外一个方法来解决这个问题。

  基本想法是,将3.4中正确的数据备份到本地,然后重写到3.5的工程中。变化的只是模型的UV2,所以只需备份每个模型的UV2即可。

  所以代码分为2个部分:(1)拿到模型UV2的数据,并序列化到本地;(2)读取序列化数据,并将数据写入到对应的模型。

  

using UnityEditor;
using UnityEngine;
using System.IO;
using System.Xml;
using System.Collections;
using System.Collections.Generic;

// (1)拿到模型UV2的数据,并序列化到本地;
public class UVConverterWindow : EditorWindow
{
    static EditorWindow window;
    static Vector2[] s_uv;

    public static Dictionary<string, List<List<Vector2>>> SceneUVs = new Dictionary<string, List<List<Vector2>>>();

    [MenuItem("Editor/UVConverterWindow")]
    static void Execute()
    {
        if (window == null)
            window = (UVConverterWindow)GetWindow(typeof(UVConverterWindow));
        window.Show();
    }

    void OnGUI()
    {
        if (GUILayout.Button("GetCurrentSceneUVs", GUILayout.Width(200), GUILayout.Height(50)))
        {
            GetAndPrintAllGameobject();
        }
        EditorGUILayout.Space();
        if (GUILayout.Button("SaveData", GUILayout.Width(200), GUILayout.Height(50)))
        {
            SaveData("SceneUVs.xml");
        }
        EditorGUILayout.Space();
        if (GUILayout.Button("SceneUVs.count", GUILayout.Width(200), GUILayout.Height(50)))
        {
            Debug.Log("SceneUVs.count = " + SceneUVs.Count);
        }
        EditorGUILayout.Space();
        if (GUILayout.Button("SceneUVs.Clear", GUILayout.Width(200), GUILayout.Height(50)))
        {
            SceneUVs.Clear();
        }

    }

    // 得到所有物体的UV2数据,并存进SceneUVs
    void GetAndPrintAllGameobject()
    {
        Object[] objects = GameObject.FindObjectsOfTypeAll(typeof(GameObject));
        List<GameObject> LstObjs = new List<GameObject>();
        foreach (Object obj in objects)
        {
            if ((obj as GameObject).GetComponentsInChildren(typeof(MeshFilter)).Length != 0)
                LstObjs.Add((obj as GameObject));
        }
        List<string> LstObjNames = new List<string>();
        foreach (GameObject go in LstObjs)
        {
            if (go.name.Contains("Rain") || go.name.Equals("flower_red01") || go.name.Equals("Snowflakes") || go.name.Equals("Snowbox") || go.name.Equals("levelmeshes_1")
                || go.name.Equals("Envi") || go.name.Equals("shrub") || go.name.Equals("bush") || go.name.Equals("EnVironments"))
            {
                continue;
            }

            if (SceneUVs.ContainsKey(go.name) || LstObjNames.Contains(go.name) )
                continue;
            else
                LstObjNames.Add(go.name);
        }

        foreach (string name in LstObjNames)
        {
            Debug.Log("name = " + name);
            // new GameObject
            List<List<Vector2>> newGoLst = new List<List<Vector2>>();
            GameObject asset = GameObject.Find(name) as GameObject;
            foreach (MeshFilter meshFilter in asset.GetComponentsInChildren(typeof(MeshFilter)))
            {
                // new MeshFilter
                List<Vector2> newMeshFilter = new List<Vector2>();
                Vector2[] uvs = meshFilter.sharedMesh.uv2;
                for (int i = 0; i < uvs.Length; i++)
                {
                    newMeshFilter.Add(new Vector2(uvs[i].x, uvs[i].y));
                }
                newGoLst.Add(newMeshFilter);
            }
            SceneUVs.Add(name, newGoLst);
        }
    }

    // 将SceneUVs中的数据存到本地文件
    public void SaveData(string strFilename)
    {
        XmlDocument XmlDoc = new XmlDocument();
        XmlElement XmlRoot = XmlDoc.CreateElement("SceneData");
        XmlDoc.AppendChild(XmlRoot);

        foreach (KeyValuePair<string, List<List<Vector2>>> pair in SceneUVs)
        {
            XmlElement xmlGameObject = XmlDoc.CreateElement("GameObject");
            XmlRoot.AppendChild(xmlGameObject);
            xmlGameObject.SetAttribute("name", pair.Key);

            foreach (List<Vector2> meshUVS in pair.Value)
            {
                XmlElement xmlMeshFilter = XmlDoc.CreateElement("Mesh");
                xmlGameObject.AppendChild(xmlMeshFilter);

                foreach (Vector2 uv in meshUVS)
                {
                    XmlElement xmlUVValues = XmlDoc.CreateElement("UV");
                    xmlMeshFilter.AppendChild(xmlUVValues);

                    xmlUVValues.SetAttribute("U", XmlConvert.ToString(uv.x));
                    xmlUVValues.SetAttribute("V", XmlConvert.ToString(uv.y));
                }
            }
        }

        string strPath = "Assets/Resources/DB/" + strFilename;
        if (strPath != "")
            XmlDoc.Save(strPath);

    }

}

  在3.4中加入上述代码,编译后,会多出一个菜单项【Editor/UVConvertorWindow】,在需要拿数据的场景中通过此菜单弹出脚本对话框,然后先通过“GetCurrentSceneUVs”按钮获取数据,再点击"SaveData"将数据存到本地。

  遍历游戏中的是几个场景,得到了12M多的数据,下面为得到的一部分数据:

<SceneData>
  <GameObject name="stone_2">
    <Mesh>
      <UV U="0.9810646" V="0.201751187" />
      <UV U="0.570682466" V="0.5148572" />
      <UV U="0.0608001873" V="0.9120108" />
      <UV U="0.224167913" V="0.982134938" />
      <UV U="0.140530467" V="0.630211532" />
      <UV U="0.0273421481" V="0.8217057" />
      <UV U="0.912159264" V="0.209614545" />
      <UV U="0.04156696" V="0.703405" />
      <UV U="0.8343242" V="0.220369309" />
      <UV U="0.6729313" V="0.498295426" />
      <UV U="0.9025791" V="0.167751029" />
      <UV U="0.6711328" V="0.469830871" />
      <UV U="0.7272404" V="0.466777444" />
...

  好的,拿到数据~\(≧▽≦)/~,接下来就是在3.5中将这些数据写入模型了。

  重写这些数据的原理是,让那些打了勾的模型重新导入,然后在导入的过程中对模型的UV2进行一个重写。下面是作为工具提供的一个窗口类:

using UnityEditor;
using UnityEngine;
using System.IO;
using System.Xml;
using System.Collections;
using System.Collections.Generic;

// 升级窗口类
public class UpgradeUnity3D352 : EditorWindow
{
    public static Dictionary<string, List<List<Vector2>>> SceneUVs = new Dictionary<string,List<List<Vector2>>>();
    public static bool PostprocessOn = false;

    static UpgradeUnity3D352 window;
    [MenuItem("Editor/UpgradeUnity3D352")]
    static void Execute()
    {
        if (window == null)
            window = (UpgradeUnity3D352)GetWindow(typeof(UpgradeUnity3D352));
        window.Show();
    }

    void OnGUI()
    {
        if (GUILayout.Button("LoadData", GUILayout.Width(200), GUILayout.Height(50)))
        {
            LoadData("SceneUVs");
        }
        EditorGUILayout.Space();
        if (GUILayout.Button("ReimportSelectedAll", GUILayout.Width(200), GUILayout.Height(50)))
        {
            DoReimport();
        }
        PostprocessOn = EditorGUILayout.Toggle("PostprocessOn", PostprocessOn);
    }

    void OnDestroy()
    {
        PostprocessOn = false;
    }

    // 手动导致Project选中的资源重新导入
    void DoReimport()
    {
        foreach (Object o in Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets))
        {
            if (!(o is GameObject))
                continue;

            Debug.Log(AssetDatabase.GetAssetPath(o));
            AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(o));
        }
    }

    // 载入数据
    public void LoadData(string strFilename)
    {
        XmlDocument xmlDoc = new XmlDocument();
        TextAsset textAsset = (TextAsset)Resources.Load("DB/" + strFilename, typeof(TextAsset));
        if (textAsset == null)
        {
            Debug.LogError("Load Monster DB Failed!");
            return;
        }
        xmlDoc.Load(new StringReader(textAsset.text));
        XmlElement xmlRoot = xmlDoc.DocumentElement;

        SceneUVs.Clear();

        XmlNodeList xmlGOList = xmlRoot.ChildNodes;
        foreach (XmlNode go in xmlGOList)
        {
            if ((go is XmlElement) == false)
                continue;

            // GameObject
            XmlElement xmlGO = go as XmlElement;
            List<List<Vector2>> lstGOData = new List<List<Vector2>>();

            foreach (XmlNode mesh in xmlGO.ChildNodes)
            {
                if ((mesh is XmlElement) == false)
                    continue;

                // MeshFilter
                XmlElement xmlMesh = mesh as XmlElement;
                List<Vector2> lstMeshUVs = new List<Vector2>();

                foreach (XmlNode uv in xmlMesh.ChildNodes)
                {
                    if ((uv is XmlElement) == false)
                        continue;

                    XmlElement xmlUV = uv as XmlElement;

                    // UV
                    lstMeshUVs.Add(new Vector2(XmlConvert.ToSingle(xmlUV.GetAttribute("U")), XmlConvert.ToSingle(xmlUV.GetAttribute("V"))));
                }

                lstGOData.Add(lstMeshUVs);
            }

            SceneUVs.Add(xmlGO.GetAttribute("name"), lstGOData);
        }
    }
}

  在3.5中加入上述代码,编译后,会多出一个菜单项【Editor/UpgradeUnity3D352】,打开此对话框,然后在Project视口中选中场景资源目录(也可选择要处理的单个对象),勾上PostprocessOn,然后LoadData,再ReimportSelectedAll。

  将下面的脚本代码也放入3.5的工程,其中的代码利用模型导入回调函数来对模型的UV2进行重写。AssetPostprocessor是U3D提供的一个资源处理类,其中的OnPostprocessModel为该类提供的一个回调,可以用来对导入完成的模型进行一个处理。

  而ReimportSelectedAll操作,会触发Reimport行为,从而运行OnPostprocessModel中的代码,其中的代码较为简单,就是重赋UV2。

using UnityEditor;
using UnityEngine;
using System.IO;
using System.Xml;
using System.Collections;
using System.Collections.Generic;

public class FbxProcessor : AssetPostprocessor
{
    void OnPreprocessModel()
    {
        ModelImporter modelImporter = (ModelImporter)assetImporter;
        if (modelImporter.assetPath.ToLower().Contains(".fbx") && 
            (modelImporter.assetPath.Contains("Char")
            || modelImporter.assetPath.Contains("Parts")
            || modelImporter.assetPath.Contains("Weapon")))
        {
            modelImporter.globalScale = 1.0f;
			modelImporter.generateMaterials = ModelImporterGenerateMaterials.PerSourceMaterial;
        }
    }

    void OnPostprocessModel(GameObject go)
    {
        if (go.name == "FBmap1" || go.name == "tortoise_01")
            return;
        ModelImporter modelImporter = (ModelImporter)assetImporter;
        if (UpgradeUnity3D352.PostprocessOn == true)
        {
            // 如果勾选了生成UV
            if (modelImporter.generateSecondaryUV == true)
            {
                if (UpgradeUnity3D352.SceneUVs == null || UpgradeUnity3D352.SceneUVs.Count == 0)
                    return;

                if (UpgradeUnity3D352.SceneUVs.ContainsKey(go.name) == false)
                    return;

                int index = 0;
                foreach (MeshFilter meshFilter in go.GetComponentsInChildren(typeof(MeshFilter)))
                {
                    Vector2[] testUVs = new Vector2[meshFilter.sharedMesh.vertices.Length];
                    if (index >= UpgradeUnity3D352.SceneUVs[go.name].Count)
                        break;
                    for (int i = 0; i < testUVs.Length; ++i)
                    {
                        if (i < UpgradeUnity3D352.SceneUVs[go.name][index].Count)
                        {
                            testUVs[i].x = UpgradeUnity3D352.SceneUVs[go.name][index][i].x;
                            testUVs[i].y = UpgradeUnity3D352.SceneUVs[go.name][index][i].y;
                        }
                    }
                    meshFilter.sharedMesh.uv2 = testUVs;
                    index++;
                }
            }
        }
    }
}

 上述方法经测试有效,项目组正在使用。

原文地址:https://www.cnblogs.com/sifenkesi/p/2673558.html