【Unity3D】URP中的打包资源加载

  当项目打包后,Shader.Find寻找Shader的方式可能会失效,原因是当前项目可能没有引用此Shader。
  一般解决方式,是Edit/Project Settings/Graphics,将Shader加入到Always Includes Shaders中,我不喜欢这种方法,因为Always Includes Shaders一般是加载那些Hidden Shader的。所以我是新建一个材质球引用Shader,并把材质球拖到需要用到的位置。
  值得注意的是,URP中的Shader也能在打包后构建材质,我找到Shader的引用位置:

[Serializable, ReloadGroup]
public sealed class ShaderResources
{
    [Reload("Shaders/PostProcessing/StopNaN.shader")]
    public Shader stopNanPS;

    [Reload("Shaders/PostProcessing/SubpixelMorphologicalAntialiasing.shader")]
    public Shader subpixelMorphologicalAntialiasingPS;

    [Reload("Shaders/PostProcessing/GaussianDepthOfField.shader")]
    public Shader gaussianDepthOfFieldPS;
//....

  用Reload标注的地址,这个路径基于URP Package地址。
  点进去能看到ReloadAttribute,就是把路径存储起来。
  ShaderResources类被PostProcessData引用:

public class PostProcessData : ScriptableObject
{
//....
    public ShaderResources shaders;
//...

  这是一个ScriptableObject,对象可以被存储为asset文件。
  存储、加载过程是在ForawdRendererData中,ForwardRendererData本身也是一个ScriptableObject,它包含了一个PostProcessData对象,当Create时,它会先从文件中读取自己的属性,然后再从文件读取PostProcessData对象的属性:

//ForwardRendererData
protected override ScriptableRenderer Create()
{
#if UNITY_EDITOR
    if (!Application.isPlaying)
    {
        ResourceReloader.TryReloadAllNullIn(this, UniversalRenderPipelineAsset.packagePath);
        ResourceReloader.TryReloadAllNullIn(postProcessData, UniversalRenderPipelineAsset.packagePath);
    }
#endif
    return new ForwardRenderer(this);
}

  ResourceReloader.TryReloadAllNullIn间接调用ReloadAllNullIn:

public static bool ReloadAllNullIn(System.Object container, string basePath)
{
    if (IsNull(container))
        return false;

    var changed = false;
    foreach (var fieldInfo in container.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static))
    {
        //Recurse on sub-containers
        // 如果标注了ReloadGroup属性
        if (IsReloadGroup(fieldInfo))
        {
            changed |= FixGroupIfNeeded(container, fieldInfo);
            changed |= ReloadAllNullIn(fieldInfo.GetValue(container), basePath);
        }

        //Find null field and reload them
        //得到ReloadAttribute
        var attribute = GetReloadAttribute(fieldInfo);
        if (attribute != null)
        {
            if (attribute.paths.Length == 1)
            {
                //如果container.field是null,则从文件中读取并赋值给field
                changed |= SetAndLoadIfNull(container, fieldInfo, GetFullPath(basePath, attribute),
                    attribute.package == ReloadAttribute.Package.Builtin);
            }
//...

  当ForwardRendererData调用ResourceReloader.TryReloadAllNullIn(this……时,先对ForwardRendererData对象中的每个有Reload的成员赋值、寻找路径,其中postProcessData成员是这样的:

[Reload("Runtime/Data/PostProcessData.asset")]
public PostProcessData postProcessData = null;

  PostProcessData.asset中存储了后处理需要的Shader和纹理,直接点看不见,是因为PostProcessDataEditor.cs中进行了开发模式判断,注释掉就好:

[CustomEditor(typeof(PostProcessData), true)]
public class PostProcessDataEditor : Editor
{
    SerializedProperty m_Shaders;
    SerializedProperty m_Textures;

    private void OnEnable()
    {
        m_Shaders = serializedObject.FindProperty("shaders");
        m_Textures = serializedObject.FindProperty("textures");
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();

        // Add a "Reload All" button in inspector when we are in developer's mode
        // 这里注释掉
        //if (EditorPrefs.GetBool("DeveloperMode"))
        //{
            EditorGUILayout.Space();
            EditorGUILayout.PropertyField(m_Shaders, true);
            EditorGUILayout.PropertyField(m_Textures, true);

            if (GUILayout.Button("Reload All"))
            {
                var resources = target as PostProcessData;
                resources.shaders = null;
                resources.textures = null;
                ResourceReloader.ReloadAllNullIn(target, UniversalRenderPipelineAsset.packagePath);
            }
        //}

        serializedObject.ApplyModifiedProperties();
    }
}

  能看到:


  如果头一次加载,这个对象中的成员都是没有的,所以ForwardRendererData又调用了ResourceReloader.TryReloadAllNullIn(postProcessData……,让postProcessData根据成员Reload标注的路径,将null成员赋值。
  当我们改代码时,就会重新调用Reload,重新加载一遍这些ScriptableObject,而打包时,因为存在引用这些资源的对象,这些资源也会被打包进去。
  不过有一点要注意,加载这些资源的方法SetAndLoadIfNull,顾名思义,只有资源是Null时才会load,所以我们单纯更改Reload的地址,重新调用这一系列流程时,Unity会判断变量不为Null,就不会重新寻找资源。我们可以通过改写代码的方式,或手动设置为Null的方式来解决这个问题。

你可能感兴趣的:(【Unity3D】URP中的打包资源加载)