using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Rendering.HighDefinition; using UnityEditor.ShaderGraph; using UnityEditor.ShaderGraph.Internal; using UnityEditor.Graphing; using UnityEditor.ShaderGraph.Legacy; using UnityEditor.Rendering.HighDefinition.ShaderGraph.Legacy; using static UnityEngine.Rendering.HighDefinition.HDMaterialProperties; using static UnityEditor.Rendering.HighDefinition.HDShaderUtils; namespace UnityEditor.Rendering.HighDefinition.ShaderGraph { abstract class SurfaceSubTarget : HDSubTarget, IRequiresData { BuiltinData m_BuiltinData; // Interface Properties BuiltinData IRequiresData.data { get => m_BuiltinData; set => m_BuiltinData = value; } public BuiltinData builtinData { get => m_BuiltinData; set => m_BuiltinData = value; } protected override string renderQueue { get => HDRenderQueue.GetShaderTagValue(HDRenderQueue.ChangeType(systemData.renderQueueType, systemData.sortPriority, systemData.alphaTest, false)); } protected override string templatePath => $"{HDUtils.GetHDRenderPipelinePath()}Editor/Material/ShaderGraph/Templates/ShaderPass.template"; protected virtual bool supportForward => false; protected virtual bool supportLighting => false; protected virtual bool supportDistortion => false; protected override bool supportRaytracing => true; protected override int ComputeMaterialNeedsUpdateHash() { // Alpha test is currently the only property in buitin data to trigger the material upgrade script. int hash = systemData.alphaTest.GetHashCode(); return hash; } static readonly GUID kSourceCodeGuid = new GUID("f4df7e8f9b8c23648ae50cbca0221e47"); // SurfaceSubTarget.cs public override void Setup(ref TargetSetupContext context) { context.AddAssetDependency(kSourceCodeGuid, AssetCollection.Flags.SourceDependency); base.Setup(ref context); } protected override IEnumerable EnumerateSubShaders() { yield return PostProcessSubShader(GetSubShaderDescriptor()); if (supportRaytracing || supportPathtracing) yield return PostProcessSubShader(GetRaytracingSubShaderDescriptor()); } protected virtual SubShaderDescriptor GetSubShaderDescriptor() { return new SubShaderDescriptor { generatesPreview = true, passes = GetPasses() }; PassCollection GetPasses() { var passes = new PassCollection { // Common "surface" passes HDShaderPasses.GenerateShadowCaster(supportLighting), HDShaderPasses.GenerateMETA(supportLighting), HDShaderPasses.GenerateScenePicking(), HDShaderPasses.GenerateSceneSelection(supportLighting), HDShaderPasses.GenerateMotionVectors(supportLighting, supportForward), { HDShaderPasses.GenerateBackThenFront(supportLighting), new FieldCondition(HDFields.TransparentBackFace, true)}, { HDShaderPasses.GenerateTransparentDepthPostpass(supportLighting), new FieldCondition(HDFields.TransparentDepthPostPass, true)} }; if (supportLighting) { // We always generate the TransparentDepthPrepass as it can be use with SSR transparent passes.Add(HDShaderPasses.GenerateTransparentDepthPrepass(true)); } else { // We only generate the pass if requested passes.Add(HDShaderPasses.GenerateTransparentDepthPrepass(false), new FieldCondition(HDFields.TransparentDepthPrePass, true)); } if (supportForward) { passes.Add(HDShaderPasses.GenerateDepthForwardOnlyPass(supportLighting)); passes.Add(HDShaderPasses.GenerateForwardOnlyPass(supportLighting)); } if (supportDistortion) passes.Add(HDShaderPasses.GenerateDistortionPass(supportLighting), new FieldCondition(HDFields.TransparentDistortion, true)); passes.Add(HDShaderPasses.GenerateFullScreenDebug()); return passes; } } protected virtual SubShaderDescriptor GetRaytracingSubShaderDescriptor() { return new SubShaderDescriptor { generatesPreview = false, passes = GetPasses(), }; PassCollection GetPasses() { var passes = new PassCollection(); if (supportRaytracing) { // Common "surface" raytracing passes passes.Add(HDShaderPasses.GenerateRaytracingIndirect(supportLighting)); passes.Add(HDShaderPasses.GenerateRaytracingVisibility(supportLighting)); passes.Add(HDShaderPasses.GenerateRaytracingForward(supportLighting)); passes.Add(HDShaderPasses.GenerateRaytracingGBuffer(supportLighting)); } ; if (supportPathtracing) passes.Add(HDShaderPasses.GeneratePathTracing(supportLighting)); return passes; } } protected override void CollectPassKeywords(ref PassDescriptor pass) { pass.keywords.Add(CoreKeywordDescriptors.AlphaTest, new FieldCondition(Fields.AlphaTest, true)); if (pass.IsDepthOrMV()) { pass.keywords.Add(CoreKeywordDescriptors.AlphaToMask, new FieldCondition(Fields.AlphaToMask, true)); pass.keywords.Add(CoreKeywordDescriptors.WriteMsaaDepth); } pass.keywords.Add(CoreKeywordDescriptors.SurfaceTypeTransparent); pass.keywords.Add(CoreKeywordDescriptors.BlendMode); pass.keywords.Add(CoreKeywordDescriptors.DoubleSided, new FieldCondition(HDFields.Unlit, false)); pass.keywords.Add(CoreKeywordDescriptors.DepthOffset, new FieldCondition(HDFields.DepthOffset, true)); pass.keywords.Add(CoreKeywordDescriptors.AddPrecomputedVelocity); pass.keywords.Add(CoreKeywordDescriptors.TransparentWritesMotionVector); pass.keywords.Add(CoreKeywordDescriptors.FogOnTransparent); if (pass.IsLightingOrMaterial()) pass.keywords.Add(CoreKeywordDescriptors.DebugDisplay); if (!pass.IsDXR()) pass.keywords.Add(CoreKeywordDescriptors.LodFadeCrossfade, new FieldCondition(Fields.LodCrossFade, true)); if (pass.lightMode == HDShaderPassNames.s_MotionVectorsStr) { if (supportForward) pass.defines.Add(CoreKeywordDescriptors.WriteNormalBuffer, 1, new FieldCondition(HDFields.Unlit, false)); else pass.keywords.Add(CoreKeywordDescriptors.WriteNormalBuffer, new FieldCondition(HDFields.Unlit, false)); } } public override void GetFields(ref TargetFieldContext context) { base.GetFields(ref context); if (supportDistortion) AddDistortionFields(ref context); // Mark the shader as unlit so we can remove lighting in FieldConditions if (!supportLighting) context.AddField(HDFields.Unlit); // Common properties between all "surface" master nodes (everything except decal right now) context.AddField(HDStructFields.FragInputs.IsFrontFace, systemData.doubleSidedMode != DoubleSidedMode.Disabled && context.pass.referenceName != "SHADERPASS_MOTION_VECTORS"); // Double Sided context.AddField(HDFields.DoubleSided, systemData.doubleSidedMode != DoubleSidedMode.Disabled); // We always generate the keyword ALPHATEST_ON. All the variant of AlphaClip (shadow, pre/postpass) are only available if alpha test is on. context.AddField(Fields.AlphaTest, systemData.alphaTest && (context.pass.validPixelBlocks.Contains(BlockFields.SurfaceDescription.AlphaClipThreshold) || context.pass.validPixelBlocks.Contains(HDBlockFields.SurfaceDescription.AlphaClipThresholdShadow) || context.pass.validPixelBlocks.Contains(HDBlockFields.SurfaceDescription.AlphaClipThresholdDepthPrepass) || context.pass.validPixelBlocks.Contains(HDBlockFields.SurfaceDescription.AlphaClipThresholdDepthPostpass))); // All the DoAlphaXXX field drive the generation of which code to use for alpha test in the template // Regular alpha test is only done if artist haven't ask to use the specific alpha test shadow one bool isShadowPass = (context.pass.lightMode == "ShadowCaster") || (context.pass.lightMode == "VisibilityDXR"); bool isTransparentDepthPrepass = context.pass.lightMode == "TransparentDepthPrepass"; // Shadow use the specific alpha test only if user have ask to override it context.AddField(HDFields.DoAlphaTestShadow, systemData.alphaTest && builtinData.alphaTestShadow && isShadowPass && context.pass.validPixelBlocks.Contains(HDBlockFields.SurfaceDescription.AlphaClipThresholdShadow)); // Pre/post pass always use the specific alpha test provided for those pass context.AddField(HDFields.DoAlphaTestPrepass, systemData.alphaTest && builtinData.transparentDepthPrepass && isTransparentDepthPrepass && context.pass.validPixelBlocks.Contains(HDBlockFields.SurfaceDescription.AlphaClipThresholdDepthPrepass)); // Features & Misc context.AddField(Fields.LodCrossFade, builtinData.supportLodCrossFade); context.AddField(Fields.AlphaToMask, systemData.alphaTest); context.AddField(HDFields.TransparentBackFace, builtinData.backThenFrontRendering); context.AddField(HDFields.TransparentDepthPrePass, builtinData.transparentDepthPrepass); context.AddField(HDFields.TransparentDepthPostPass, builtinData.transparentDepthPostpass); context.AddField(HDFields.DepthOffset, builtinData.depthOffset && context.pass.validPixelBlocks.Contains(HDBlockFields.SurfaceDescription.DepthOffset)); // Depth offset needs positionRWS and is now a multi_compile if (builtinData.depthOffset) context.AddField(HDStructFields.FragInputs.positionRWS); } protected void AddDistortionFields(ref TargetFieldContext context) { // Distortion context.AddField(HDFields.DistortionDepthTest, builtinData.distortionDepthTest); context.AddField(HDFields.DistortionAdd, builtinData.distortionMode == DistortionMode.Add); context.AddField(HDFields.DistortionMultiply, builtinData.distortionMode == DistortionMode.Multiply); context.AddField(HDFields.DistortionReplace, builtinData.distortionMode == DistortionMode.Replace); context.AddField(HDFields.TransparentDistortion, systemData.surfaceType != SurfaceType.Opaque && builtinData.distortion); } public override void GetActiveBlocks(ref TargetActiveBlockContext context) { if (supportDistortion) AddDistortionBlocks(ref context); // Common block between all "surface" master nodes // Vertex context.AddBlock(BlockFields.VertexDescription.Position); context.AddBlock(BlockFields.VertexDescription.Normal); context.AddBlock(BlockFields.VertexDescription.Tangent); // Surface context.AddBlock(BlockFields.SurfaceDescription.BaseColor); context.AddBlock(BlockFields.SurfaceDescription.Emission); context.AddBlock(BlockFields.SurfaceDescription.Alpha); context.AddBlock(BlockFields.SurfaceDescription.AlphaClipThreshold, systemData.alphaTest); // Alpha Test context.AddBlock(HDBlockFields.SurfaceDescription.AlphaClipThresholdDepthPrepass, systemData.alphaTest && builtinData.transparentDepthPrepass); context.AddBlock(HDBlockFields.SurfaceDescription.AlphaClipThresholdDepthPostpass, systemData.alphaTest && builtinData.transparentDepthPostpass); context.AddBlock(HDBlockFields.SurfaceDescription.AlphaClipThresholdShadow, systemData.alphaTest && builtinData.alphaTestShadow); // Misc context.AddBlock(HDBlockFields.SurfaceDescription.DepthOffset, builtinData.depthOffset); } protected void AddDistortionBlocks(ref TargetActiveBlockContext context) { context.AddBlock(HDBlockFields.SurfaceDescription.Distortion, systemData.surfaceType == SurfaceType.Transparent && builtinData.distortion); context.AddBlock(HDBlockFields.SurfaceDescription.DistortionBlur, systemData.surfaceType == SurfaceType.Transparent && builtinData.distortion); } public override void GetPropertiesGUI(ref TargetPropertyGUIContext context, Action onChange, Action registerUndo) { var gui = new SubTargetPropertiesGUI(context, onChange, registerUndo, systemData, builtinData, null); AddInspectorPropertyBlocks(gui); context.Add(gui); } public override void CollectShaderProperties(PropertyCollector collector, GenerationMode generationMode) { // Trunk currently relies on checking material property "_EmissionColor" to allow emissive GI. If it doesn't find that property, or it is black, GI is forced off. // ShaderGraph doesn't use this property, so currently it inserts a dummy color (white). This dummy color may be removed entirely once the following PR has been merged in trunk: Pull request #74105 // The user will then need to explicitly disable emissive GI if it is not needed. // To be able to automatically disable emission based on the ShaderGraph config when emission is black, // we will need a more general way to communicate this to the engine (not directly tied to a material property). collector.AddShaderProperty(new ColorShaderProperty() { overrideReferenceName = "_EmissionColor", hidden = true, overrideHLSLDeclaration = true, hlslDeclarationOverride = HLSLDeclaration.UnityPerMaterial, value = new Color(1.0f, 1.0f, 1.0f, 1.0f) }); // ShaderGraph only property used to send the RenderQueueType to the material collector.AddShaderProperty(new Vector1ShaderProperty { overrideReferenceName = "_RenderQueueType", hidden = true, overrideHLSLDeclaration = true, hlslDeclarationOverride = HLSLDeclaration.DoNotDeclare, value = (int)systemData.renderQueueType, }); //See SG-ADDITIONALVELOCITY-NOTE collector.AddShaderProperty(new BooleanShaderProperty { value = builtinData.addPrecomputedVelocity, hidden = true, overrideHLSLDeclaration = true, hlslDeclarationOverride = HLSLDeclaration.DoNotDeclare, overrideReferenceName = kAddPrecomputedVelocity, }); collector.AddShaderProperty(new BooleanShaderProperty { value = builtinData.depthOffset, hidden = true, overrideHLSLDeclaration = true, hlslDeclarationOverride = HLSLDeclaration.DoNotDeclare, overrideReferenceName = kDepthOffsetEnable }); collector.AddShaderProperty(new BooleanShaderProperty { value = builtinData.transparentWritesMotionVec, hidden = true, overrideHLSLDeclaration = true, hlslDeclarationOverride = HLSLDeclaration.DoNotDeclare, overrideReferenceName = kTransparentWritingMotionVec }); // Common properties for all "surface" master nodes HDSubShaderUtilities.AddAlphaCutoffShaderProperties(collector, systemData.alphaTest, builtinData.alphaTestShadow); HDSubShaderUtilities.AddDoubleSidedProperty(collector, systemData.doubleSidedMode); HDSubShaderUtilities.AddPrePostPassProperties(collector, builtinData.transparentDepthPrepass, builtinData.transparentDepthPostpass); // Add all shader properties required by the inspector HDSubShaderUtilities.AddBlendingStatesShaderProperties( collector, systemData.surfaceType, systemData.blendMode, systemData.sortPriority, builtinData.alphaToMask, systemData.transparentZWrite, systemData.transparentCullMode, systemData.opaqueCullMode, systemData.zTest, builtinData.backThenFrontRendering, builtinData.transparencyFog ); } public override void ProcessPreviewMaterial(Material material) { // Fixup the material settings: material.SetFloat(kSurfaceType, (int)systemData.surfaceType); material.SetFloat(kDoubleSidedNormalMode, (int)systemData.doubleSidedMode); material.SetFloat(kDoubleSidedEnable, systemData.doubleSidedMode != DoubleSidedMode.Disabled ? 1 : 0); material.SetFloat(kAlphaCutoffEnabled, systemData.alphaTest ? 1 : 0); material.SetFloat(kBlendMode, (int)systemData.blendMode); material.SetFloat(kEnableFogOnTransparent, builtinData.transparencyFog ? 1.0f : 0.0f); material.SetFloat(kZTestTransparent, (int)systemData.zTest); material.SetFloat(kTransparentCullMode, (int)systemData.transparentCullMode); material.SetFloat(kOpaqueCullMode, (int)systemData.opaqueCullMode); material.SetFloat(kTransparentZWrite, systemData.transparentZWrite ? 1.0f : 0.0f); // No sorting priority for shader graph preview material.renderQueue = (int)HDRenderQueue.ChangeType(systemData.renderQueueType, offset: 0, alphaTest: systemData.alphaTest, false); LightingShaderGraphGUI.SetupLightingKeywordsAndPass(material); } internal override void MigrateTo(ShaderGraphVersion version) { base.MigrateTo(version); if (version == ShaderGraphVersion.FirstTimeMigration) { #pragma warning disable 618 // If we come from old master node, nothing to do. // Only perform an action if we are a shader stack if (!m_MigrateFromOldSG) { builtinData.transparentDepthPrepass = systemData.m_TransparentDepthPrepass; builtinData.transparentDepthPostpass = systemData.m_TransparentDepthPostpass; builtinData.supportLodCrossFade = systemData.m_SupportLodCrossFade; } #pragma warning restore 618 } } } }