diff --git a/Assets/Resources/Shaders/VpeUrpResolverVariants.shadervariants b/Assets/Resources/Shaders/VpeUrpResolverVariants.shadervariants new file mode 100644 index 0000000..cfaa112 --- /dev/null +++ b/Assets/Resources/Shaders/VpeUrpResolverVariants.shadervariants @@ -0,0 +1,334 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!200 &20000000 +ShaderVariantCollection: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: VpeUrpResolverVariants + m_Shaders: + - first: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + second: + variants: + - keywords: + passType: 8 + - keywords: _ALPHATEST_ON + passType: 8 + - keywords: + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP + _OCCLUSIONMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP + _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _EMISSION _METALLICSPECGLOSSMAP _OCCLUSIONMAP + _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _EMISSION _METALLICSPECGLOSSMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _EMISSION _NORMALMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _EMISSION _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP + _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _METALLICSPECGLOSSMAP _NORMALMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _METALLICSPECGLOSSMAP _OCCLUSIONMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _METALLICSPECGLOSSMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _NORMALMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHATEST_ON + passType: 13 + - keywords: _ALPHATEST_ON _EMISSION + passType: 13 + - keywords: _ALPHATEST_ON _EMISSION _METALLICSPECGLOSSMAP + passType: 13 + - keywords: _ALPHATEST_ON _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP + passType: 13 + - keywords: _ALPHATEST_ON _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP + passType: 13 + - keywords: _ALPHATEST_ON _EMISSION _METALLICSPECGLOSSMAP _OCCLUSIONMAP + passType: 13 + - keywords: _ALPHATEST_ON _EMISSION _NORMALMAP + passType: 13 + - keywords: _ALPHATEST_ON _METALLICSPECGLOSSMAP + passType: 13 + - keywords: _ALPHATEST_ON _METALLICSPECGLOSSMAP _NORMALMAP + passType: 13 + - keywords: _ALPHATEST_ON _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP + passType: 13 + - keywords: _ALPHATEST_ON _METALLICSPECGLOSSMAP _OCCLUSIONMAP + passType: 13 + - keywords: _ALPHATEST_ON _NORMALMAP + passType: 13 + - keywords: _EMISSION + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _OCCLUSIONMAP + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _OCCLUSIONMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _EMISSION _NORMALMAP + passType: 13 + - keywords: _EMISSION _NORMALMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _EMISSION _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _METALLICSPECGLOSSMAP + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _NORMALMAP + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _NORMALMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _OCCLUSIONMAP + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _OCCLUSIONMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _NORMALMAP + passType: 13 + - keywords: _NORMALMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _SURFACE_TYPE_TRANSPARENT + passType: 13 + - first: {fileID: 4800000, guid: 5c8e21bce15211d45b491dce08046aae, type: 3} + second: + variants: + - keywords: + passType: 8 + - keywords: _ALPHATEST_ON + passType: 8 + - keywords: + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP + _OCCLUSIONMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP + _OCCLUSIONMAP _SURFACE_TYPE_TRANSPARENT _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP + _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP + _SURFACE_TYPE_TRANSPARENT _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _EMISSION _METALLICSPECGLOSSMAP _OCCLUSIONMAP + _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _EMISSION _METALLICSPECGLOSSMAP _OCCLUSIONMAP + _SURFACE_TYPE_TRANSPARENT _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _EMISSION _METALLICSPECGLOSSMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _EMISSION _METALLICSPECGLOSSMAP _SURFACE_TYPE_TRANSPARENT + _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _EMISSION _NORMALMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _EMISSION _NORMALMAP _SURFACE_TYPE_TRANSPARENT + _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _EMISSION _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _EMISSION _SURFACE_TYPE_TRANSPARENT _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP + _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP + _SURFACE_TYPE_TRANSPARENT _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _METALLICSPECGLOSSMAP _NORMALMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _METALLICSPECGLOSSMAP _NORMALMAP _SURFACE_TYPE_TRANSPARENT + _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _METALLICSPECGLOSSMAP _OCCLUSIONMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _METALLICSPECGLOSSMAP _OCCLUSIONMAP _SURFACE_TYPE_TRANSPARENT + _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _METALLICSPECGLOSSMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _METALLICSPECGLOSSMAP _SURFACE_TYPE_TRANSPARENT + _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _NORMALMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _NORMALMAP _SURFACE_TYPE_TRANSPARENT _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _ALPHAPREMULTIPLY_ON _SURFACE_TYPE_TRANSPARENT _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHATEST_ON + passType: 13 + - keywords: _ALPHATEST_ON _EMISSION + passType: 13 + - keywords: _ALPHATEST_ON _EMISSION _METALLICSPECGLOSSMAP + passType: 13 + - keywords: _ALPHATEST_ON _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP + passType: 13 + - keywords: _ALPHATEST_ON _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP + passType: 13 + - keywords: _ALPHATEST_ON _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP + _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHATEST_ON _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHATEST_ON _EMISSION _METALLICSPECGLOSSMAP _OCCLUSIONMAP + passType: 13 + - keywords: _ALPHATEST_ON _EMISSION _METALLICSPECGLOSSMAP _OCCLUSIONMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHATEST_ON _EMISSION _METALLICSPECGLOSSMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHATEST_ON _EMISSION _NORMALMAP + passType: 13 + - keywords: _ALPHATEST_ON _EMISSION _NORMALMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHATEST_ON _EMISSION _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHATEST_ON _METALLICSPECGLOSSMAP + passType: 13 + - keywords: _ALPHATEST_ON _METALLICSPECGLOSSMAP _NORMALMAP + passType: 13 + - keywords: _ALPHATEST_ON _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP + passType: 13 + - keywords: _ALPHATEST_ON _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHATEST_ON _METALLICSPECGLOSSMAP _NORMALMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHATEST_ON _METALLICSPECGLOSSMAP _OCCLUSIONMAP + passType: 13 + - keywords: _ALPHATEST_ON _METALLICSPECGLOSSMAP _OCCLUSIONMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHATEST_ON _METALLICSPECGLOSSMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHATEST_ON _NORMALMAP + passType: 13 + - keywords: _ALPHATEST_ON _NORMALMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _ALPHATEST_ON _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _EMISSION + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP _SURFACE_TYPE_TRANSPARENT + _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP _SURFACE_TYPE_TRANSPARENT + _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _NORMALMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _OCCLUSIONMAP + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _OCCLUSIONMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _OCCLUSIONMAP _SURFACE_TYPE_TRANSPARENT + _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _OCCLUSIONMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _SURFACE_TYPE_TRANSPARENT _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _EMISSION _METALLICSPECGLOSSMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _EMISSION _NORMALMAP + passType: 13 + - keywords: _EMISSION _NORMALMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _EMISSION _NORMALMAP _SURFACE_TYPE_TRANSPARENT _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _EMISSION _NORMALMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _EMISSION _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _EMISSION _SURFACE_TYPE_TRANSPARENT _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _EMISSION _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _METALLICSPECGLOSSMAP + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _NORMALMAP + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP _SURFACE_TYPE_TRANSPARENT + _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _NORMALMAP _OCCLUSIONMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _NORMALMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _NORMALMAP _SURFACE_TYPE_TRANSPARENT _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _NORMALMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _OCCLUSIONMAP + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _OCCLUSIONMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _OCCLUSIONMAP _SURFACE_TYPE_TRANSPARENT _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _OCCLUSIONMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _SURFACE_TYPE_TRANSPARENT _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _METALLICSPECGLOSSMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _NORMALMAP + passType: 13 + - keywords: _NORMALMAP _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _NORMALMAP _SURFACE_TYPE_TRANSPARENT _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _NORMALMAP _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _SURFACE_TYPE_TRANSPARENT + passType: 13 + - keywords: _SURFACE_TYPE_TRANSPARENT _VPE_MAPPING_PLANAR + passType: 13 + - keywords: _VPE_MAPPING_PLANAR + passType: 13 diff --git a/Assets/Resources/Shaders/VpeUrpResolverVariants.shadervariants.meta b/Assets/Resources/Shaders/VpeUrpResolverVariants.shadervariants.meta new file mode 100644 index 0000000..5d8ca31 --- /dev/null +++ b/Assets/Resources/Shaders/VpeUrpResolverVariants.shadervariants.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5e2e8d964077c764194d8a1a8fb7b117 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 20000000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/UrpShaderVariants.cs b/Editor/UrpShaderVariants.cs new file mode 100644 index 0000000..085e285 --- /dev/null +++ b/Editor/UrpShaderVariants.cs @@ -0,0 +1,197 @@ +// Visual Pinball Engine +// Copyright (C) 2026 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.Rendering; + +namespace VisualPinball.Unity.Urp +{ + /// + /// The shader variants needs in a player build. + /// + /// + /// + /// The resolver builds URP/Lit and triplanar materials at runtime and switches shader_feature keywords + /// (surface type, mask map, normal map, ...). A build only compiles the shader_feature variants that + /// materials or variant collections in the build use, and no material in the build uses most of these. + /// Without them, the runtime materials fall back to stripped variants: mask-driven metals turn into white + /// dielectrics, transparent triplanar plastics lose their look. This collection lists every keyword set + /// the resolver can produce, and lives in Resources so every URP build includes it. + /// + public static class UrpShaderVariants + { + public const string CollectionPath = "Packages/org.visualpinball.engine.unity.urp/Assets/Resources/Shaders/VpeUrpResolverVariants.shadervariants"; + + private const string LitShaderName = "Universal Render Pipeline/Lit"; + private const string TriplanarShaderPath = "Packages/org.visualpinball.engine.unity.urp/Assets/Resources/Shaders/VpeUrpLitTriplanar.shader"; + + // Surface states the resolver sets (ApplySurface, ApplyBlend, BuildDecal, BuildInsert). + private static readonly string[][] Surfaces = { + new string[0], + new[] { "_ALPHATEST_ON" }, + new[] { "_SURFACE_TYPE_TRANSPARENT" }, + new[] { "_SURFACE_TYPE_TRANSPARENT", "_ALPHAPREMULTIPLY_ON" }, + }; + + // Mask map states (ApplyMaskMap): none, mask, mask with occlusion. + private static readonly string[][] Masks = { + new string[0], + new[] { "_METALLICSPECGLOSSMAP" }, + new[] { "_METALLICSPECGLOSSMAP", "_OCCLUSIONMAP" }, + }; + + private static readonly string[] Toggles = { "_NORMALMAP", "_EMISSION" }; + + // The shadow caster pass only has the alpha test. + private static readonly string[][] ShadowKeywordSets = { new string[0], new[] { "_ALPHATEST_ON" } }; + + // UrpProjectedMapping: triplanar without, planar with this keyword. + private const string PlanarKeyword = "_VPE_MAPPING_PLANAR"; + + [MenuItem("VPE/Shaders/Generate URP Resolver Variants")] + public static void Generate() + { + var lit = Shader.Find(LitShaderName); + var triplanar = AssetDatabase.LoadAssetAtPath(TriplanarShaderPath); + if (!lit || !triplanar) { + Debug.LogError($"UrpShaderVariants: missing shader ({LitShaderName}: {(bool)lit}, triplanar: {(bool)triplanar})."); + return; + } + + var collection = new ShaderVariantCollection { name = System.IO.Path.GetFileNameWithoutExtension(CollectionPath) }; + var added = Add(collection, lit, PassType.ScriptableRenderPipeline, KeywordSets(false)) + + Add(collection, triplanar, PassType.ScriptableRenderPipeline, KeywordSets(true)) + + Add(collection, lit, PassType.ShadowCaster, ShadowKeywordSets) + + Add(collection, triplanar, PassType.ShadowCaster, ShadowKeywordSets); + + var existing = AssetDatabase.LoadAssetAtPath(CollectionPath); + if (existing) { + EditorUtility.CopySerialized(collection, existing); + EditorUtility.SetDirty(existing); + Object.DestroyImmediate(collection); + } else { + AssetDatabase.CreateAsset(collection, CollectionPath); + } + AssetDatabase.SaveAssets(); + Debug.Log($"UrpShaderVariants: {added} variants written to {CollectionPath}."); + } + + /// + /// Every shader_feature keyword set the resolver can give a material of the given shader. + /// + public static IEnumerable KeywordSets(bool triplanar) + { + var mappings = triplanar ? new[] { new string[0], new[] { PlanarKeyword } } : new[] { new string[0] }; + foreach (var mapping in mappings) { + foreach (var surface in Surfaces) { + foreach (var mask in Masks) { + for (var toggles = 0; toggles < 1 << Toggles.Length; toggles++) { + var keywords = new List(mapping); + keywords.AddRange(surface); + keywords.AddRange(mask); + keywords.AddRange(Toggles.Where((_, i) => (toggles & (1 << i)) != 0)); + yield return keywords.ToArray(); + } + } + } + } + } + + /// + /// The keyword sets the collection at doesn't have, or why it can't be checked. + /// + public static IEnumerable Missing() + { + var collection = AssetDatabase.LoadAssetAtPath(CollectionPath); + var lit = Shader.Find(LitShaderName); + var triplanar = AssetDatabase.LoadAssetAtPath(TriplanarShaderPath); + if (!collection || !lit || !triplanar) { + yield return $"{CollectionPath} or its shaders are missing"; + yield break; + } + foreach (var (shader, isTriplanar) in new[] { (lit, false), (triplanar, true) }) { + var sets = KeywordSets(isTriplanar).Select(k => (PassType.ScriptableRenderPipeline, k)) + .Concat(ShadowKeywordSets.Select(k => (PassType.ShadowCaster, k))); + foreach (var (passType, keywords) in sets) { + ShaderVariantCollection.ShaderVariant variant; + try { + variant = new ShaderVariantCollection.ShaderVariant(shader, passType, keywords); + } catch (System.ArgumentException) { + continue; // not a valid set for this shader, Generate skips it as well + } + if (!collection.Contains(variant)) { + yield return $"{shader.name} [{string.Join(" ", keywords)}]"; + } + } + } + } + + private static int Add(ShaderVariantCollection collection, Shader shader, PassType passType, IEnumerable keywordSets) + { + var added = 0; + foreach (var keywords in keywordSets) { + // all URP passes but the shadow caster (and motion vectors) are of the SRP type + try { + if (collection.Add(new ShaderVariantCollection.ShaderVariant(shader, passType, keywords))) { + added++; + } + } catch (System.ArgumentException e) { + Debug.LogWarning($"UrpShaderVariants: {shader.name} [{string.Join(" ", keywords)}]: {e.Message}"); + } + } + return added; + } + } + + /// + /// Fails URP player builds whose resolver variant collection is missing or out of date, since the build would + /// render runtime-built materials with stripped variants. + /// + public class UrpShaderVariantsBuildGuard : UnityEditor.Build.IPreprocessBuildWithReport + { + public int callbackOrder => 0; + + public void OnPreprocessBuild(UnityEditor.Build.Reporting.BuildReport report) + { + if (!UsesUrp()) { + return; + } + var missing = UrpShaderVariants.Missing().ToList(); + if (missing.Count > 0) { + throw new UnityEditor.Build.BuildFailedException( + $"VPE: the URP resolver's shader variant collection lacks {missing.Count} variant(s), e.g. {missing[0]}. " + + "Run VPE > Shaders > Generate URP Resolver Variants and commit the result."); + } + } + + // the default pipeline, or the one a quality level overrides it with + private static bool UsesUrp() + { + if (GraphicsSettings.defaultRenderPipeline is UnityEngine.Rendering.Universal.UniversalRenderPipelineAsset) { + return true; + } + for (var i = 0; i < QualitySettings.names.Length; i++) { + if (QualitySettings.GetRenderPipelineAssetAt(i) is UnityEngine.Rendering.Universal.UniversalRenderPipelineAsset) { + return true; + } + } + return false; + } + } +} diff --git a/Editor/UrpShaderVariants.cs.meta b/Editor/UrpShaderVariants.cs.meta new file mode 100644 index 0000000..6d027a3 --- /dev/null +++ b/Editor/UrpShaderVariants.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ff6930a2473298540a04a871666398f0 \ No newline at end of file