Unity中对预制体烘焙光照贴图,在其他Scene中使用或者动态生成带光照贴图的预制体

news2025/1/12 6:18:04

记录个人开发笔记,如果有大佬有更好的方法或者觉得我这个方法哪里有问题欢迎指正!

首先说下为什么会弄预制体烘焙光照贴图,因为项目需求需要动态生成一个房间的,因此是将房间弄成预制体,动态生成就好了,这个很简单,但是呢最后程序是在一体机中跑的,性能比较差,所有美术调好的效果后如果是实时光,性能开销就比较大,烘焙呢又因为不需要一开始显示而且必须在同一个scene中所有才去弄了这么个烘焙预制体光照贴图。(既然是预制体烘焙,因此此方法也可以跨工程使用,所有有时候美术调效果时可以直接只烘焙一个预制体给到开发人员,不用再给开发人员整个场景了)

接下来直接说怎么操作:

先简单的搭建了一个Plane和Cube再打了一个点光源 

接下去,我们再需要烘焙的预制体的最外层父节点上挂上一个PrefabLightmapsData的脚本(具体脚本内容会在最后提供)

 后面将我们需要烘焙光照贴图的物体都勾上Static,让他成为静态物体,同时我们要点击Static旁边的小箭头,取消Batching Static

当然了,别忘了把我们的光(这边是点光源)改成Baked

 然后打开Lighting Setting一样按照需求设置光照贴图数据就行 

但是接下来烘焙需要注意不再是点击Lighting setting里的Generate Lighting按钮烘焙了,再之前的PrefabLightmapsData脚本中提供了一个简单的编辑器扩展的烘焙按钮,在最上方有个Tools->Bake Prefab Lightmaps的按钮,我们点击这个按钮进行烘焙

顺便提一下,通过我们编辑器扩展的按钮烘焙的时候,烘焙进度不在原来的引擎自带的地方显示了,而是在下方的任务栏上那个Unity图标上会有一个绿色的进度

烘焙完成后,我们就可以在PrefabLightmapsData上的三个数组中看到这些模型和光照贴图的数据了 

接着我们可以创建一个新的Scene,然后把预制体拖进去看下效果,下图我们可以看到新的场景中是没有任何光照灯的,但是看到的效果却是有正常光照贴图的效果,如果没看到效果,因为是在Awake中设置的,可以运行后再看就会有效果了

到这里基本上预制体烘焙光照贴图就已经完成了,下面放上PrefabLightmapsData脚本的代码,以及最后会写几点之前我使用时踩到过的几个坑。

(脚本有更新,原先的脚本不支持地形的光照贴图设置)

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

[DisallowMultipleComponent]
[ExecuteInEditMode]
public class PrefabLightmapsData : MonoBehaviour
{
    [System.Serializable]
    struct RendererInfo
    {
        public Terrain terrain;
        public Renderer renderer;
        public int lightmapIndex;
        public Vector4 lightmapOffsetScale;
    }

    [SerializeField]
    RendererInfo[] m_RendererInfo;
    [SerializeField]
    Texture2D[] m_LightmapsColor;
    [SerializeField]
    Texture2D[] _lightmaps;

    void Awake()
    {
        if (m_RendererInfo == null || m_RendererInfo.Length == 0)
            return;

        var lightmaps = LightmapSettings.lightmaps;
        var combinedLightmaps = new LightmapData[lightmaps.Length + m_LightmapsColor.Length];

        lightmaps.CopyTo(combinedLightmaps, 0);
        for (int i = 0; i < m_LightmapsColor.Length; i++)
        {
            combinedLightmaps[i + lightmaps.Length] = new LightmapData();
            combinedLightmaps[i + lightmaps.Length].lightmapColor = m_LightmapsColor[i];
            combinedLightmaps[i + lightmaps.Length].lightmapDir = _lightmaps[i];

        }

        ApplyRendererInfo(m_RendererInfo, lightmaps.Length);
        LightmapSettings.lightmaps = combinedLightmaps;
    }


    static void ApplyRendererInfo(RendererInfo[] infos, int lightmapOffsetIndex)
    {
        for (int i = 0; i < infos.Length; i++)
        {
            var info = infos[i];
            if (info.renderer != null)
            {
                info.renderer.lightmapIndex = info.lightmapIndex + lightmapOffsetIndex;
                info.renderer.lightmapScaleOffset = info.lightmapOffsetScale;
            }
            else if(info.terrain!=null)
            {
                info.terrain.lightmapIndex = info.lightmapIndex + lightmapOffsetIndex;
                info.terrain.lightmapScaleOffset = info.lightmapOffsetScale;
            }
        }
    }


#if UNITY_EDITOR
    [UnityEditor.MenuItem("Tools/Bake Prefab Lightmaps")]
    static void GenerateLightmapInfo()
    {
        if (UnityEditor.Lightmapping.giWorkflowMode != UnityEditor.Lightmapping.GIWorkflowMode.OnDemand)
        {
            Debug.LogError("ExtractLightmapData requires that you have baked you lightmaps and Auto mode is disabled.");
            return;
        }
        UnityEditor.Lightmapping.Bake();

        PrefabLightmapsData[] prefabs = GameObject.FindObjectsOfType<PrefabLightmapsData>();

        foreach (var instance in prefabs)
        {
            var gameObject = instance.gameObject;
            var rendererInfos = new List<RendererInfo>();
            var lightmapsColor = new List<Texture2D>();
            List<Texture2D> lightmapsDir = new List<Texture2D>();

            GenerateLightmapInfo(gameObject, rendererInfos, lightmapsColor, lightmapsDir);

            instance.m_RendererInfo = rendererInfos.ToArray();
            instance.m_LightmapsColor = lightmapsColor.ToArray();
            instance._lightmaps = lightmapsDir.ToArray();


            var targetPrefab = PrefabUtility.GetCorrespondingObjectFromOriginalSource(instance.gameObject) as GameObject;
            if (targetPrefab != null)
            {
                GameObject root = PrefabUtility.GetOutermostPrefabInstanceRoot(instance.gameObject);

                if (root != null)
                {
                    GameObject rootPrefab = PrefabUtility.GetCorrespondingObjectFromSource(instance.gameObject);
                    string rootPath = AssetDatabase.GetAssetPath(rootPrefab);
                    PrefabUtility.UnpackPrefabInstanceAndReturnNewOutermostRoots(root, PrefabUnpackMode.OutermostRoot);
                    try
                    {
                        PrefabUtility.ApplyPrefabInstance(instance.gameObject, InteractionMode.AutomatedAction);
                    }
                    catch { }
                    finally
                    {
                        PrefabUtility.SaveAsPrefabAssetAndConnect(root, rootPath, InteractionMode.AutomatedAction);
                    }
                }
                else
                {
                    PrefabUtility.ApplyPrefabInstance(instance.gameObject, InteractionMode.AutomatedAction);
                }
            }
        }
    }

    static void GenerateLightmapInfo(GameObject root, List<RendererInfo> rendererInfos, List<Texture2D> lightmapsColor, List<Texture2D> lightmapsDir)
    {
        var renderers = root.GetComponentsInChildren<MeshRenderer>();
        foreach (MeshRenderer renderer in renderers)
        {
            if (renderer.lightmapIndex != -1)
            {
                RendererInfo info = new RendererInfo();
                info.renderer = renderer;
                if (renderer.lightmapScaleOffset != Vector4.zero)
                {
                    info.lightmapOffsetScale = renderer.lightmapScaleOffset;
                    Texture2D lightmapColor = LightmapSettings.lightmaps[renderer.lightmapIndex].lightmapColor;
                    Texture2D lightmapDir = LightmapSettings.lightmaps[renderer.lightmapIndex].lightmapDir;

                    info.lightmapIndex = lightmapsColor.IndexOf(lightmapColor);
                    if (info.lightmapIndex == -1)
                    {
                        info.lightmapIndex = lightmapsColor.Count;
                        lightmapsColor.Add(lightmapColor);
                        lightmapsDir.Add(lightmapDir);
                    }

                    rendererInfos.Add(info);
                }
            }
        }

        var Terrainrenderers = root.GetComponentsInChildren<Terrain>();
        foreach (var terrain in Terrainrenderers)
        {
            if (terrain.lightmapIndex != -1)
            {
                RendererInfo info = new RendererInfo();
                info.terrain = terrain;
                if (terrain.lightmapScaleOffset != Vector4.zero)
                {
                    info.lightmapOffsetScale = terrain.lightmapScaleOffset;
                    Texture2D lightmapColor = LightmapSettings.lightmaps[terrain.lightmapIndex].lightmapColor;
                    Texture2D lightmapDir = LightmapSettings.lightmaps[terrain.lightmapIndex].lightmapDir;

                    info.lightmapIndex = lightmapsColor.IndexOf(lightmapColor);
                    if (info.lightmapIndex == -1)
                    {
                        info.lightmapIndex = lightmapsColor.Count;
                        lightmapsColor.Add(lightmapColor);
                        lightmapsDir.Add(lightmapDir);
                    }

                    rendererInfos.Add(info);
                }
            }
        }
    }
#endif
} 

具体的我也没去往深入的研究为什么,如果有大佬研究了可以写在评论区,这边我就直接说我的解决方案,在烘焙前的Lightmapping Settings里有个Directional Mode的设置,默认选择的时Directional,表示有漫反射的,Non-Directional是关闭漫反射的烘焙,我们只需要选择Non-Directional后烘焙就行,但是呢这样烘焙出来的光照贴图会缺少一张lightmap-dir的图片,导致效果不好。 

 

要怎么保证效果好旋转后又不会出现问题,后面再说,先来说下我碰到的过的第二个问题,因为这个的解决方法和第二个问题的解决方法一致。

2、第二个问题呢我直接描述下,因为现在写笔记时暂时没碰到因此就没有图片看出现问题的效果了。出现的问题是烘培好预制体后放到新的工程或者新的场景中时,所有文件都正常的,但是在场景中不管是运行状态还是没运行状态预制体的贴图都变成黑白的了。

具体原因我也没有深入的查找,如果有大佬知道欢迎在评论区或者私信告知,谢谢!

我这里也直接说解决方案,我们直接托烘焙好的预制体虽然会把光照贴图设置上去,但是在Lighting Setting中下图这个位置,是没有LightingDataAsset文件的,我们只需要在这个场景中(可以不放任何东西烘焙一个空的贴图生成一个LightingDataAsset文件即可)然后在把烘焙好的预制体放到场景中,就不会出现黑白的情况了。 

由于在最终的Scene会烘焙一个空的光照贴图生成一个LightingDataAsset文件,就让我产生了一个想法,在第一个问题中我们需要把Directional Mode设置成Non-Directional来避免第一个问题,那么我们是不是可以在最终Scene上烘焙的时候这么设置,然后烘焙预制体的时候还是选择Directional来正常设置,这样如果能成功不就能提高了预制体的烘焙效果,又能避免旋转后出现问题了吗。

然后我就开始了尝试,先给空的场景设置Non-Directional后用PrefabLightmapsData脚本中提供的烘焙按钮给空场景烘焙了个贴图和LightingDataAsset

 

然后再将之前设置Directional烘焙出来的预制体放入场景中,再把X旋转90度后我们可以看到做出红框中的光照贴图也不再变黑了。

 好了本篇笔记就到这边结束了,如果有问题,请在评论区留言讨论.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/602022.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Datagear】如何给Datagear追加Admin

【背景】 Datagear默认是只设置了一个Admin&#xff0c;这个Admin可以完成用户的各种管理操作&#xff0c;并且对所有数据源&#xff0c;数据集&#xff0c;图表&#xff0c;面板拥有全部编辑权限。 【问题】 只有一个Admin很多场景下不够用。但是应用设置本身没有提供追加A…

【测试人生】测试工程如何去学习接口自动化技术

一、为什么要学习自动化 提高生产力&#xff1a;自动化可以帮助您在短时间内完成重复性工作&#xff0c;从而大大提高生产力。自动化不仅减少了手动执行任务所需的时间和精力&#xff0c;还减少了出错的风险。 质量保证&#xff1a;自动化测试可以确保软件在修改后仍然按预期运…

2023 光亚展|乐鑫将携 AI、Wi-Fi 6、私有云和 Matter 方案精彩亮相

2023 广州国际照明展览会&#xff08;光亚展&#xff09;将于 6 月 9 至 12 日在广州琶洲展馆启幕。本届展会以“光未来”为主题&#xff0c;畅想未来生活方式的无限可能。乐鑫科技 (688018.SH) 将在 B 区 9.2 号厅 D55 展位&#xff0c;带来具有前瞻性的智能照明解决方案和实体…

龙芯2K1000实战开发-以太网/串口设计

文章目录 概要整体架构流程技术名词解释技术细节小结概要 本文主要针对2k1000的以太网及串口的国产化设计 整体架构流程 提示:这里可以添加技术整体架构 整体架构,以太网,使用2k1000自带的以太网mac控制器,外选用国产化PHY,国产化变压器。 整体框架,如下图,主要是器…

2023安卓逆向 -- JNI学习(从开发到反编译)

一、新建native C项目&#xff0c;填写好项目信息&#xff0c;一路下一步即可 二、创建好项目&#xff0c;直接点击运行&#xff0c;出现下面界面&#xff0c;说明我们的环境都没有问题 三、Java层调用java层函数 1、新建一个Java Class&#xff0c;命名为JavaFun 2、编写java…

维宏系统修改端口位置操作说明

1.关闭软件后找到Ncconfig.exe工具并打开 具体操作步骤 (1)桌面上鼠标点到 NcStudio鼠标右键-打开文件所在的位置如下图 (2) 在目录中找到NcConfig.exe的快捷方式&#xff08;黄色图标&#xff09; (3)打开并输入密码&#xff08;密码和软件密码一样默认初始密码ncstudio …

欧科云链:2023年5月链上安全事件盘点

一、基本信息 2023年5月安全事件约造成1800万美元损失&#xff0c;相比上月有显著下降&#xff0c;但安全事件发生频率并未减少。其中针对Jimbos Protocol的攻击造成了约750万美元损失。Arbitrum链的Swaprum项目Rug Pull造成了约300万美元的损失。此外&#xff0c;社交媒体钓鱼…

工业RFID解决方案怎么选?主要看这几项内容

如何选择适合您需求的RFID解决方案&#xff1f;通过深入了解需求、环境适应性、成本效益和供应商选择&#xff0c;您将能够更加全面地评估和选择适合自身需求的RFID解决方案。同时&#xff0c;不断与供应商和专业人员进行沟通和合作&#xff0c;可以获取更多关于特定解决方案的…

FineReport自定义排序

FineReport是帆软的报表开发工具&#xff0c;报表开发者可以用低代码的形式&#xff0c;配置出报表。主要适用于较简单的填报场景&#xff08;比如填写销售目标&#xff0c;维护项目映射关系等&#xff09;&#xff0c;用户可以在报表上填报数据&#xff0c;存储于数据库&#…

【TES605】基于Virtex-7 FPGA的高性能实时信号处理板

板卡概述 TES605是一款基于Virtex-7 FPGA的高性能实时信号处理平台&#xff0c;该平台采用1片TI的KeyStone系列多核DSP TMS320C6678作为主处理单元&#xff0c;采用1片Xilinx的Virtex-7系列FPGA XC7VX690T作为协处理单元&#xff0c;具有2个FMC子卡接口&#xff0c;各个处理节点…

PIC 使能485/422_Part2.2_激活485半双工(修订中...)

1.485底层协议辨识 黄色是PIC发出的&#xff0c;打头的字符是"\r\n\r\n"。绿色的是上位机485接口设备发送字符串“App1"的波形。波特率均为115200。上位机绿色的波形自发自收没有问题。 示波器设置为下降沿触发——485平时空闲状态&#xff0c;线路维持高电平&…

linux0.12-12-1-总体功能

[606页] 12-1 总体功能 本章所注释的程序量较大&#xff0c;但是通过第5章中对Linux源代码目录结构的分析&#xff08;参见图5-27&#xff09;&#xff0c; 可以把它们从功能上分为4个部分进行讨论。 第一部分是有关高速缓冲区的管理程序&#xff0c;主要实现了对硬盘等块设备…

隔离驱动芯片SLMi332兼容光耦隔离驱动器时的注意事项

数明深力科SLMi33x系列SLMi332是一款兼容光耦带DESAT保护功能的IGBT/SiC隔离驱动器。内置快速去饱和(DESAT) 故障检测功能、米勒钳位功能、漏极开路故障反馈、软关断功能以及可选择的自恢复模式&#xff0c;兼容光耦隔离驱动器。 SLMi33x系列SLMi332的DESAT阈值为6.5V&#xf…

Hive学习---4、函数

1、函数 1.1 函数简介 Hive会将常用的逻辑封装成函数给用户进行使用&#xff0c;类似java中的函数。 好处&#xff1a;避免用户反复写逻辑&#xff0c;可以直接拿来使用 重点&#xff1a;用户需要知道函数叫什么&#xff0c;能做什么 Hive提供了大量的内置函数&#xff0c;按…

ATTCK v13版本战术介绍——凭证访问(一)

一、引言 在前几期文章中我们介绍了ATT&CK中侦察、资源开发、初始访问、执行、持久化、提权、防御规避战术&#xff0c;本期我们为大家介绍ATT&CK 14项战术中凭证访问战术第1-6种子技术&#xff0c;后续会介绍凭证访问其他子技术&#xff0c;敬请关注。 二、ATT&C…

深度学习目标检测项目实战(六)-基于Faster rcnn pytorch的遥感图像检测

基于Faster rcnn pytorch的遥感图像检测 代码&#xff1a;https://github.com/jwyang/faster-rcnn.pytorch/tree/pytorch-1.0 数据集 使用RSOD遥感数据集&#xff0c;VOC的数据格式如下&#xff1a; RSOD是一个开放的目标检测数据集&#xff0c;用于遥感图像中的目标检测。…

01_pytorch中的DataSet

在pytorch 中&#xff0c; Dataset: 用于数据集的创建&#xff1b; DataLoader: 用于在训练过程中&#xff0c;传递获取一个batch的数据&#xff1b; 这里先介绍 pytorch 中的 Dataset 这个类&#xff0c; torch.utils.data. dataset.py 是一个表示数据集的抽象类。任何自定义…

SharpContour论文精读

SharpContour: A Contour-based Boundary Refinement Approach for Efficient and Accurate Instance Segmentation 论文链接&#xff1a;[2203.13312] SharpContour: A Contour-based Boundary Refinement Approach for Efficient and Accurate Instance Segmentation (arxiv…

[SpringBoot]Knife4j框架

Knife4j框架 Knife4j框架是一款国人开发的、基于Swagger 2的在线API文档框架。 Knife4j框架的一些主要作用和特点&#xff1a; 自动生成API文档&#xff1a;Knife4j可以根据代码中的注解和配置信息&#xff0c;自动生成API接口文档。开发者只需要在代码中添加相关注解&#…

数据治理服务解决方案word

本资料是ppt格式&#xff0c;适用于方案规划、项目实施、工作汇报。本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除。篇幅有限&#xff0c;无法完全展示&#xff0c;喜欢资料可转发评论&#xff0c;私信“方案”了解更多信息。…