[总结] Unity Lightmap使用总结

关于“lightmap 动态加载”有两种解释:

一、动态加载 lightmap 给 Prefab : 可用于克隆出多个对象使用同一个光照贴图

二、动态加载 lightmap 给 Scene :可用于不同天气环境的动态切换

一 、动态加载 lightmap 给 Prefab

效果

xiaoguo

操作步骤

1. 烘焙光照贴图

先将预制体对象设置为 Lightmap Static 并放在场景中,如下图,具体操作就不细说了。可以在 MeshRenderer 组件上看到,该对象的 Baked Lightmap 信息:

{ LightmapIndex = 65535, LightmapScaleOffset = new Vector4 (1,1,0,0) }

然后在Lighting Settings 中勾选 Mixed Lighting ,其他设置自行选择,点击 Generate Lighting 生成光照贴图,再次查看预制体对象的光照贴图信息,与没有烘焙前不一样了,其中:

{ LightmapIndex = 使用的第几张光照贴图, LightmapScaleOffset = 在光照贴图中使用的 UV 大小和偏移 }

再次选择一个预制体,拖放进场景中,发现的并没有光照效果,查看 Baked Lightmap 信息:

{ LightmapIndex = 65535, LightmapScaleOffset = new Vector4 (1,1,0,0) }

可以得出结论:烘焙的光照贴图信息(Baked Lightmap)不会保存到预制体上。于是就有了下一步——记录烘焙的光照贴图信息。

before
after

2. 记录光照信息

这一步主要是编写脚本(详细代码附在最后),用递归获取并保存此对象及所有子对象的烘焙光照贴图信息,包括:

{ LightmapIndex(int),LightmapScaleOffset(Vector4) }

考虑到运行状态下赋值时多对象 GetComponment() 耗费性能,所以在编辑器状态下记录光照信息时同时保存 MeshRenderer 的引用,所以保存的信息的数据结构可以为:

//(详细代码附在最后)
[Serializable]
public struct CustomLightmapData
   {
    public MeshRenderer _renderer;
    public int _lightmapIndex;
 public Vector4 _lightmapScaleOffset;
   }

3. 加载光照信息

这一步主要是将上一步保存的烘焙信息重新赋值给相应的 Mesh Renderer 组件(代码附在最后)

遇到的问题(已解决)

1. 光照贴图错位问题(已解决)

可以看到 Console 面板提示的警告:

warning

主要意思是:在对象静态批处理后无法再为 Baked Lightmap 赋值,解决办法就是在 Awake() 函数里进行赋值(代码附在最后)或者取消对象的静态批处理(不推荐)

详细代码

LightmapRoot.cs

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

public class LightmapRoot : MonoBehaviour
{
    public List<CustomLightmapData> _datas;

    void Start()
    {
        SetAllLightmapData();
    }
    
    public void GetAllLightmapData()
    {
        _datas = new List<CustomLightmapData>();
        GetChildData(transform);
    }

    public void SetAllLightmapData()
    {
        if (_datas == null) return;
        for (int i = 0; i < _datas.Count; i++)
        {
            _datas[i]._renderer.lightmapIndex = _datas[i]._lightmapIndex;
            _datas[i]._renderer.lightmapScaleOffset = _datas[i]._lightmapScaleOffset;
        }
    }

    private void GetChildData(Transform parent)
    {
        if (parent.gameObject.isStatic)
        {
            var render = parent.GetComponent<MeshRenderer>();
            if (render != null)
            {
                _datas.Add(new CustomLightmapData(render, render.lightmapIndex, render.lightmapScaleOffset));
            }

            for (int i = 0; i < parent.childCount; i++)
            {
                GetChildData(parent.GetChild(i));
            }
        }
    }

    // private void SetChildData(Transform parent, int index = -1)
    // {
    //     if (parent.gameObject.isStatic)
    //     {
    //         var render = parent.GetComponent<Renderer>();
    //         if (render != null)
    //         {
    //             ++index;
    //             var data = _datas[index];
    //             render.lightmapIndex = data._lightmapIndex;
    //             render.lightmapScaleOffset = data._lightmapScaleOffset;
    //         }
    //
    //         for (int i = 0; i < parent.childCount; i++)
    //         {
    //             SetChildData(parent.GetChild(i), index);
    //         }
    //     }
    // }
}

[Serializable]
public struct CustomLightmapData
{
    public MeshRenderer _renderer;
    public int _lightmapIndex;
    public Vector4 _lightmapScaleOffset;

    public CustomLightmapData(MeshRenderer renderer, int lightmapIndex, Vector4 lightmapScaleOffset)
    {
        _renderer = renderer;
        _lightmapIndex = lightmapIndex;
        _lightmapScaleOffset = lightmapScaleOffset;
    }
}

LightmapRootEditor.cs

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);
        if (prefab != null)
        {
            PrefabUtility.ReplacePrefab(Root.gameObject, prefab);
        }
    }
}

二 、 动态加载 lightmap 给 Scene

相当于动态切换场景的 lightmap,实现的核心逻辑是:

[SerializeField] private Texture2D _lightmapColor;
[SerializeField] private Texture2D _lightmapDir;
[SerializeField] private Texture2D _shadowMask;

public void ResetLightmapData()
{
        LightmapSettings.lightmaps = new[] {new LightmapData {lightmapColor = _lightmapColor, lightmapDir = _lightmapDir, shadowMask = _shadowMask}};
}

举个栗子吧

lightmap

在王者荣耀中切换查看英雄时,要根据英雄的阵营(背景故事)切换不同的背景,此功能肯定不能通过切换场景实现,那样太慢了,所以根据上面的思路:

将不同的背景做成预制体,烘焙好光照贴图

在 LightmapRoot 中增加字段,保存对光照贴图的引用

(上面的图中两张贴图分别记录了光照信息 lightmapColor 和方向信息 lightmapDir )

切换英雄时动态加载背景预制体,设置场景的光照贴图

这个栗子同时包含了以上提到的两个 lightmap 动态加载 👍

  • 6
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Unity是一款功能强大的游戏引擎,允许开发者用脚本进行灯光贴图操作。 在Unity中,灯光贴图(lightmap)是指一种预计算的技术,用于模拟光源对场景的影响。通过将灯光和阴影信息渲染到一张纹理上,然后应用到场景中的物体上,可以实现更加真实的光照效果。 在使用脚本进行灯光贴图操作时,我们可以使用Unity提供的Lighting类中的相关函数和属性来获取和设置灯光贴图的参数。 首先,我们可以使用Lightmapping类中的静态函数之一来生成灯光贴图。例如,调用Lightmapping.BakeAsync函数可以进行异步地灯光贴图烘焙操作,该函数将会在后台进行烘焙过程,不会阻塞主线程。另外,还可以使用Lightmapping.Bake函数进行同步的灯光贴图烘焙操作。 在灯光贴图完成后,我们可以通过LightmapSettings类来获取和设置灯光贴图的参数。例如,可以使用LightmapSettings.lightmaps属性来获取当前场景中的灯光贴图数组。每个灯光贴图都包含了光照信息以及阴影信息。我们可以通过修改该数组中的元素来改变灯光的状态。 此外,还可以使用LightmapSettings类中的其他属性和函数来进一步操作灯光贴图,如LightmapSettings.lightmapsMode用于设置灯光模式、LightmapSettings.bakedColorSpace用于设置烘焙颜色空间等。 总结起来,Unity中可以通过脚本进行灯光贴图操作,通过调用Lightmapping类和LightmapSettings类中的相关函数和属性,可以生成、获取和设置灯光贴图,实现更加真实的光照效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值