using System; using System.Collections.Generic; using UnityEngine; using UnityEditor.ShaderGraph; using UnityEditor.ShaderGraph.Legacy; using static UnityEditor.Rendering.Universal.ShaderGraph.SubShaderUtils; using static Unity.Rendering.Universal.ShaderUtils; namespace UnityEditor.Rendering.Universal.ShaderGraph { sealed class UniversalUnlitSubTarget : UniversalSubTarget, ILegacyTarget { static readonly GUID kSourceCodeGuid = new GUID("97c3f7dcb477ec842aa878573640313a"); // UniversalUnlitSubTarget.cs public UniversalUnlitSubTarget() { displayName = "Unlit"; } protected override ShaderID shaderID => ShaderID.SG_Unlit; public override bool IsActive() => true; public override void Setup(ref TargetSetupContext context) { context.AddAssetDependency(kSourceCodeGuid, AssetCollection.Flags.SourceDependency); base.Setup(ref context); var universalRPType = typeof(UnityEngine.Rendering.Universal.UniversalRenderPipelineAsset); if (!context.HasCustomEditorForRenderPipeline(universalRPType)) { var gui = typeof(ShaderGraphUnlitGUI); #if HAS_VFX_GRAPH if (TargetsVFX()) gui = typeof(VFXShaderGraphUnlitGUI); #endif context.AddCustomEditorForRenderPipeline(gui.FullName, universalRPType); } // Process SubShaders context.AddSubShader(PostProcessSubShader(SubShaders.UnlitDOTS(target, target.renderType, target.renderQueue))); context.AddSubShader(PostProcessSubShader(SubShaders.Unlit(target, target.renderType, target.renderQueue))); } public override void ProcessPreviewMaterial(Material material) { if (target.allowMaterialOverride) { // copy our target's default settings into the material // (technically not necessary since we are always recreating the material from the shader each time, // which will pull over the defaults from the shader definition) // but if that ever changes, this will ensure the defaults are set material.SetFloat(Property.SurfaceType, (float)target.surfaceType); material.SetFloat(Property.BlendMode, (float)target.alphaMode); material.SetFloat(Property.AlphaClip, target.alphaClip ? 1.0f : 0.0f); material.SetFloat(Property.CullMode, (int)target.renderFace); material.SetFloat(Property.CastShadows, target.castShadows ? 1.0f : 0.0f); material.SetFloat(Property.ZWriteControl, (float)target.zWriteControl); material.SetFloat(Property.ZTest, (float)target.zTestMode); } // We always need these properties regardless of whether the material is allowed to override // Queue control & offset enable correct automatic render queue behavior // Control == 0 is automatic, 1 is user-specified render queue material.SetFloat(Property.QueueOffset, 0.0f); material.SetFloat(Property.QueueControl, (float)BaseShaderGUI.QueueControl.Auto); // call the full unlit material setup function ShaderGraphUnlitGUI.UpdateMaterial(material, MaterialUpdateType.CreatedNewMaterial); } public override void GetFields(ref TargetFieldContext context) { base.GetFields(ref context); } public override void GetActiveBlocks(ref TargetActiveBlockContext context) { context.AddBlock(BlockFields.SurfaceDescription.Alpha, (target.surfaceType == SurfaceType.Transparent || target.alphaClip) || target.allowMaterialOverride); context.AddBlock(BlockFields.SurfaceDescription.AlphaClipThreshold, target.alphaClip || target.allowMaterialOverride); } public override void CollectShaderProperties(PropertyCollector collector, GenerationMode generationMode) { if (target.allowMaterialOverride) { collector.AddFloatProperty(Property.CastShadows, target.castShadows ? 1.0f : 0.0f); collector.AddFloatProperty(Property.SurfaceType, (float)target.surfaceType); collector.AddFloatProperty(Property.BlendMode, (float)target.alphaMode); collector.AddFloatProperty(Property.AlphaClip, target.alphaClip ? 1.0f : 0.0f); collector.AddFloatProperty(Property.SrcBlend, 1.0f); // always set by material inspector collector.AddFloatProperty(Property.DstBlend, 0.0f); // always set by material inspector collector.AddToggleProperty(Property.ZWrite, (target.surfaceType == SurfaceType.Opaque)); collector.AddFloatProperty(Property.ZWriteControl, (float)target.zWriteControl); collector.AddFloatProperty(Property.ZTest, (float)target.zTestMode); // ztest mode is designed to directly pass as ztest collector.AddFloatProperty(Property.CullMode, (float)target.renderFace); // render face enum is designed to directly pass as a cull mode } // We always need these properties regardless of whether the material is allowed to override other shader properties. // Queue control & offset enable correct automatic render queue behavior. Control == 0 is automatic, 1 is user-specified. // We initialize queue control to -1 to indicate to UpdateMaterial that it needs to initialize it properly on the material. collector.AddFloatProperty(Property.QueueOffset, 0.0f); collector.AddFloatProperty(Property.QueueControl, -1.0f); } public override void GetPropertiesGUI(ref TargetPropertyGUIContext context, Action onChange, Action registerUndo) { var universalTarget = (target as UniversalTarget); universalTarget.AddDefaultMaterialOverrideGUI(ref context, onChange, registerUndo); universalTarget.AddDefaultSurfacePropertiesGUI(ref context, onChange, registerUndo, showReceiveShadows: false); } public bool TryUpgradeFromMasterNode(IMasterNode1 masterNode, out Dictionary blockMap) { blockMap = null; if (!(masterNode is UnlitMasterNode1 unlitMasterNode)) return false; // Set blockmap blockMap = new Dictionary() { { BlockFields.VertexDescription.Position, 9 }, { BlockFields.VertexDescription.Normal, 10 }, { BlockFields.VertexDescription.Tangent, 11 }, { BlockFields.SurfaceDescription.BaseColor, 0 }, { BlockFields.SurfaceDescription.Alpha, 7 }, { BlockFields.SurfaceDescription.AlphaClipThreshold, 8 }, }; return true; } #region SubShader static class SubShaders { public static SubShaderDescriptor Unlit(UniversalTarget target, string renderType, string renderQueue) { var result = new SubShaderDescriptor() { pipelineTag = UniversalTarget.kPipelineTag, customTags = UniversalTarget.kUnlitMaterialTypeTag, renderType = renderType, renderQueue = renderQueue, generatesPreview = true, passes = new PassCollection() }; result.passes.Add(UnlitPasses.Forward(target)); if (target.mayWriteDepth) result.passes.Add(CorePasses.DepthOnly(target)); result.passes.Add(CorePasses.DepthNormalOnly(target)); if (target.castShadows || target.allowMaterialOverride) result.passes.Add(CorePasses.ShadowCaster(target)); // Currently neither of these passes (selection/picking) can be last for the game view for // UI shaders to render correctly. Verify [1352225] before changing this order. result.passes.Add(CorePasses.SceneSelection(target)); result.passes.Add(CorePasses.ScenePicking(target)); result.passes.Add(UnlitPasses.DepthNormalOnly(target)); return result; } public static SubShaderDescriptor UnlitDOTS(UniversalTarget target, string renderType, string renderQueue) { var result = new SubShaderDescriptor() { pipelineTag = UniversalTarget.kPipelineTag, customTags = UniversalTarget.kUnlitMaterialTypeTag, renderType = renderType, renderQueue = renderQueue, generatesPreview = true, passes = new PassCollection() }; result.passes.Add(PassVariant(UnlitPasses.Forward(target), CorePragmas.DOTSForward)); if (target.mayWriteDepth) result.passes.Add(PassVariant(CorePasses.DepthOnly(target), CorePragmas.DOTSInstanced)); result.passes.Add(PassVariant(CorePasses.DepthNormalOnly(target), CorePragmas.DOTSInstanced)); if (target.castShadows || target.allowMaterialOverride) result.passes.Add(PassVariant(CorePasses.ShadowCaster(target), CorePragmas.DOTSInstanced)); // Currently neither of these passes (selection/picking) can be last for the game view for // UI shaders to render correctly. Verify [1352225] before changing this order. result.passes.Add(PassVariant(CorePasses.SceneSelection(target), CorePragmas.DOTSDefault)); result.passes.Add(PassVariant(CorePasses.ScenePicking(target), CorePragmas.DOTSDefault)); result.passes.Add(PassVariant(UnlitPasses.DepthNormalOnly(target), CorePragmas.DOTSInstanced)); return result; } } #endregion #region Pass static class UnlitPasses { public static PassDescriptor Forward(UniversalTarget target) { var result = new PassDescriptor { // Definition displayName = "Universal Forward", referenceName = "SHADERPASS_UNLIT", useInPreview = true, // Template passTemplatePath = UniversalTarget.kUberTemplatePath, sharedTemplateDirectories = UniversalTarget.kSharedTemplateDirectories, // Port Mask validVertexBlocks = CoreBlockMasks.Vertex, validPixelBlocks = CoreBlockMasks.FragmentColorAlpha, // Fields structs = CoreStructCollections.Default, requiredFields = UnlitRequiredFields.Unlit, fieldDependencies = CoreFieldDependencies.Default, // Conditional State renderStates = CoreRenderStates.UberSwitchedRenderState(target), pragmas = CorePragmas.Forward, defines = new DefineCollection() { CoreDefines.UseFragmentFog }, keywords = new KeywordCollection() { UnlitKeywords.UnlitBaseKeywords }, includes = UnlitIncludes.Unlit, // Custom Interpolator Support customInterpolators = CoreCustomInterpDescriptors.Common }; CorePasses.AddTargetSurfaceControlsToPass(ref result, target); return result; } public static PassDescriptor DepthNormalOnly(UniversalTarget target) { var result = new PassDescriptor { // Definition displayName = "DepthNormals", referenceName = "SHADERPASS_DEPTHNORMALSONLY", lightMode = "DepthNormalsOnly", useInPreview = false, // Template passTemplatePath = UniversalTarget.kUberTemplatePath, sharedTemplateDirectories = UniversalTarget.kSharedTemplateDirectories, // Port Mask validVertexBlocks = CoreBlockMasks.Vertex, validPixelBlocks = UnlitBlockMasks.FragmentDepthNormals, // Fields structs = CoreStructCollections.Default, requiredFields = UnlitRequiredFields.DepthNormalsOnly, fieldDependencies = CoreFieldDependencies.Default, // Conditional State renderStates = CoreRenderStates.DepthNormalsOnly(target), pragmas = CorePragmas.Forward, defines = new DefineCollection(), keywords = new KeywordCollection(), includes = CoreIncludes.DepthNormalsOnly, // Custom Interpolator Support customInterpolators = CoreCustomInterpDescriptors.Common }; CorePasses.AddTargetSurfaceControlsToPass(ref result, target); return result; } #region PortMasks static class UnlitBlockMasks { public static readonly BlockFieldDescriptor[] FragmentDepthNormals = new BlockFieldDescriptor[] { BlockFields.SurfaceDescription.NormalWS, BlockFields.SurfaceDescription.Alpha, BlockFields.SurfaceDescription.AlphaClipThreshold, }; } #endregion #region RequiredFields static class UnlitRequiredFields { public static readonly FieldCollection Unlit = new FieldCollection() { StructFields.Varyings.positionWS, StructFields.Varyings.normalWS, StructFields.Varyings.viewDirectionWS, }; public static readonly FieldCollection DepthNormalsOnly = new FieldCollection() { StructFields.Varyings.normalWS, }; } #endregion } #endregion #region Keywords static class UnlitKeywords { public static readonly KeywordCollection UnlitBaseKeywords = new KeywordCollection() { // This contain lightmaps because without a proper custom lighting solution in Shadergraph, // people start with the unlit then add lightmapping nodes to it. // If we removed lightmaps from the unlit target this would ruin a lot of peoples days. CoreKeywordDescriptors.StaticLightmap, CoreKeywordDescriptors.DirectionalLightmapCombined, CoreKeywordDescriptors.SampleGI, CoreKeywordDescriptors.DBuffer, CoreKeywordDescriptors.DebugDisplay, }; } #endregion #region Includes static class UnlitIncludes { const string kUnlitPass = "Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/UnlitPass.hlsl"; public static IncludeCollection Unlit = new IncludeCollection { // Pre-graph { CoreIncludes.CorePregraph }, { CoreIncludes.ShaderGraphPregraph }, { CoreIncludes.DBufferPregraph }, // Post-graph { CoreIncludes.CorePostgraph }, { kUnlitPass, IncludeLocation.Postgraph }, }; } #endregion } }