当前位置:网站首页>[总结] Unity Lightmap使用总结
[总结] Unity Lightmap使用总结
2022-08-08 06:26:00 【GREAT1217】
Unity Lightmap使用总结
关于“lightmap 动态加载”有两种解释:
一、动态加载 lightmap 给 Prefab : 可用于克隆出多个对象使用同一个光照贴图
二、动态加载 lightmap 给 Scene :可用于不同天气环境的动态切换
一 、动态加载 lightmap 给 Prefab
效果
操作步骤
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)不会保存到预制体上。于是就有了下一步——记录烘焙的光照贴图信息。
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 面板提示的警告:
主要意思是:在对象静态批处理后无法再为 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}};
}
举个栗子吧
在王者荣耀中切换查看英雄时,要根据英雄的阵营(背景故事)切换不同的背景,此功能肯定不能通过切换场景实现,那样太慢了,所以根据上面的思路:
将不同的背景做成预制体,烘焙好光照贴图
在 LightmapRoot 中增加字段,保存对光照贴图的引用
(上面的图中两张贴图分别记录了光照信息 lightmapColor 和方向信息 lightmapDir )
切换英雄时动态加载背景预制体,设置场景的光照贴图
这个栗子同时包含了以上提到的两个 lightmap 动态加载
边栏推荐
- C语言实现冒泡排序及对冒泡排序的优化处理
- Leetcode topic sharing and explanation
- ResNet网络结构详解、完整代码实现
- 正则爬取豆瓣Top250数据存储到CSV文件(6行代码)
- [GWCTF 2019] I have a database 1
- 【图形学】10 UnityShader入门(二)
- 神经网络二分类问题范例,神经网络解决分类问题
- Leetcode题目分享以及讲解
- 最详细的Vivado安装教程
- The state machine control shift register multisim simulation in the process of state variables and state transition conditions don't match
猜你喜欢
随机推荐
Bugku faster
SCIERC语料格式解读
rhcsa第三天笔记
【嵌入式Linux】SQLite数据库
逻辑回归推导
如何使用conda,pip安装、更新、查看和卸载重装Pytorch?
MongoDB3.x创建用户与用户角色
C语言实现顺序表的九个常用接口
略解损失函数
正则爬取豆瓣Top250数据存储到CSV文件(6行代码)
Unity HDRP下VRTK传送、穿墙 时画面淡入淡出、视觉遮挡无法正确显示问题解决
深度学习中的优化问题(Optimization)
3.关于剪枝论文的分类和整理(随笔)
TextCNN实现imdb数据集情感分类任务
RCNN目标检测原文理解
【图形学】18 光照模型(三、镜面反射的Shader实现)
Unity中获取一个物体下所有的子物体的方法
RHCSA-配置redhat
关系抽取论文阅读笔记
NVIDIA CUDA 高度并行处理器编程(九):并行模式:稀疏矩阵-向量乘法