Unity3D优化:分场景烘焙,综合场景加载

需求:

在游戏开发中,我们经常会遇到以下情况:

1,烘焙场景太大,每次只需要烘焙部分;

2,资源热更,需要分步加载;

3,场景跳转太慢,需手动处理资源;

4,网页游戏不方便场景跳转。

诸如此类问题,就需要将资源分配到不同的小场景分别进行烘焙,最后统一到综合场景中进行合并加载。

 

解决思路:

这里主要参考了这两篇博客:Unity在一个场景中使用其他场景烘焙的物体,Unity Lightmap使用总结。

其中第一篇博客提供了解决思路,但年代久远;第二篇不太完整,所以取舍之后做出如下解决方案。

注意:在以下解决方案测试中,渲染环境基本保持一致;如果不一致需自己进行扩充。

 

解决方案:

第一步:烘焙场景

将父节点及之下需烘焙的子节点勾选静态模式,自行设置参数进行烘焙:

Unity3D优化:分场景烘焙,综合场景加载_第1张图片

Unity3D优化:分场景烘焙,综合场景加载_第2张图片

烘焙会出现场景同名文件夹,如下图所示:

Unity3D优化:分场景烘焙,综合场景加载_第3张图片

烘焙场景可以拷贝,这样能保持烘焙信息一致;另外拷贝一份删除所有信息,作为主场景。

 

第二步:代码处理

1,主场景渲染管理器,这个主要是控制场景的光照贴图信息,所以要放到Awake中启动,并在需要的时候刷新:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LightMapControl : MonoBehaviour
{
  
    [SerializeField]
    public List _renderDatainfo = new List();

    // Start is called before the first frame update
    void Awake()
    {
        SetLightMapData();
    }
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            SetLightMapData();
        }
    }

    /// 
    /// 对场景进行烘培灯光性息的赋值\n
    /// _renderDatainfo: LIST类型的贴图组
    /// 
    public void SetLightMapData()
    {
       
        // //缔特
        // //整理灯光贴图
        // //创建临时贴图list,并复制导入数据进入
        LightmapSettings.lightmapsMode = LightmapsMode.NonDirectional;
        List Templightmaps = new List(this._renderDatainfo.ToArray());
        // this._renderDatainfo.ForEach(i => Templightmaps.Add(i));
        // Templightmaps = this._renderDatainfo; //List 
        //遍历当前场景数据,如果临时list里有就删除list里的数据,并按当前id插入进去,得到最终贴图的顺序
        for (int i = 0; i < LightmapSettings.lightmaps.Length; i++)
        {
            Texture2D nowTex = LightmapSettings.lightmaps[i].lightmapColor;
            int ia = Templightmaps.IndexOf(nowTex);
            if (ia != -1)
            {
                Templightmaps.RemoveAt(ia);
            }
            Templightmaps.Insert(i, LightmapSettings.lightmaps[i].lightmapColor);
        }
        //把贴图赋值给当前场景
        List aa = new List();
        Templightmaps.ForEach(i => aa.Add(this.setNewLightmapdata(i)));
        LightmapSettings.lightmaps = aa.ToArray();
    }

    private LightmapData setNewLightmapdata(Texture2D a)
    {
        LightmapData tt = new LightmapData();
        tt.lightmapColor = a;
        return tt;
    }
}

将该脚本拖到主场景中,并将各个分场景烘焙的贴图赋值到光照贴图数组中,运行结果如下:

Unity3D优化:分场景烘焙,综合场景加载_第4张图片

 

2,记录保存和加载使用烘焙信息,这里需要将父节点设置为预制体:

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(LightmapRoot))]
public class LightmapRootEditor : Editor
{
    private LightmapRoot Root
    {
        get { return target as LightmapRoot; }
    }

    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        GUILayout.Space(10);
        if (GUILayout.Button("SaveLightmapData"))
        {
            Root.GetAllLightmapData();
            SavePrefab();
        }

        GUILayout.Space(10);
        if (GUILayout.Button("SetLightmapData"))
        {
            Root.SetAllLightmapData();
        }
    }

    private void SavePrefab()
    {
        var prefab = PrefabUtility.GetCorrespondingObjectFromSource(Root);
        Debug.Log("记录预制体信息");
        if (prefab != null)
        {
            PrefabUtility.ReplacePrefab(Root.gameObject, prefab);
            Debug.Log(prefab);
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LightMapControl : MonoBehaviour
{

    [SerializeField]
    public List _renderDatainfo = new List();

    // Start is called before the first frame update
    void Awake()
    {
        SetLightMapData();
    }
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            SetLightMapData();
        }
    }

    /// 
    /// 对场景进行烘培灯光性息的赋值\n
    /// _renderDatainfo: LIST类型的贴图组
    /// 
    public void SetLightMapData()
    {

        // //缔特
        // //整理灯光贴图
        // //创建临时贴图list,并复制导入数据进入
        LightmapSettings.lightmapsMode = LightmapsMode.NonDirectional;
        List Templightmaps = new List(this._renderDatainfo.ToArray());
        // this._renderDatainfo.ForEach(i => Templightmaps.Add(i));
        // Templightmaps = this._renderDatainfo; //List 
        //遍历当前场景数据,如果临时list里有就删除list里的数据,并按当前id插入进去,得到最终贴图的顺序
        for (int i = 0; i < LightmapSettings.lightmaps.Length; i++)
        {
            Texture2D nowTex = LightmapSettings.lightmaps[i].lightmapColor;
            int ia = Templightmaps.IndexOf(nowTex);
            if (ia != -1)
            {
                Templightmaps.RemoveAt(ia);
            }
            Templightmaps.Insert(i, LightmapSettings.lightmaps[i].lightmapColor);
        }
        //把贴图赋值给当前场景
        List aa = new List();
        Templightmaps.ForEach(i => aa.Add(this.setNewLightmapdata(i)));
        LightmapSettings.lightmaps = aa.ToArray();
    }

    private LightmapData setNewLightmapdata(Texture2D a)
    {
        LightmapData tt = new LightmapData();
        tt.lightmapColor = a;
        return tt;
    }
}

这里的具体原理可参考博客:Unity Lightmap使用总结

使用方法是将一个放到Editor文件夹中,另一个赋值给各个分场景的预制体父节点,以记录烘焙信息。使用结果如下:

Unity3D优化:分场景烘焙,综合场景加载_第5张图片

这里的basic是记录光照贴图初始位置的,下标从0开始。

 

3,在主场景中测试

测试代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Test : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        for (var _i = 1; _i <= 3; _i++)
        {
            GameObject _obj = Instantiate(Resources.Load("" + _i));
        }   
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

测试结果为:

Unity3D优化:分场景烘焙,综合场景加载_第6张图片

 

 

测试工程连接为:https://download.csdn.net/download/Tel17610887670/14934894

 

四,后记

 

这种方法在流程化的情况下,操作起来比较简单,但还是有如下不足之处:

1,因为静态的缘故,会占用双份内存;

2,加载操作比较麻烦,需要分步处理;

 

在公司内部项目使用的时候,使用了更好的替代方案,大致思路是:合并网格之后,使用shader的顶点信息记录本身贴图与光照贴图。在此就不做公开。

你可能感兴趣的:(U3D,游戏,烘焙,合并光照贴图,Unity3D)