public static bool TestActive(this IConditional conditional, ActiveFields fields) { // Test FieldCondition against current active Fields bool TestFieldCondition(FieldCondition fieldCondition) { // Required active field is not active if (fieldCondition.condition == true && !fields.baseInstance.Contains(fieldCondition.field)) { return(false); } // Required non-active field is active else if (fieldCondition.condition == false && fields.baseInstance.Contains(fieldCondition.field)) { return(false); } return(true); } // No FieldConditions if (conditional.fieldConditions == null) { return(true); } // One or more FieldConditions failed if (conditional.fieldConditions.Where(x => !TestFieldCondition(x)).Any()) { return(false); } // All FieldConditions passed return(true); }
private static ActiveFields GetActiveFieldsFromMasterNode(AbstractMaterialNode iMasterNode, Pass pass) { var activeFields = new ActiveFields(); var baseActiveFields = activeFields.baseInstance; HDUnlitMasterNode masterNode = iMasterNode as HDUnlitMasterNode; if (masterNode == null) { return(activeFields); } if (masterNode.alphaTest.isOn && pass.PixelShaderUsesSlot(HDUnlitMasterNode.AlphaThresholdSlotId)) { baseActiveFields.Add("AlphaTest"); } if (masterNode.surfaceType != SurfaceType.Opaque) { if (masterNode.transparencyFog.isOn) { baseActiveFields.Add("AlphaFog"); } } if (masterNode.addPrecomputedVelocity.isOn) { baseActiveFields.Add("AddPrecomputedVelocity"); } return(activeFields); }
private static ActiveFields GetActiveFieldsFromMasterNode(AbstractMaterialNode iMasterNode, Pass pass) { var activeFields = new ActiveFields(); var baseActiveFields = activeFields.baseInstance; DecalMasterNode masterNode = iMasterNode as DecalMasterNode; if (masterNode == null) { return(activeFields); } if (masterNode.affectsAlbedo.isOn) { baseActiveFields.Add("Material.AffectsAlbedo"); } if (masterNode.affectsNormal.isOn) { baseActiveFields.Add("Material.AffectsNormal"); } if (masterNode.affectsEmission.isOn) { baseActiveFields.Add("Material.AffectsEmission"); } if (masterNode.affectsSmoothness.isOn || masterNode.affectsMetal.isOn || masterNode.affectsAO.isOn) { baseActiveFields.Add("Material.AffectsMaskMap"); } return(activeFields); }
ActiveFields GetActiveFieldsFromMasterNode(ToonMasterNode masterNode, ShaderPass pass) { var activeFields = new ActiveFields(); var baseActiveFields = activeFields.baseInstance; // Graph Vertex if (masterNode.IsSlotConnected(ToonMasterNode.PositionSlotId) || masterNode.IsSlotConnected(ToonMasterNode.VertNormalSlotId) || masterNode.IsSlotConnected(ToonMasterNode.VertTangentSlotId) || masterNode.IsSlotConnected(ToonMasterNode.OutlineWidthSlotId) ) { baseActiveFields.Add("features.graphVertex"); } // Graph Pixel (always enabled) baseActiveFields.Add("features.graphPixel"); if (masterNode.IsSlotConnected(ToonMasterNode.AlphaThresholdSlotId) || masterNode.GetInputSlots <Vector1MaterialSlot> ().First(x => x.id == ToonMasterNode.AlphaThresholdSlotId).value > 0.0f) { baseActiveFields.Add("AlphaClip"); } if (masterNode.model == ToonMasterNode.Model.Specular) { baseActiveFields.Add("SpecularSetup"); } if (masterNode.IsSlotConnected(ToonMasterNode.NormalSlotId)) { baseActiveFields.Add("Normal"); } // Keywords for transparent // #pragma shader_feature _SURFACE_TYPE_TRANSPARENT if (masterNode.surfaceType != SurfaceType.Opaque) { // transparent-only defines baseActiveFields.Add("SurfaceType.Transparent"); // #pragma shader_feature _ _BLENDMODE_ALPHA _BLENDMODE_ADD _BLENDMODE_PRE_MULTIPLY if (masterNode.alphaMode == AlphaMode.Alpha) { baseActiveFields.Add("BlendMode.Alpha"); } else if (masterNode.alphaMode == AlphaMode.Additive) { baseActiveFields.Add("BlendMode.Add"); } else if (masterNode.alphaMode == AlphaMode.Premultiply) { baseActiveFields.Add("BlendMode.Premultiply"); } } return(activeFields); }
private static ActiveFields GetActiveFieldsFromMasterNode(CustomMasterNode masterNode, ShaderPass pass) { var activeFields = new ActiveFields(); var baseActiveFields = activeFields.baseInstance; baseActiveFields.Add("features.graphPixel"); return(activeFields); }
public TemplatePreprocessor(ActiveFields activeFields, Dictionary <string, string> namedFragments, bool isDebug, string[] templatePaths, AssetCollection assetCollection, ShaderStringBuilder outShaderCodeResult = null) { this.activeFields = activeFields; this.namedFragments = namedFragments; this.isDebug = isDebug; this.templatePaths = templatePaths; this.assetCollection = assetCollection; this.result = outShaderCodeResult ?? new ShaderStringBuilder(); includedFiles = new HashSet <string>(); }
public TemplatePreprocessor(ActiveFields activeFields, Dictionary <string, string> namedFragments, bool isDebug, string templatePath, List <string> sourceAssetDependencyPaths, ShaderStringBuilder outShaderCodeResult = null) { this.activeFields = activeFields; this.namedFragments = namedFragments; this.isDebug = isDebug; this.templatePath = templatePath; this.sourceAssetDependencyPaths = sourceAssetDependencyPaths; this.result = outShaderCodeResult ?? new ShaderStringBuilder(); includedFiles = new HashSet <string>(); }
private static ActiveFields GetActiveFieldsFromMasterNode(AbstractMaterialNode iMasterNode, Pass pass) { var activeFields = new ActiveFields(); var baseActiveFields = activeFields.baseInstance; UnlitMasterNode masterNode = iMasterNode as UnlitMasterNode; if (masterNode == null) { return(activeFields); } if (masterNode.IsSlotConnected(UnlitMasterNode.VertNormalSlotId)) { baseActiveFields.Add("AttributesMesh.normalOS"); } if (masterNode.IsSlotConnected(UnlitMasterNode.AlphaThresholdSlotId) || masterNode.GetInputSlots <Vector1MaterialSlot>().First(x => x.id == UnlitMasterNode.AlphaThresholdSlotId).value > 0.0f) { baseActiveFields.Add("AlphaTest"); } // Keywords for transparent // #pragma shader_feature _SURFACE_TYPE_TRANSPARENT if (masterNode.surfaceType != ShaderGraph.SurfaceType.Opaque) { // transparent-only defines baseActiveFields.Add("SurfaceType.Transparent"); // #pragma shader_feature _ _BLENDMODE_ALPHA _BLENDMODE_ADD _BLENDMODE_PRE_MULTIPLY if (masterNode.alphaMode == AlphaMode.Alpha) { baseActiveFields.Add("BlendMode.Alpha"); } else if (masterNode.alphaMode == AlphaMode.Additive) { baseActiveFields.Add("BlendMode.Add"); } } else { // opaque-only defines } if (masterNode.addPrecomputedVelocity.isOn) { baseActiveFields.Add("AddPrecomputedVelocity"); } return(activeFields); }
public static ActiveFields GatherActiveFieldsFromNode(AbstractMaterialNode outputNode, PassDescriptor pass) { var activeFields = new ActiveFields(); if (outputNode is IMasterNode masterNode) { var fields = GenerationUtils.GetActiveFieldsFromConditionals(masterNode.GetConditionalFields(pass)); foreach (FieldDescriptor field in fields) { activeFields.baseInstance.Add(field); } } // Preview shader else { activeFields.baseInstance.Add(Fields.GraphPixel); } return(activeFields); }
private static ActiveFields GetActiveFieldsFromMasterNode(SpriteLitMasterNode masterNode, ShaderPass pass) { var activeFields = new ActiveFields(); var baseActiveFields = activeFields.baseInstance; // Graph Vertex if (masterNode.IsSlotConnected(SpriteLitMasterNode.PositionSlotId) || masterNode.IsSlotConnected(SpriteLitMasterNode.VertNormalSlotId) || masterNode.IsSlotConnected(SpriteLitMasterNode.VertTangentSlotId)) { baseActiveFields.Add("features.graphVertex"); } // Graph Pixel (always enabled) baseActiveFields.Add("features.graphPixel"); baseActiveFields.Add("SurfaceType.Transparent"); baseActiveFields.Add("BlendMode.Alpha"); return(activeFields); }
static void GetActiveFieldsAndPermutationsForNodes(AbstractMaterialNode masterNode, ShaderPass pass, KeywordCollector keywordCollector, List <AbstractMaterialNode> vertexNodes, List <AbstractMaterialNode> pixelNodes, List <int>[] vertexNodePermutations, List <int>[] pixelNodePermutations, ActiveFields activeFields, out ShaderGraphRequirementsPerKeyword graphRequirements) { // Initialize requirements ShaderGraphRequirementsPerKeyword pixelRequirements = new ShaderGraphRequirementsPerKeyword(); ShaderGraphRequirementsPerKeyword vertexRequirements = new ShaderGraphRequirementsPerKeyword(); graphRequirements = new ShaderGraphRequirementsPerKeyword(); // Evaluate all Keyword permutations if (keywordCollector.permutations.Count > 0) { for (int i = 0; i < keywordCollector.permutations.Count; i++) { // Get active nodes for this permutation var localVertexNodes = Graphing.ListPool <AbstractMaterialNode> .Get(); var localPixelNodes = Graphing.ListPool <AbstractMaterialNode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(localVertexNodes, masterNode, NodeUtils.IncludeSelf.Include, pass.vertexPorts, keywordCollector.permutations[i]); NodeUtils.DepthFirstCollectNodesFromNode(localPixelNodes, masterNode, NodeUtils.IncludeSelf.Include, pass.pixelPorts, keywordCollector.permutations[i]); // Track each vertex node in this permutation foreach (AbstractMaterialNode vertexNode in localVertexNodes) { int nodeIndex = vertexNodes.IndexOf(vertexNode); if (vertexNodePermutations[nodeIndex] == null) { vertexNodePermutations[nodeIndex] = new List <int>(); } vertexNodePermutations[nodeIndex].Add(i); } // Track each pixel node in this permutation foreach (AbstractMaterialNode pixelNode in localPixelNodes) { int nodeIndex = pixelNodes.IndexOf(pixelNode); if (pixelNodePermutations[nodeIndex] == null) { pixelNodePermutations[nodeIndex] = new List <int>(); } pixelNodePermutations[nodeIndex].Add(i); } // Get requirements for this permutation vertexRequirements[i].SetRequirements(ShaderGraphRequirements.FromNodes(localVertexNodes, ShaderStageCapability.Vertex, false)); pixelRequirements[i].SetRequirements(ShaderGraphRequirements.FromNodes(localPixelNodes, ShaderStageCapability.Fragment, false)); // Add active fields AddActiveFieldsFromGraphRequirements(activeFields[i], vertexRequirements[i].requirements, "VertexDescriptionInputs"); AddActiveFieldsFromGraphRequirements(activeFields[i], pixelRequirements[i].requirements, "SurfaceDescriptionInputs"); } } // No Keywords else { // Get requirements vertexRequirements.baseInstance.SetRequirements(ShaderGraphRequirements.FromNodes(vertexNodes, ShaderStageCapability.Vertex, false)); pixelRequirements.baseInstance.SetRequirements(ShaderGraphRequirements.FromNodes(pixelNodes, ShaderStageCapability.Fragment, false)); // Add active fields AddActiveFieldsFromGraphRequirements(activeFields.baseInstance, vertexRequirements.baseInstance.requirements, "VertexDescriptionInputs"); AddActiveFieldsFromGraphRequirements(activeFields.baseInstance, pixelRequirements.baseInstance.requirements, "SurfaceDescriptionInputs"); } // Build graph requirements graphRequirements.UnionWith(pixelRequirements); graphRequirements.UnionWith(vertexRequirements); }
public static void BuildType(System.Type t, ActiveFields activeFields, ShaderGenerator result, bool isDebug) { result.AddShaderChunk("struct " + t.Name); result.AddShaderChunk("{"); result.Indent(); foreach (FieldInfo field in t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) { if (field.MemberType == MemberTypes.Field) { bool isOptional = false; var fieldIsActive = false; var keywordIfdefs = string.Empty; if (activeFields.permutationCount > 0) { // Evaluate all activeFields instance var instances = activeFields .allPermutations.instances .Where(i => ShouldSpliceField(t, field, i, out isOptional)) .ToList(); fieldIsActive = instances.Count > 0; if (fieldIsActive) { keywordIfdefs = KeywordUtil.GetKeywordPermutationSetConditional(instances .Select(i => i.permutationIndex).ToList()); } } else { fieldIsActive = ShouldSpliceField(t, field, activeFields.baseInstance, out isOptional); } if (fieldIsActive) { // The field is used, so generate it var semanticString = GetFieldSemantic(field); int componentCount; var fieldType = GetFieldType(field, out componentCount); var conditional = GetFieldConditional(field); if (conditional != null) { result.AddShaderChunk("#if " + conditional); } if (!string.IsNullOrEmpty(keywordIfdefs)) { result.AddShaderChunk(keywordIfdefs); } var fieldDecl = fieldType + " " + field.Name + semanticString + ";" + (isOptional & isDebug ? " // optional" : string.Empty); result.AddShaderChunk(fieldDecl); if (!string.IsNullOrEmpty(keywordIfdefs)) { result.AddShaderChunk("#endif" + (isDebug ? " // Shader Graph Keywords" : string.Empty)); } if (conditional != null) { result.AddShaderChunk("#endif" + (isDebug ? $" // {conditional}" : string.Empty)); } } } } result.Deindent(); result.AddShaderChunk("};"); object[] packAttributes = t.GetCustomAttributes(typeof(InterpolatorPack), false); if (packAttributes.Length > 0) { result.AddNewLine(); if (activeFields.permutationCount > 0) { var generatedPackedTypes = new Dictionary <string, (ShaderGenerator, List <int>)>(); foreach (var instance in activeFields.allPermutations.instances) { var instanceGenerator = new ShaderGenerator(); BuildPackedType(t, instance, instanceGenerator, isDebug); var key = instanceGenerator.GetShaderString(0); if (generatedPackedTypes.TryGetValue(key, out var value)) { value.Item2.Add(instance.permutationIndex); } else { generatedPackedTypes.Add(key, (instanceGenerator, new List <int> { instance.permutationIndex }));
ActiveFields GetActiveFieldsFromMasterNode(PBRMasterNode masterNode, ShaderPass pass) { var activeFields = new ActiveFields(); var baseActiveFields = activeFields.baseInstance; // Graph Vertex if (masterNode.IsSlotConnected(PBRMasterNode.PositionSlotId) || masterNode.IsSlotConnected(PBRMasterNode.VertNormalSlotId) || masterNode.IsSlotConnected(PBRMasterNode.VertTangentSlotId)) { baseActiveFields.Add("features.graphVertex"); } // Graph Pixel (always enabled) baseActiveFields.Add("features.graphPixel"); if (masterNode.IsSlotConnected(PBRMasterNode.AlphaThresholdSlotId) || masterNode.GetInputSlots <Vector1MaterialSlot>().First(x => x.id == PBRMasterNode.AlphaThresholdSlotId).value > 0.0f) { baseActiveFields.Add("AlphaClip"); } if (masterNode.model == PBRMasterNode.Model.Specular) { baseActiveFields.Add("SpecularSetup"); } if (masterNode.IsSlotConnected(PBRMasterNode.NormalSlotId)) { baseActiveFields.Add("Normal"); } switch (masterNode.normalDropOffSpace) { case NormalDropOffSpace.Tangent: baseActiveFields.AddAll("features.NormalDropOffTS"); break; case NormalDropOffSpace.Object: baseActiveFields.AddAll("features.NormalDropOffOS"); break; case NormalDropOffSpace.World: baseActiveFields.AddAll("features.NormalDropOffWS"); break; default: UnityEngine.Debug.LogError("Unknown normal drop off space: " + masterNode.normalDropOffSpace); break; } // Keywords for transparent // #pragma shader_feature _SURFACE_TYPE_TRANSPARENT if (masterNode.surfaceType != ShaderGraph.SurfaceType.Opaque) { // transparent-only defines baseActiveFields.Add("SurfaceType.Transparent"); // #pragma shader_feature _ _BLENDMODE_ALPHA _BLENDMODE_ADD _BLENDMODE_PRE_MULTIPLY if (masterNode.alphaMode == AlphaMode.Alpha) { baseActiveFields.Add("BlendMode.Alpha"); } else if (masterNode.alphaMode == AlphaMode.Additive) { baseActiveFields.Add("BlendMode.Add"); } else if (masterNode.alphaMode == AlphaMode.Premultiply) { baseActiveFields.Add("BlendMode.Premultiply"); } } return(activeFields); }
private static ActiveFields GetActiveFieldsFromMasterNode(AbstractMaterialNode iMasterNode, Pass pass) { var activeFields = new ActiveFields(); var baseActiveFields = activeFields.baseInstance; FabricMasterNode masterNode = iMasterNode as FabricMasterNode; if (masterNode == null) { return(activeFields); } if (masterNode.doubleSidedMode != DoubleSidedMode.Disabled) { if (pass.ShaderPassName != "SHADERPASS_MOTION_VECTORS") // HACK to get around lack of a good interpolator dependency system { // we need to be able to build interpolators using multiple input structs // also: should only require isFrontFace if Normals are required... // Important: the following is used in SharedCode.template.hlsl for determining the normal flip mode baseActiveFields.Add("FragInputs.isFrontFace"); } } switch (masterNode.materialType) { case FabricMasterNode.MaterialType.CottonWool: baseActiveFields.Add("Material.CottonWool"); break; case FabricMasterNode.MaterialType.Silk: baseActiveFields.Add("Material.Silk"); break; default: UnityEngine.Debug.LogError("Unknown material type: " + masterNode.materialType); break; } if (masterNode.alphaTest.isOn) { if (pass.PixelShaderUsesSlot(FabricMasterNode.AlphaClipThresholdSlotId)) { baseActiveFields.Add("AlphaTest"); } } if (masterNode.surfaceType != SurfaceType.Opaque) { if (masterNode.transparencyFog.isOn) { baseActiveFields.Add("AlphaFog"); } if (masterNode.blendPreserveSpecular.isOn) { baseActiveFields.Add("BlendMode.PreserveSpecular"); } } if (!masterNode.receiveDecals.isOn) { baseActiveFields.Add("DisableDecals"); } if (!masterNode.receiveSSR.isOn) { baseActiveFields.Add("DisableSSR"); } if (masterNode.addPrecomputedVelocity.isOn) { baseActiveFields.Add("AddPrecomputedVelocity"); } if (masterNode.energyConservingSpecular.isOn) { baseActiveFields.Add("Specular.EnergyConserving"); } if (masterNode.transmission.isOn) { baseActiveFields.Add("Material.Transmission"); } if (masterNode.subsurfaceScattering.isOn && masterNode.surfaceType != SurfaceType.Transparent) { baseActiveFields.Add("Material.SubsurfaceScattering"); } if (masterNode.IsSlotConnected(FabricMasterNode.BentNormalSlotId) && pass.PixelShaderUsesSlot(FabricMasterNode.BentNormalSlotId)) { baseActiveFields.Add("BentNormal"); } if (masterNode.IsSlotConnected(FabricMasterNode.TangentSlotId) && pass.PixelShaderUsesSlot(FabricMasterNode.TangentSlotId)) { baseActiveFields.Add("Tangent"); } switch (masterNode.specularOcclusionMode) { case SpecularOcclusionMode.Off: break; case SpecularOcclusionMode.FromAO: baseActiveFields.Add("SpecularOcclusionFromAO"); break; case SpecularOcclusionMode.FromAOAndBentNormal: baseActiveFields.Add("SpecularOcclusionFromAOBentNormal"); break; case SpecularOcclusionMode.Custom: baseActiveFields.Add("SpecularOcclusionCustom"); break; default: break; } if (pass.PixelShaderUsesSlot(FabricMasterNode.AmbientOcclusionSlotId)) { var occlusionSlot = masterNode.FindSlot <Vector1MaterialSlot>(FabricMasterNode.AmbientOcclusionSlotId); bool connected = masterNode.IsSlotConnected(FabricMasterNode.AmbientOcclusionSlotId); if (connected || occlusionSlot.value != occlusionSlot.defaultValue) { baseActiveFields.Add("AmbientOcclusion"); } } if (masterNode.IsSlotConnected(FabricMasterNode.LightingSlotId) && pass.PixelShaderUsesSlot(FabricMasterNode.LightingSlotId)) { baseActiveFields.Add("LightingGI"); } if (masterNode.IsSlotConnected(FabricMasterNode.BackLightingSlotId) && pass.PixelShaderUsesSlot(FabricMasterNode.BackLightingSlotId)) { baseActiveFields.Add("BackLightingGI"); } if (masterNode.depthOffset.isOn && pass.PixelShaderUsesSlot(FabricMasterNode.DepthOffsetSlotId)) { baseActiveFields.Add("DepthOffset"); } if (masterNode.supportLodCrossFade.isOn) { baseActiveFields.AddAll("LodCrossFade"); } return(activeFields); }
// // Reference for GetActiveFieldsFromMasterNode // ------------------------------------------- // // Properties (enables etc): // // ok+MFD -> material feature define: means we need a predicate, because we will transform it into a #define that match the material feature, shader_feature-defined, that the rest of the shader code uses. // // ok+MFD masterNode.baseParametrization --> even though we can just always transfer present fields (check with $SurfaceDescription.*) like specularcolor and metallic, // we need to translate this into the _MATERIAL_FEATURE_SPECULAR_COLOR define. // // ok masterNode.energyConservingSpecular // // ~~~~ ok+MFD: these are almost all material features: // masterNode.anisotropy // masterNode.coat // masterNode.coatNormal // masterNode.dualSpecularLobe // masterNode.dualSpecularLobeParametrization // masterNode.capHazinessWrtMetallic -> not a material feature define, as such, we will create a combined predicate for the HazyGlossMaxDielectricF0 slot dependency // instead of adding a #define in the template... // masterNode.iridescence // masterNode.subsurfaceScattering // masterNode.transmission // // ~~~~ ...ok+MFD: these are all material features // // ok masterNode.receiveDecals // ok masterNode.receiveSSR // ok masterNode.geometricSpecularAA --> check, a way to combine predicates and/or exclude passes: TODOTODO What about WRITE_NORMAL_BUFFER passes ? (ie smoothness) // ok masterNode.specularOcclusion --> no use for it though! see comments. // // ~~~~ ok+D: these require translation to defines also... // // masterNode.anisotropyForAreaLights // masterNode.recomputeStackPerLight // masterNode.shadeBaseUsingRefractedAngles // masterNode.debug // Inputs: Most inputs don't need a specific predicate in addition to the "present field predicate", ie the $SurfaceDescription.*, // but in some special cases we check connectivity to avoid processing the default value for nothing... // (see specular occlusion with _MASKMAP and _BENTNORMALMAP in LitData, or _TANGENTMAP, _BENTNORMALMAP, etc. which act a bit like that // although they also avoid sampling in that case, but default tiny texture map sampling isn't a big hit since they are all cached once // a default "unityTexWhite" is sampled, it is cached for everyone defaulting to white...) // // ok+ means there's a specific additional predicate // // ok masterNode.BaseColorSlotId // ok masterNode.NormalSlotId // // ok+ masterNode.BentNormalSlotId --> Dependency of the predicate on IsSlotConnected avoids processing even if the slots // ok+ masterNode.TangentSlotId are always there so any pass that declares its use in PixelShaderSlots will have the field in SurfaceDescription, // but it's not necessarily useful (if slot isnt connected, waste processing on potentially static expressions if // shader compiler cant optimize...and even then, useless to have static override value for those.) // // TODOTODO: Note you could have the same argument for NormalSlot (which we dont exclude with a predicate). // Also and anyways, the compiler is smart enough not to do the TS to WS matrix multiply on a (0,0,1) vector. // // ok+ masterNode.CoatNormalSlotId -> we already have a "material feature" coat normal map so can use that instead, although using that former, we assume the coat normal slot // will be there, but it's ok, we can #ifdef the code on the material feature define, and use the $SurfaceDescription.CoatNormal predicate // for the actual assignment, // although for that one we could again // use the "connected" condition like for tangent and bentnormal // // The following are all ok, no need beyond present field predicate, ie $SurfaceDescription.*, // except special cases where noted // // ok masterNode.SubsurfaceMaskSlotId // ok masterNode.ThicknessSlotId // ok masterNode.DiffusionProfileHashSlotId // ok masterNode.IridescenceMaskSlotId // ok masterNode.IridescenceThicknessSlotId // ok masterNode.SpecularColorSlotId // ok masterNode.DielectricIorSlotId // ok masterNode.MetallicSlotId // ok masterNode.EmissionSlotId // ok masterNode.SmoothnessASlotId // ok masterNode.SmoothnessBSlotId // ok+ masterNode.AmbientOcclusionSlotId -> defined a specific predicate, but not used, see StackLitData. // ok masterNode.AlphaSlotId // ok masterNode.AlphaClipThresholdSlotId // ok masterNode.AnisotropyASlotId // ok masterNode.AnisotropyBSlotId // ok masterNode.SpecularAAScreenSpaceVarianceSlotId // ok masterNode.SpecularAAThresholdSlotId // ok masterNode.CoatSmoothnessSlotId // ok masterNode.CoatIorSlotId // ok masterNode.CoatThicknessSlotId // ok masterNode.CoatExtinctionSlotId // ok masterNode.LobeMixSlotId // ok masterNode.HazinessSlotId // ok masterNode.HazeExtentSlotId // ok masterNode.HazyGlossMaxDielectricF0SlotId -> No need for a predicate, the needed predicate is the combined (capHazinessWrtMetallic + HazyGlossMaxDielectricF0) // "leaking case": if the 2 are true, but we're not in metallic mode, the capHazinessWrtMetallic property is wrong, // that means the master node is really misconfigured, spew an error, should never happen... // If it happens, it's because we forgot UpdateNodeAfterDeserialization() call when modifying the capHazinessWrtMetallic or baseParametrization // properties, maybe through debug etc. // // ok masterNode.DistortionSlotId -> Warning: peculiarly, instead of using $SurfaceDescription.Distortion and DistortionBlur, // ok masterNode.DistortionBlurSlotId we do an #if (SHADERPASS == SHADERPASS_DISTORTION) in the template, instead of // relying on other passed NOT to include the DistortionSlotId in their PixelShaderSlots!! // Other to deal with, and // Common between Lit and StackLit: // // doubleSidedMode, alphaTest, receiveDecals, // surfaceType, alphaMode, blendPreserveSpecular, transparencyFog, // distortion, distortionMode, distortionDepthTest, // sortPriority (int) // geometricSpecularAA, energyConservingSpecular, specularOcclusion // private static ActiveFields GetActiveFieldsFromMasterNode(AbstractMaterialNode iMasterNode, Pass pass) { var activeFields = new ActiveFields(); var baseActiveFields = activeFields.baseInstance; StackLitMasterNode masterNode = iMasterNode as StackLitMasterNode; if (masterNode == null) { return(activeFields); } if (masterNode.doubleSidedMode != DoubleSidedMode.Disabled) { if (pass.ShaderPassName != "SHADERPASS_MOTION_VECTORS") // HACK to get around lack of a good interpolator dependency system { // we need to be able to build interpolators using multiple input structs // also: should only require isFrontFace if Normals are required... // Important: the following is used in SharedCode.template.hlsl for determining the normal flip mode baseActiveFields.Add("FragInputs.isFrontFace"); } } if (masterNode.alphaTest.isOn) { if (pass.PixelShaderUsesSlot(StackLitMasterNode.AlphaClipThresholdSlotId)) { baseActiveFields.Add("AlphaTest"); } } if (masterNode.surfaceType != SurfaceType.Opaque) { if (masterNode.transparencyFog.isOn) { baseActiveFields.Add("AlphaFog"); } if (masterNode.blendPreserveSpecular.isOn) { baseActiveFields.Add("BlendMode.PreserveSpecular"); } } // // Predicates to change into defines: // // Even though we can just always transfer the present (check with $SurfaceDescription.*) fields like specularcolor // and metallic, we still need to know the baseParametrization in the template to translate into the // _MATERIAL_FEATURE_SPECULAR_COLOR define: if (masterNode.baseParametrization == StackLit.BaseParametrization.SpecularColor) { baseActiveFields.Add("BaseParametrization.SpecularColor"); } if (masterNode.energyConservingSpecular.isOn) // No defines, suboption of BaseParametrization.SpecularColor { baseActiveFields.Add("EnergyConservingSpecular"); } if (masterNode.anisotropy.isOn) { baseActiveFields.Add("Material.Anisotropy"); } if (masterNode.coat.isOn) { baseActiveFields.Add("Material.Coat"); if (pass.PixelShaderUsesSlot(StackLitMasterNode.CoatMaskSlotId)) { var coatMaskSlot = masterNode.FindSlot <Vector1MaterialSlot>(StackLitMasterNode.CoatMaskSlotId); bool connected = masterNode.IsSlotConnected(StackLitMasterNode.CoatMaskSlotId); if (connected || (coatMaskSlot.value != 0.0f && coatMaskSlot.value != 1.0f)) { baseActiveFields.Add("CoatMask"); } else if (coatMaskSlot.value == 0.0f) { baseActiveFields.Add("CoatMaskZero"); } else if (coatMaskSlot.value == 1.0f) { baseActiveFields.Add("CoatMaskOne"); } } } if (masterNode.coatNormal.isOn) { baseActiveFields.Add("Material.CoatNormal"); } if (masterNode.dualSpecularLobe.isOn) { baseActiveFields.Add("Material.DualSpecularLobe"); if (masterNode.dualSpecularLobeParametrization == StackLit.DualSpecularLobeParametrization.HazyGloss) { baseActiveFields.Add("DualSpecularLobeParametrization.HazyGloss"); // Option for baseParametrization == Metallic && DualSpecularLobeParametrization == HazyGloss: if (masterNode.capHazinessWrtMetallic.isOn && pass.PixelShaderUsesSlot(StackLitMasterNode.HazyGlossMaxDielectricF0SlotId)) { // check the supporting slot is there (although masternode should deal with having a consistent property config) var maxDielectricF0Slot = masterNode.FindSlot <Vector1MaterialSlot>(StackLitMasterNode.HazyGlossMaxDielectricF0SlotId); if (maxDielectricF0Slot != null) { // Again we assume masternode has HazyGlossMaxDielectricF0 which should always be the case // if capHazinessWrtMetallic.isOn. baseActiveFields.Add("CapHazinessIfNotMetallic"); } } } } if (masterNode.iridescence.isOn) { baseActiveFields.Add("Material.Iridescence"); } if (masterNode.subsurfaceScattering.isOn && masterNode.surfaceType != SurfaceType.Transparent) { baseActiveFields.Add("Material.SubsurfaceScattering"); } if (masterNode.transmission.isOn) { baseActiveFields.Add("Material.Transmission"); } // Advanced: if (masterNode.anisotropyForAreaLights.isOn) { baseActiveFields.Add("AnisotropyForAreaLights"); } if (masterNode.recomputeStackPerLight.isOn) { baseActiveFields.Add("RecomputeStackPerLight"); } if (masterNode.honorPerLightMinRoughness.isOn) { baseActiveFields.Add("HonorPerLightMinRoughness"); } if (masterNode.shadeBaseUsingRefractedAngles.isOn) { baseActiveFields.Add("ShadeBaseUsingRefractedAngles"); } if (masterNode.debug.isOn) { baseActiveFields.Add("StackLitDebug"); } // // Other property predicates: // if (!masterNode.receiveDecals.isOn) { baseActiveFields.Add("DisableDecals"); } if (!masterNode.receiveSSR.isOn) { baseActiveFields.Add("DisableSSR"); } if (masterNode.addPrecomputedVelocity.isOn) { baseActiveFields.Add("AddPrecomputedVelocity"); } // Note here we combine an "enable"-like predicate and the $SurfaceDescription.(slotname) predicate // into a single $GeometricSpecularAA pedicate. // // ($SurfaceDescription.* predicates are useful to make sure the field is present in the struct in the template. // The field will be present if both the master node and pass have the slotid, see this set intersection we make // in GenerateSurfaceDescriptionStruct(), with HDSubShaderUtilities.FindMaterialSlotsOnNode().) // // Normally, since the feature enable adds the required slots, only the $SurfaceDescription.* would be required, // but some passes might not need it and not declare the PixelShaderSlot, or, inversely, the pass might not // declare it as a way to avoid it. // // IE this has also the side effect to disable geometricSpecularAA - even if "on" - for passes that don't explicitly // advertise these slots(eg for a general feature, with separate "enable" and "field present" predicates, the // template could take a default value and process it anyway if a feature is "on"). // // (Note we can achieve the same results in the template on just single predicates by making defines out of them, // and using #if defined() && etc) bool haveSomeSpecularAA = false; // TODOTODO in prevision of normal texture filtering if (masterNode.geometricSpecularAA.isOn && pass.PixelShaderUsesSlot(StackLitMasterNode.SpecularAAThresholdSlotId) && pass.PixelShaderUsesSlot(StackLitMasterNode.SpecularAAScreenSpaceVarianceSlotId)) { haveSomeSpecularAA = true; baseActiveFields.Add("GeometricSpecularAA"); } if (haveSomeSpecularAA) { baseActiveFields.Add("SpecularAA"); } if (masterNode.screenSpaceSpecularOcclusionBaseMode != StackLitMasterNode.SpecularOcclusionBaseMode.Off || masterNode.dataBasedSpecularOcclusionBaseMode != StackLitMasterNode.SpecularOcclusionBaseMode.Off) { // activates main define baseActiveFields.Add("SpecularOcclusion"); } baseActiveFields.Add("ScreenSpaceSpecularOcclusionBaseMode." + masterNode.screenSpaceSpecularOcclusionBaseMode.ToString()); if (StackLitMasterNode.SpecularOcclusionModeUsesVisibilityCone(masterNode.screenSpaceSpecularOcclusionBaseMode)) { baseActiveFields.Add("ScreenSpaceSpecularOcclusionAOConeSize." + masterNode.screenSpaceSpecularOcclusionAOConeSize.ToString()); baseActiveFields.Add("ScreenSpaceSpecularOcclusionAOConeDir." + masterNode.screenSpaceSpecularOcclusionAOConeDir.ToString()); } baseActiveFields.Add("DataBasedSpecularOcclusionBaseMode." + masterNode.dataBasedSpecularOcclusionBaseMode.ToString()); if (StackLitMasterNode.SpecularOcclusionModeUsesVisibilityCone(masterNode.dataBasedSpecularOcclusionBaseMode)) { baseActiveFields.Add("DataBasedSpecularOcclusionAOConeSize." + masterNode.dataBasedSpecularOcclusionAOConeSize.ToString()); } // Set bent normal fixup predicate if needed: if (masterNode.SpecularOcclusionUsesBentNormal()) { baseActiveFields.Add("SpecularOcclusionConeFixupMethod." + masterNode.specularOcclusionConeFixupMethod.ToString()); } // // Input special-casing predicates: // if (masterNode.IsSlotConnected(StackLitMasterNode.BentNormalSlotId) && pass.PixelShaderUsesSlot(StackLitMasterNode.BentNormalSlotId)) { baseActiveFields.Add("BentNormal"); } if (masterNode.IsSlotConnected(StackLitMasterNode.TangentSlotId) && pass.PixelShaderUsesSlot(StackLitMasterNode.TangentSlotId)) { baseActiveFields.Add("Tangent"); } // The following idiom enables an optimization on feature ports that don't have an enable switch in the settings // view, where the default value might not produce a visual result and incur a processing cost we want to avoid. // For ambient occlusion, this is the case for the SpecularOcclusion calculations which also depend on it, // where a value of 1 will produce no results. // See SpecularOcclusion, we don't optimize out this case... if (pass.PixelShaderUsesSlot(StackLitMasterNode.AmbientOcclusionSlotId)) { bool connected = masterNode.IsSlotConnected(StackLitMasterNode.AmbientOcclusionSlotId); var ambientOcclusionSlot = masterNode.FindSlot <Vector1MaterialSlot>(StackLitMasterNode.AmbientOcclusionSlotId); // master node always has it, assert ambientOcclusionSlot != null if (connected || ambientOcclusionSlot.value != ambientOcclusionSlot.defaultValue) { baseActiveFields.Add("AmbientOcclusion"); } } if (masterNode.IsSlotConnected(StackLitMasterNode.CoatNormalSlotId) && pass.PixelShaderUsesSlot(StackLitMasterNode.CoatNormalSlotId)) { baseActiveFields.Add("CoatNormal"); } if (masterNode.IsSlotConnected(StackLitMasterNode.LightingSlotId) && pass.PixelShaderUsesSlot(StackLitMasterNode.LightingSlotId)) { baseActiveFields.Add("LightingGI"); } if (masterNode.IsSlotConnected(StackLitMasterNode.BackLightingSlotId) && pass.PixelShaderUsesSlot(StackLitMasterNode.BackLightingSlotId)) { baseActiveFields.Add("BackLightingGI"); } if (masterNode.depthOffset.isOn && pass.PixelShaderUsesSlot(StackLitMasterNode.DepthOffsetSlotId)) { baseActiveFields.Add("DepthOffset"); } return(activeFields); }
private static ActiveFields GetActiveFieldsFromMasterNode(AbstractMaterialNode iMasterNode, Pass pass) { var activeFields = new ActiveFields(); var baseActiveFields = activeFields.baseInstance; HairMasterNode masterNode = iMasterNode as HairMasterNode; if (masterNode == null) { return(activeFields); } if (masterNode.doubleSidedMode != DoubleSidedMode.Disabled) { if (pass.ShaderPassName != "SHADERPASS_MOTION_VECTORS") // HACK to get around lack of a good interpolator dependency system { // we need to be able to build interpolators using multiple input structs // also: should only require isFrontFace if Normals are required... // Important: the following is used in SharedCode.template.hlsl for determining the normal flip mode baseActiveFields.Add("FragInputs.isFrontFace"); } } switch (masterNode.materialType) { case HairMasterNode.MaterialType.KajiyaKay: baseActiveFields.Add("Material.KajiyaKay"); break; default: UnityEngine.Debug.LogError("Unknown material type: " + masterNode.materialType); break; } if (masterNode.alphaTest.isOn) { int count = 0; // If alpha test shadow is enable, we use it, otherwise we use the regular test if (pass.PixelShaderUsesSlot(HairMasterNode.AlphaClipThresholdShadowSlotId) && masterNode.alphaTestShadow.isOn) { baseActiveFields.Add("AlphaTestShadow"); ++count; } else if (pass.PixelShaderUsesSlot(HairMasterNode.AlphaClipThresholdSlotId)) { baseActiveFields.Add("AlphaTest"); ++count; } // Other alpha test are suppose to be alone else if (pass.PixelShaderUsesSlot(HairMasterNode.AlphaClipThresholdDepthPrepassSlotId)) { baseActiveFields.Add("AlphaTestPrepass"); ++count; } else if (pass.PixelShaderUsesSlot(HairMasterNode.AlphaClipThresholdDepthPostpassSlotId)) { baseActiveFields.Add("AlphaTestPostpass"); ++count; } UnityEngine.Debug.Assert(count == 1, "Alpha test value not set correctly"); } if (masterNode.surfaceType != SurfaceType.Opaque) { if (masterNode.transparencyFog.isOn) { baseActiveFields.Add("AlphaFog"); } if (masterNode.transparentWritesMotionVec.isOn) { baseActiveFields.Add("TransparentWritesMotionVec"); } if (masterNode.blendPreserveSpecular.isOn) { baseActiveFields.Add("BlendMode.PreserveSpecular"); } } if (!masterNode.receiveDecals.isOn) { baseActiveFields.Add("DisableDecals"); } if (!masterNode.receiveSSR.isOn) { baseActiveFields.Add("DisableSSR"); } if (masterNode.addPrecomputedVelocity.isOn) { baseActiveFields.Add("AddPrecomputedVelocity"); } if (masterNode.specularAA.isOn && pass.PixelShaderUsesSlot(HairMasterNode.SpecularAAThresholdSlotId) && pass.PixelShaderUsesSlot(HairMasterNode.SpecularAAScreenSpaceVarianceSlotId)) { baseActiveFields.Add("Specular.AA"); } if (masterNode.IsSlotConnected(HairMasterNode.BentNormalSlotId) && pass.PixelShaderUsesSlot(HairMasterNode.BentNormalSlotId)) { baseActiveFields.Add("BentNormal"); } if (masterNode.IsSlotConnected(HairMasterNode.HairStrandDirectionSlotId) && pass.PixelShaderUsesSlot(HairMasterNode.HairStrandDirectionSlotId)) { baseActiveFields.Add("HairStrandDirection"); } if (masterNode.IsSlotConnected(HairMasterNode.TransmittanceSlotId) && pass.PixelShaderUsesSlot(HairMasterNode.TransmittanceSlotId)) { baseActiveFields.Add(HairMasterNode.TransmittanceSlotName); } if (masterNode.IsSlotConnected(HairMasterNode.RimTransmissionIntensitySlotId) && pass.PixelShaderUsesSlot(HairMasterNode.RimTransmissionIntensitySlotId)) { baseActiveFields.Add(HairMasterNode.RimTransmissionIntensitySlotName); } if (masterNode.useLightFacingNormal.isOn) { baseActiveFields.Add("UseLightFacingNormal"); } switch (masterNode.specularOcclusionMode) { case SpecularOcclusionMode.Off: break; case SpecularOcclusionMode.FromAO: baseActiveFields.Add("SpecularOcclusionFromAO"); break; case SpecularOcclusionMode.FromAOAndBentNormal: baseActiveFields.Add("SpecularOcclusionFromAOBentNormal"); break; case SpecularOcclusionMode.Custom: baseActiveFields.Add("SpecularOcclusionCustom"); break; default: break; } if (pass.PixelShaderUsesSlot(HairMasterNode.AmbientOcclusionSlotId)) { var occlusionSlot = masterNode.FindSlot <Vector1MaterialSlot>(HairMasterNode.AmbientOcclusionSlotId); bool connected = masterNode.IsSlotConnected(HairMasterNode.AmbientOcclusionSlotId); if (connected || occlusionSlot.value != occlusionSlot.defaultValue) { baseActiveFields.Add("AmbientOcclusion"); } } if (masterNode.IsSlotConnected(HairMasterNode.LightingSlotId) && pass.PixelShaderUsesSlot(HairMasterNode.LightingSlotId)) { baseActiveFields.Add("LightingGI"); } if (masterNode.IsSlotConnected(HairMasterNode.BackLightingSlotId) && pass.PixelShaderUsesSlot(HairMasterNode.LightingSlotId)) { baseActiveFields.Add("BackLightingGI"); } if (masterNode.depthOffset.isOn && pass.PixelShaderUsesSlot(HairMasterNode.DepthOffsetSlotId)) { baseActiveFields.Add("DepthOffset"); } if (masterNode.supportLodCrossFade.isOn) { baseActiveFields.AddAll("LodCrossFade"); } return(activeFields); }
public static bool GenerateShaderPass(AbstractMaterialNode masterNode, ShaderPass pass, GenerationMode mode, ActiveFields activeFields, ShaderGenerator result, List <string> sourceAssetDependencyPaths, List <Dependency[]> dependencies, string resourceClassName, string assemblyName) { // -------------------------------------------------- // Debug // Get scripting symbols BuildTargetGroup buildTargetGroup = EditorUserBuildSettings.selectedBuildTargetGroup; string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup); bool isDebug = defines.Contains(kDebugSymbol); // -------------------------------------------------- // Setup // Initiailize Collectors var propertyCollector = new PropertyCollector(); var keywordCollector = new KeywordCollector(); masterNode.owner.CollectShaderKeywords(keywordCollector, mode); // Get upstream nodes from ShaderPass port mask List <AbstractMaterialNode> vertexNodes; List <AbstractMaterialNode> pixelNodes; GetUpstreamNodesForShaderPass(masterNode, pass, out vertexNodes, out pixelNodes); // Track permutation indices for all nodes List <int>[] vertexNodePermutations = new List <int> [vertexNodes.Count]; List <int>[] pixelNodePermutations = new List <int> [pixelNodes.Count]; // Get active fields from upstream Node requirements ShaderGraphRequirementsPerKeyword graphRequirements; GetActiveFieldsAndPermutationsForNodes(masterNode, pass, keywordCollector, vertexNodes, pixelNodes, vertexNodePermutations, pixelNodePermutations, activeFields, out graphRequirements); // GET CUSTOM ACTIVE FIELDS HERE! // Get active fields from ShaderPass AddRequiredFields(pass.requiredAttributes, activeFields.baseInstance); AddRequiredFields(pass.requiredVaryings, activeFields.baseInstance); // Get Port references from ShaderPass var pixelSlots = FindMaterialSlotsOnNode(pass.pixelPorts, masterNode); var vertexSlots = FindMaterialSlotsOnNode(pass.vertexPorts, masterNode); // Function Registry var functionBuilder = new ShaderStringBuilder(); var functionRegistry = new FunctionRegistry(functionBuilder); // Hash table of named $splice(name) commands // Key: splice token // Value: string to splice Dictionary <string, string> spliceCommands = new Dictionary <string, string>(); // -------------------------------------------------- // Dependencies // Propagate active field requirements using dependencies // Must be executed before types are built foreach (var instance in activeFields.all.instances) { ShaderSpliceUtil.ApplyDependencies(instance, dependencies); } // -------------------------------------------------- // Pass Setup // Name if (!string.IsNullOrEmpty(pass.displayName)) { spliceCommands.Add("PassName", $"Name \"{pass.displayName}\""); } else { spliceCommands.Add("PassName", "// Name: <None>"); } // Tags if (!string.IsNullOrEmpty(pass.lightMode)) { spliceCommands.Add("LightMode", $"\"LightMode\" = \"{pass.lightMode}\""); } else { spliceCommands.Add("LightMode", "// LightMode: <None>"); } // Render state BuildRenderStatesFromPass(pass, ref spliceCommands); // -------------------------------------------------- // Pass Code // Pragmas using (var passPragmaBuilder = new ShaderStringBuilder()) { if (pass.pragmas != null) { foreach (string pragma in pass.pragmas) { passPragmaBuilder.AppendLine($"#pragma {pragma}"); } } if (passPragmaBuilder.length == 0) { passPragmaBuilder.AppendLine("// PassPragmas: <None>"); } spliceCommands.Add("PassPragmas", passPragmaBuilder.ToCodeBlack()); } // Includes using (var passIncludeBuilder = new ShaderStringBuilder()) { if (pass.includes != null) { foreach (string include in pass.includes) { passIncludeBuilder.AppendLine($"#include \"{include}\""); } } if (passIncludeBuilder.length == 0) { passIncludeBuilder.AppendLine("// PassIncludes: <None>"); } spliceCommands.Add("PassIncludes", passIncludeBuilder.ToCodeBlack()); } // Keywords using (var passKeywordBuilder = new ShaderStringBuilder()) { if (pass.keywords != null) { foreach (KeywordDescriptor keyword in pass.keywords) { passKeywordBuilder.AppendLine(keyword.ToDeclarationString()); } } if (passKeywordBuilder.length == 0) { passKeywordBuilder.AppendLine("// PassKeywords: <None>"); } spliceCommands.Add("PassKeywords", passKeywordBuilder.ToCodeBlack()); } // -------------------------------------------------- // Graph Vertex var vertexBuilder = new ShaderStringBuilder(); // If vertex modification enabled if (activeFields.baseInstance.Contains("features.graphVertex")) { // Setup string vertexGraphInputName = "VertexDescriptionInputs"; string vertexGraphOutputName = "VertexDescription"; string vertexGraphFunctionName = "VertexDescriptionFunction"; var vertexGraphInputGenerator = new ShaderGenerator(); var vertexGraphFunctionBuilder = new ShaderStringBuilder(); var vertexGraphOutputBuilder = new ShaderStringBuilder(); // Build vertex graph inputs ShaderSpliceUtil.BuildType(GetTypeForStruct("VertexDescriptionInputs", resourceClassName, assemblyName), activeFields, vertexGraphInputGenerator, isDebug); // Build vertex graph outputs // Add struct fields to active fields SubShaderGenerator.GenerateVertexDescriptionStruct(vertexGraphOutputBuilder, vertexSlots, vertexGraphOutputName, activeFields.baseInstance); // Build vertex graph functions from ShaderPass vertex port mask SubShaderGenerator.GenerateVertexDescriptionFunction( masterNode.owner as GraphData, vertexGraphFunctionBuilder, functionRegistry, propertyCollector, keywordCollector, mode, masterNode, vertexNodes, vertexNodePermutations, vertexSlots, vertexGraphInputName, vertexGraphFunctionName, vertexGraphOutputName); // Generate final shader strings vertexBuilder.AppendLines(vertexGraphInputGenerator.GetShaderString(0, false)); vertexBuilder.AppendNewLine(); vertexBuilder.AppendLines(vertexGraphOutputBuilder.ToString()); vertexBuilder.AppendNewLine(); vertexBuilder.AppendLines(vertexGraphFunctionBuilder.ToString()); } // Add to splice commands if (vertexBuilder.length == 0) { vertexBuilder.AppendLine("// GraphVertex: <None>"); } spliceCommands.Add("GraphVertex", vertexBuilder.ToCodeBlack()); // -------------------------------------------------- // Graph Pixel // Setup string pixelGraphInputName = "SurfaceDescriptionInputs"; string pixelGraphOutputName = "SurfaceDescription"; string pixelGraphFunctionName = "SurfaceDescriptionFunction"; var pixelGraphInputGenerator = new ShaderGenerator(); var pixelGraphOutputBuilder = new ShaderStringBuilder(); var pixelGraphFunctionBuilder = new ShaderStringBuilder(); // Build pixel graph inputs ShaderSpliceUtil.BuildType(GetTypeForStruct("SurfaceDescriptionInputs", resourceClassName, assemblyName), activeFields, pixelGraphInputGenerator, isDebug); // Build pixel graph outputs // Add struct fields to active fields SubShaderGenerator.GenerateSurfaceDescriptionStruct(pixelGraphOutputBuilder, pixelSlots, pixelGraphOutputName, activeFields.baseInstance); // Build pixel graph functions from ShaderPass pixel port mask SubShaderGenerator.GenerateSurfaceDescriptionFunction( pixelNodes, pixelNodePermutations, masterNode, masterNode.owner as GraphData, pixelGraphFunctionBuilder, functionRegistry, propertyCollector, keywordCollector, mode, pixelGraphFunctionName, pixelGraphOutputName, null, pixelSlots, pixelGraphInputName); using (var pixelBuilder = new ShaderStringBuilder()) { // Generate final shader strings pixelBuilder.AppendLines(pixelGraphInputGenerator.GetShaderString(0, false)); pixelBuilder.AppendNewLine(); pixelBuilder.AppendLines(pixelGraphOutputBuilder.ToString()); pixelBuilder.AppendNewLine(); pixelBuilder.AppendLines(pixelGraphFunctionBuilder.ToString()); // Add to splice commands if (pixelBuilder.length == 0) { pixelBuilder.AppendLine("// GraphPixel: <None>"); } spliceCommands.Add("GraphPixel", pixelBuilder.ToCodeBlack()); } // -------------------------------------------------- // Graph Functions if (functionBuilder.length == 0) { functionBuilder.AppendLine("// GraphFunctions: <None>"); } spliceCommands.Add("GraphFunctions", functionBuilder.ToCodeBlack()); // -------------------------------------------------- // Graph Keywords using (var keywordBuilder = new ShaderStringBuilder()) { keywordCollector.GetKeywordsDeclaration(keywordBuilder, mode); if (keywordBuilder.length == 0) { keywordBuilder.AppendLine("// GraphKeywords: <None>"); } spliceCommands.Add("GraphKeywords", keywordBuilder.ToCodeBlack()); } // -------------------------------------------------- // Graph Properties using (var propertyBuilder = new ShaderStringBuilder()) { propertyCollector.GetPropertiesDeclaration(propertyBuilder, mode, masterNode.owner.concretePrecision); if (propertyBuilder.length == 0) { propertyBuilder.AppendLine("// GraphProperties: <None>"); } spliceCommands.Add("GraphProperties", propertyBuilder.ToCodeBlack()); } // -------------------------------------------------- // Graph Defines using (var graphDefines = new ShaderStringBuilder()) { graphDefines.AppendLine("#define {0}", pass.referenceName); if (graphRequirements.permutationCount > 0) { List <int> activePermutationIndices; // Depth Texture activePermutationIndices = graphRequirements.allPermutations.instances .Where(p => p.requirements.requiresDepthTexture) .Select(p => p.permutationIndex) .ToList(); if (activePermutationIndices.Count > 0) { graphDefines.AppendLine(KeywordUtil.GetKeywordPermutationSetConditional(activePermutationIndices)); graphDefines.AppendLine("#define REQUIRE_DEPTH_TEXTURE"); graphDefines.AppendLine("#endif"); } // Opaque Texture activePermutationIndices = graphRequirements.allPermutations.instances .Where(p => p.requirements.requiresCameraOpaqueTexture) .Select(p => p.permutationIndex) .ToList(); if (activePermutationIndices.Count > 0) { graphDefines.AppendLine(KeywordUtil.GetKeywordPermutationSetConditional(activePermutationIndices)); graphDefines.AppendLine("#define REQUIRE_OPAQUE_TEXTURE"); graphDefines.AppendLine("#endif"); } } else { // Depth Texture if (graphRequirements.baseInstance.requirements.requiresDepthTexture) { graphDefines.AppendLine("#define REQUIRE_DEPTH_TEXTURE"); } // Opaque Texture if (graphRequirements.baseInstance.requirements.requiresCameraOpaqueTexture) { graphDefines.AppendLine("#define REQUIRE_OPAQUE_TEXTURE"); } } // Add to splice commands spliceCommands.Add("GraphDefines", graphDefines.ToCodeBlack()); } // -------------------------------------------------- // Main // Main include is expected to contain vert/frag definitions for the pass // This must be defined after all graph code using (var mainBuilder = new ShaderStringBuilder()) { mainBuilder.AppendLine($"#include \"{pass.varyingsInclude}\""); mainBuilder.AppendLine($"#include \"{pass.passInclude}\""); // Add to splice commands spliceCommands.Add("MainInclude", mainBuilder.ToCodeBlack()); } // -------------------------------------------------- // Debug // Debug output all active fields using (var debugBuilder = new ShaderStringBuilder()) { if (isDebug) { // Active fields debugBuilder.AppendLine("// ACTIVE FIELDS:"); foreach (string field in activeFields.baseInstance.fields) { debugBuilder.AppendLine("// " + field); } } if (debugBuilder.length == 0) { debugBuilder.AppendLine("// <None>"); } // Add to splice commands spliceCommands.Add("Debug", debugBuilder.ToCodeBlack()); } // -------------------------------------------------- // Finalize // Get Template string templateLocation = GetTemplatePath("PassMesh.template"); if (!File.Exists(templateLocation)) { return(false); } // Get Template preprocessor string templatePath = "Assets/com.unity.render-pipelines.universal/Editor/ShaderGraph/Templates"; var templatePreprocessor = new ShaderSpliceUtil.TemplatePreprocessor(activeFields, spliceCommands, isDebug, templatePath, sourceAssetDependencyPaths, assemblyName, resourceClassName); // Process Template templatePreprocessor.ProcessTemplateFile(templateLocation); result.AddShaderChunk(templatePreprocessor.GetShaderCode().ToString(), false); return(true); }
private static ActiveFields GetActiveFieldsFromMasterNode(AbstractMaterialNode iMasterNode, Pass pass) { var activeFields = new ActiveFields(); var baseActiveFields = activeFields.baseInstance; PBRMasterNode masterNode = iMasterNode as PBRMasterNode; if (masterNode == null) { return(activeFields); } if (masterNode.twoSided.isOn) { baseActiveFields.Add("DoubleSided"); if (pass.ShaderPassName != "SHADERPASS_MOTION_VECTORS") // HACK to get around lack of a good interpolator dependency system { // we need to be able to build interpolators using multiple input structs // also: should only require isFrontFace if Normals are required... baseActiveFields.Add("DoubleSided.Mirror"); // TODO: change this depending on what kind of normal flip you want.. baseActiveFields.Add("FragInputs.isFrontFace"); // will need this for determining normal flip mode } } switch (masterNode.model) { case PBRMasterNode.Model.Metallic: break; case PBRMasterNode.Model.Specular: baseActiveFields.Add("Material.SpecularColor"); break; default: // TODO: error! break; } if (masterNode.IsSlotConnected(PBRMasterNode.AlphaThresholdSlotId) || masterNode.GetInputSlots <Vector1MaterialSlot>().First(x => x.id == PBRMasterNode.AlphaThresholdSlotId).value > 0.0f) { baseActiveFields.Add("AlphaTest"); } if (masterNode.surfaceType != UnityEditor.ShaderGraph.SurfaceType.Opaque) { baseActiveFields.Add("SurfaceType.Transparent"); if (masterNode.alphaMode == AlphaMode.Alpha) { baseActiveFields.Add("BlendMode.Alpha"); } else if (masterNode.alphaMode == AlphaMode.Additive) { baseActiveFields.Add("BlendMode.Add"); } // By default PBR node will take the fog baseActiveFields.Add("AlphaFog"); } else { // opaque-only defines } return(activeFields); }
void GenerateShaderPass(int targetIndex, PassDescriptor pass, ActiveFields activeFields) { // Early exit if pass is not used in preview if (m_Mode == GenerationMode.Preview && !pass.useInPreview) { return; } // -------------------------------------------------- // Debug // Get scripting symbols BuildTargetGroup buildTargetGroup = EditorUserBuildSettings.selectedBuildTargetGroup; string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup); bool isDebug = defines.Contains(kDebugSymbol); // -------------------------------------------------- // Setup // Initiailize Collectors var propertyCollector = new PropertyCollector(); var keywordCollector = new KeywordCollector(); m_OutputNode.owner.CollectShaderKeywords(keywordCollector, m_Mode); // Get upstream nodes from ShaderPass port mask List <AbstractMaterialNode> vertexNodes; List <AbstractMaterialNode> pixelNodes; GenerationUtils.GetUpstreamNodesForShaderPass(m_OutputNode, pass, out vertexNodes, out pixelNodes); // Track permutation indices for all nodes List <int>[] vertexNodePermutations = new List <int> [vertexNodes.Count]; List <int>[] pixelNodePermutations = new List <int> [pixelNodes.Count]; // Get active fields from upstream Node requirements ShaderGraphRequirementsPerKeyword graphRequirements; GenerationUtils.GetActiveFieldsAndPermutationsForNodes(m_OutputNode, pass, keywordCollector, vertexNodes, pixelNodes, vertexNodePermutations, pixelNodePermutations, activeFields, out graphRequirements); // GET CUSTOM ACTIVE FIELDS HERE! // Get active fields from ShaderPass GenerationUtils.AddRequiredFields(pass.requiredFields, activeFields.baseInstance); // Get Port references from ShaderPass List <MaterialSlot> pixelSlots; List <MaterialSlot> vertexSlots; if (m_OutputNode is IMasterNode) { pixelSlots = GenerationUtils.FindMaterialSlotsOnNode(pass.pixelPorts, m_OutputNode); vertexSlots = GenerationUtils.FindMaterialSlotsOnNode(pass.vertexPorts, m_OutputNode); } else if (m_OutputNode is SubGraphOutputNode) { pixelSlots = new List <MaterialSlot>() { m_OutputNode.GetInputSlots <MaterialSlot>().FirstOrDefault(), }; vertexSlots = new List <MaterialSlot>(); } else { pixelSlots = new List <MaterialSlot>() { new Vector4MaterialSlot(0, "Out", "Out", SlotType.Output, Vector4.zero) { owner = m_OutputNode }, }; vertexSlots = new List <MaterialSlot>(); } // Function Registry var functionBuilder = new ShaderStringBuilder(); var functionRegistry = new FunctionRegistry(functionBuilder); // Hash table of named $splice(name) commands // Key: splice token // Value: string to splice Dictionary <string, string> spliceCommands = new Dictionary <string, string>(); // -------------------------------------------------- // Dependencies // Propagate active field requirements using dependencies // Must be executed before types are built foreach (var instance in activeFields.all.instances) { GenerationUtils.ApplyFieldDependencies(instance, pass.fieldDependencies); } // -------------------------------------------------- // Pass Setup // Name if (!string.IsNullOrEmpty(pass.displayName)) { spliceCommands.Add("PassName", $"Name \"{pass.displayName}\""); } else { spliceCommands.Add("PassName", "// Name: <None>"); } // Tags if (!string.IsNullOrEmpty(pass.lightMode)) { spliceCommands.Add("LightMode", $"\"LightMode\" = \"{pass.lightMode}\""); } else { spliceCommands.Add("LightMode", "// LightMode: <None>"); } // -------------------------------------------------- // Pass Code // Render State using (var renderStateBuilder = new ShaderStringBuilder()) { // Render states need to be separated by RenderState.Type // The first passing ConditionalRenderState of each type is inserted foreach (RenderStateType type in Enum.GetValues(typeof(RenderStateType))) { var renderStates = pass.renderStates?.Where(x => x.descriptor.type == type); if (renderStates != null) { foreach (RenderStateCollection.Item renderState in renderStates) { if (renderState.TestActive(activeFields)) { renderStateBuilder.AppendLine(renderState.value); break; } } } } string command = GenerationUtils.GetSpliceCommand(renderStateBuilder.ToCodeBlock(), "RenderState"); spliceCommands.Add("RenderState", command); } // Pragmas using (var passPragmaBuilder = new ShaderStringBuilder()) { if (pass.pragmas != null) { foreach (PragmaCollection.Item pragma in pass.pragmas) { if (pragma.TestActive(activeFields)) { passPragmaBuilder.AppendLine(pragma.value); } } } string command = GenerationUtils.GetSpliceCommand(passPragmaBuilder.ToCodeBlock(), "PassPragmas"); spliceCommands.Add("PassPragmas", command); } // Includes using (var preGraphIncludeBuilder = new ShaderStringBuilder()) { if (pass.includes != null) { foreach (IncludeCollection.Item include in pass.includes.Where(x => x.descriptor.location == IncludeLocation.Pregraph)) { if (include.TestActive(activeFields)) { preGraphIncludeBuilder.AppendLine(include.value); } } } string command = GenerationUtils.GetSpliceCommand(preGraphIncludeBuilder.ToCodeBlock(), "PreGraphIncludes"); spliceCommands.Add("PreGraphIncludes", command); } using (var postGraphIncludeBuilder = new ShaderStringBuilder()) { if (pass.includes != null) { foreach (IncludeCollection.Item include in pass.includes.Where(x => x.descriptor.location == IncludeLocation.Postgraph)) { if (include.TestActive(activeFields)) { postGraphIncludeBuilder.AppendLine(include.value); } } } string command = GenerationUtils.GetSpliceCommand(postGraphIncludeBuilder.ToCodeBlock(), "PostGraphIncludes"); spliceCommands.Add("PostGraphIncludes", command); } // Keywords using (var passKeywordBuilder = new ShaderStringBuilder()) { if (pass.keywords != null) { foreach (KeywordCollection.Item keyword in pass.keywords) { if (keyword.TestActive(activeFields)) { passKeywordBuilder.AppendLine(keyword.value); } } } string command = GenerationUtils.GetSpliceCommand(passKeywordBuilder.ToCodeBlock(), "PassKeywords"); spliceCommands.Add("PassKeywords", command); } // ----------------------------- // Generated structs and Packing code var interpolatorBuilder = new ShaderStringBuilder(); var passStructs = new List <StructDescriptor>(); if (pass.structs != null) { passStructs.AddRange(pass.structs.Select(x => x.descriptor)); foreach (StructCollection.Item shaderStruct in pass.structs) { if (shaderStruct.descriptor.packFields == false) { continue; //skip structs that do not need interpolator packs } List <int> packedCounts = new List <int>(); var packStruct = new StructDescriptor(); //generate packed functions if (activeFields.permutationCount > 0) { var generatedPackedTypes = new Dictionary <string, (ShaderStringBuilder, List <int>)>(); foreach (var instance in activeFields.allPermutations.instances) { var instanceGenerator = new ShaderStringBuilder(); GenerationUtils.GenerateInterpolatorFunctions(shaderStruct.descriptor, instance, out instanceGenerator); var key = instanceGenerator.ToCodeBlock(); if (generatedPackedTypes.TryGetValue(key, out var value)) { value.Item2.Add(instance.permutationIndex); } else { generatedPackedTypes.Add(key, (instanceGenerator, new List <int> { instance.permutationIndex }));