浏览代码

Merge pull request #1376 from Unity-Technologies/sg/hdsubshaders

HD Master Node Support
/main
GitHub 7 年前
当前提交
cd218826
共有 30 个文件被更改,包括 2818 次插入267 次删除
  1. 539
      ShaderGraph/HDPipeline/HDUnlitSubShader.cs
  2. 4
      ShaderGraph/com.unity.shadergraph/Editor/Data/Graphs/ShaderGraphRequirements.cs
  3. 2
      ShaderGraph/com.unity.shadergraph/Editor/Data/Interfaces/IGeneratesFunction.cs
  4. 2
      ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Artistic/Adjustment/ChannelMixerNode.cs
  5. 4
      ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Artistic/Adjustment/InvertColorsNode.cs
  6. 2
      ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Artistic/Mask/ChannelMaskNode.cs
  7. 2
      ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Artistic/Normal/NormalCreateNode.cs
  8. 4
      ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Channel/FlipNode.cs
  9. 2
      ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/CodeFunctionNode.cs
  10. 2
      ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Input/Gradient/GradientNode.cs
  11. 2
      ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Input/Scene/FogNode.cs
  12. 2
      ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Math/Basic/MultiplyNode.cs
  13. 2
      ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Math/Matrix/MatrixConstructionNode.cs
  14. 4
      ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Procedural/Noise/GradientNoiseNode.cs
  15. 4
      ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Procedural/Noise/SimpleNoiseNode.cs
  16. 4
      ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Procedural/Noise/VoronoiNode.cs
  17. 2
      ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/UV/FlipbookNode.cs
  18. 6
      ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Utility/SubGraphNode.cs
  19. 6
      ShaderGraph/com.unity.shadergraph/Editor/Data/SubGraph/SubGraph.cs
  20. 533
      ShaderGraph/com.unity.shadergraph/Editor/Data/Util/GraphUtil.cs
  21. 20
      ShaderGraph/com.unity.shadergraph/Editor/Data/Util/ShaderGenerator.cs
  22. 275
      ShaderGraph/com.unity.shadergraph/Editor/Templates/HDUnlitPassForward.template
  23. 790
      ShaderGraph/HDPipeline/HDPBRSubShader.cs
  24. 11
      ShaderGraph/HDPipeline/HDPBRSubShader.cs.meta
  25. 444
      ShaderGraph/HDPipeline/HDSubShaderUtilities.cs
  26. 11
      ShaderGraph/HDPipeline/HDSubShaderUtilities.cs.meta
  27. 21
      ShaderGraph/com.unity.shadergraph/Editor/Data/Util/GraphContext.cs
  28. 11
      ShaderGraph/com.unity.shadergraph/Editor/Data/Util/GraphContext.cs.meta
  29. 366
      ShaderGraph/com.unity.shadergraph/Editor/Templates/HDPBRPass.template
  30. 8
      ShaderGraph/com.unity.shadergraph/Editor/Templates/HDPBRPass.template.meta

539
ShaderGraph/HDPipeline/HDUnlitSubShader.cs


using System.IO;
using System.Linq;
using UnityEditor.Graphing;
using UnityEngine.Experimental.UIElements;
[Serializable]
public class HDUnlitSubShader
// [Serializable] ??
public class HDUnlitSubShader : IUnlitSubShader
static NeededCoordinateSpace m_VertexCoordinateSpace = NeededCoordinateSpace.Object;
static NeededCoordinateSpace m_PixelCoordinateSpace = NeededCoordinateSpace.World;
struct Pass
Pass m_PassDepthOnly = new Pass()
public string Name;
public string ShaderPassName;
public string ShaderPassInclude;
public List<int> VertexShaderSlots;
public List<int> PixelShaderSlots;
}
Name = "Depth prepass",
LightMode = "DepthForwardOnly",
TemplateName = "HDUnlitPassForward.template",
ShaderPassName = "SHADERPASS_DEPTH_ONLY",
ZWriteOverride = "ZWrite On",
Includes = new List<string>()
{
"#include \"HDRP/ShaderPass/ShaderPassDepthOnly.hlsl\"",
},
PixelShaderSlots = new List<int>()
{
UnlitMasterNode.AlphaSlotId,
UnlitMasterNode.AlphaThresholdSlotId
}
};
Pass m_UnlitPassForwardOnly = new Pass()
Pass m_PassForward = new Pass()
Name = "ForwardOnly",
Name = "Forward Unlit",
LightMode = "ForwardOnly",
TemplateName = "HDUnlitPassForward.template",
ShaderPassInclude = "ShaderPassForwardUnlit",
ExtraDefines = new List<string>()
{
"#pragma multi_compile _ DEBUG_DISPLAY"
},
Includes = new List<string>()
{
"#include \"HDRP/ShaderPass/ShaderPassForwardUnlit.hlsl\"",
},
PixelShaderSlots = new List<int>()
{
UnlitMasterNode.ColorSlotId,

};
Pass m_UnlitPassForwardDepthOnly = new Pass()
Pass m_PassMETA = new Pass()
Name = "DepthForwardOnly",
ShaderPassName = "SHADERPASS_DEPTH_ONLY",
ShaderPassInclude = "ShaderPassDepthOnly",
Name = "META",
LightMode = "Meta",
TemplateName = "HDUnlitPassForward.template",
ShaderPassName = "SHADERPASS_LIGHT_TRANSPORT",
CullOverride = "Cull Off",
Includes = new List<string>()
{
"#include \"HDRP/ShaderPass/ShaderPassLightTransport.hlsl\"",
},
RequiredFields = new List<string>()
{
"AttributesMesh.normalOS",
"AttributesMesh.tangentOS", // Always present as we require it also in case of anisotropic lighting
"AttributesMesh.uv0",
"AttributesMesh.uv1",
"AttributesMesh.color",
"AttributesMesh.uv2", // SHADERPASS_LIGHT_TRANSPORT always uses uv2
},
PixelShaderSlots = new List<int>()
{
UnlitMasterNode.ColorSlotId,

};
public string GetSubshader(IMasterNode inMasterNode, GenerationMode mode)
Pass m_PassDistortion = new Pass()
var templatePath = GetTemplatePath("HDUnlitPassForward.template");
if (!File.Exists(templatePath))
return string.Empty;
string passTemplate = File.ReadAllText(templatePath);
var masterNode = inMasterNode as UnlitMasterNode;
var subShader = new ShaderStringBuilder();
subShader.AppendLine("SubShader");
using(subShader.BlockScope())
Name = "Distortion",
LightMode = "DistortionVectors",
TemplateName = "HDUnlitPassForward.template",
ShaderPassName = "SHADERPASS_DISTORTION",
BlendOverride = "Blend One One, One One", // [_DistortionSrcBlend] [_DistortionDstBlend], [_DistortionBlurSrcBlend] [_DistortionBlurDstBlend]
BlendOpOverride = "BlendOp Add, Add", // Add, [_DistortionBlurBlendOp]
ZTestOverride = "ZTest LEqual", // [_ZTestModeDistortion]
ZWriteOverride = "ZWrite Off",
Includes = new List<string>()
{
"#include \"HDRP/ShaderPass/ShaderPassDistortion.hlsl\"",
},
PixelShaderSlots = new List<int>()
subShader.AppendLine("Tags{ \"RenderPipeline\" = \"HDRenderPipeline\"}");
subShader.AppendLine("Tags{ \"RenderType\" = \"Opaque\" }");
subShader.AppendLines(
GetShaderPassFromTemplate(
passTemplate,
masterNode,
m_UnlitPassForwardDepthOnly,
mode));
subShader.AppendLines(
GetShaderPassFromTemplate(
passTemplate,
masterNode,
m_UnlitPassForwardOnly,
mode));
PBRMasterNode.AlphaSlotId,
PBRMasterNode.AlphaThresholdSlotId
return subShader.ToString();
}
};
static string GetTemplatePath(string templateName)
private static string GetVariantDefines(UnlitMasterNode masterNode)
var templatePath = ShaderGenerator.GetTemplatePath(templateName);
if (File.Exists(templatePath))
return templatePath;
throw new FileNotFoundException(string.Format(@"Cannot find a template with name ""{0}"".", templateName));
}
private static string GetShaderPassFromTemplate(string template, UnlitMasterNode masterNode, Pass pass, GenerationMode mode)
{
// ----------------------------------------------------- //
// SETUP //
// ----------------------------------------------------- //
// -------------------------------------
// String builders
var shaderProperties = new PropertyCollector();
var functionBuilder = new ShaderStringBuilder(1);
var functionRegistry = new FunctionRegistry(functionBuilder);
ShaderGenerator defines = new ShaderGenerator();
var defines = new ShaderStringBuilder(1);
var graph = new ShaderStringBuilder(0);
// #pragma shader_feature _ALPHATEST_ON
float constantAlpha = 0.0f;
if (masterNode.IsSlotConnected(PBRMasterNode.AlphaThresholdSlotId) ||
(float.TryParse(masterNode.GetSlotValue(PBRMasterNode.AlphaThresholdSlotId, GenerationMode.ForReals), out constantAlpha) && (constantAlpha > 0.0f)))
{
defines.AddShaderChunk("#define _ALPHATEST_ON 1", true);
}
var surfaceDescriptionInputStruct = new ShaderStringBuilder(1);
var surfaceDescriptionFunction = new ShaderStringBuilder(1);
var surfaceDescriptionStruct = new ShaderStringBuilder(1);
// if (kTesselationMode != TessellationMode.None)
// {
// defines.AddShaderChunk("#define _TESSELLATION_PHONG 1", true);
// }
var pixelShader = new ShaderStringBuilder(2);
var pixelShaderSurfaceInputs = new ShaderStringBuilder(2);
var pixelShaderSurfaceRemap = new ShaderStringBuilder(2);
// #pragma shader_feature _ _VERTEX_DISPLACEMENT _PIXEL_DISPLACEMENT
// switch (kDisplacementMode)
// {
// case DisplacementMode.None:
// break;
// case DisplacementMode.Vertex:
// defines.AddShaderChunk("#define _VERTEX_DISPLACEMENT 1", true);
// break;
// case DisplacementMode.Pixel:
// defines.AddShaderChunk("#define _PIXEL_DISPLACEMENT 1", true);
// Depth offset is only enabled if per pixel displacement is
// if (kDepthOffsetEnable)
// {
// // #pragma shader_feature _DEPTHOFFSET_ON
// defines.AddShaderChunk("#define _DEPTHOFFSET_ON 1", true);
// }
// break;
// case DisplacementMode.Tessellation:
// if (kTessellationEnabled)
// {
// defines.AddShaderChunk("#define _TESSELLATION_DISPLACEMENT 1", true);
// }
// break;
// }
// -------------------------------------
// Get Slot and Node lists per stage
// #pragma shader_feature _VERTEX_DISPLACEMENT_LOCK_OBJECT_SCALE
// #pragma shader_feature _DISPLACEMENT_LOCK_TILING_SCALE
// #pragma shader_feature _PIXEL_DISPLACEMENT_LOCK_OBJECT_SCALE
// #pragma shader_feature _VERTEX_WIND
// #pragma shader_feature _ _REFRACTION_PLANE _REFRACTION_SPHERE
//
// #pragma shader_feature _ _MAPPING_PLANAR _MAPPING_TRIPLANAR // MOVE to a node
// #pragma shader_feature _NORMALMAP_TANGENT_SPACE
// #pragma shader_feature _ _REQUIRE_UV2 _REQUIRE_UV3
var pixelSlots = pass.PixelShaderSlots.Select(masterNode.FindSlot<MaterialSlot>).ToList();
var pixelNodes = ListPool<INode>.Get();
NodeUtils.DepthFirstCollectNodesFromNode(pixelNodes, masterNode, NodeUtils.IncludeSelf.Include, pass.PixelShaderSlots);
// #pragma shader_feature _MASKMAP
// #pragma shader_feature _BENTNORMALMAP
// #pragma shader_feature _EMISSIVE_COLOR_MAP
// #pragma shader_feature _ENABLESPECULAROCCLUSION
// #pragma shader_feature _HEIGHTMAP
// #pragma shader_feature _TANGENTMAP
// #pragma shader_feature _ANISOTROPYMAP
// #pragma shader_feature _DETAIL_MAP // MOVE to a node
// #pragma shader_feature _SUBSURFACE_RADIUS_MAP
// #pragma shader_feature _THICKNESSMAP
// #pragma shader_feature _SPECULARCOLORMAP
// #pragma shader_feature _TRANSMITTANCECOLORMAP
// -------------------------------------
// Get Requirements
// Keywords for transparent
// #pragma shader_feature _SURFACE_TYPE_TRANSPARENT
if (masterNode.surfaceType != SurfaceType.Opaque)
{
// transparent-only defines
defines.AddShaderChunk("#define _SURFACE_TYPE_TRANSPARENT 1", true);
var pixelRequirements = ShaderGraphRequirements.FromNodes(pixelNodes);
// #pragma shader_feature _ _BLENDMODE_ALPHA _BLENDMODE_ADD _BLENDMODE_PRE_MULTIPLY
if (masterNode.alphaMode == AlphaMode.Alpha)
{
defines.AddShaderChunk("#define _BLENDMODE_ALPHA 1", true);
}
else if (masterNode.alphaMode == AlphaMode.Additive)
{
defines.AddShaderChunk("#define _BLENDMODE_ADD 1", true);
}
// else if (masterNode.alphaMode == PBRMasterNode.AlphaMode.PremultiplyAlpha) // TODO
// {
// defines.AddShaderChunk("#define _BLENDMODE_PRE_MULTIPLY 1", true);
// }
// ----------------------------------------------------- //
// START SHADER GENERATION //
// ----------------------------------------------------- //
// #pragma shader_feature _BLENDMODE_PRESERVE_SPECULAR_LIGHTING
// if (kEnableBlendModePreserveSpecularLighting)
// {
// defines.AddShaderChunk("#define _BLENDMODE_PRESERVE_SPECULAR_LIGHTING 1", true);
// }
// -------------------------------------
// Calculate material options
// #pragma shader_feature _ENABLE_FOG_ON_TRANSPARENT
// if (kEnableFogOnTransparent)
// {
// defines.AddShaderChunk("#define _ENABLE_FOG_ON_TRANSPARENT 1", true);
// }
}
else
{
// opaque-only defines
}
var tagsBuilder = new ShaderStringBuilder(1);
var blendingBuilder = new ShaderStringBuilder(1);
var cullingBuilder = new ShaderStringBuilder(1);
var zTestBuilder = new ShaderStringBuilder(1);
var zWriteBuilder = new ShaderStringBuilder(1);
// enable dithering LOD crossfade
// #pragma multi_compile _ LOD_FADE_CROSSFADE
// TODO: We should have this keyword only if VelocityInGBuffer is enable, how to do that ?
//#pragma multi_compile VELOCITYOUTPUT_OFF VELOCITYOUTPUT_ON
var materialOptions = new SurfaceMaterialOptions();
materialOptions.GetTags(tagsBuilder);
materialOptions.GetBlend(blendingBuilder);
materialOptions.GetCull(cullingBuilder);
materialOptions.GetDepthTest(zTestBuilder);
materialOptions.GetDepthWrite(zWriteBuilder);
return defines.GetShaderString(2);
}
// -------------------------------------
// Generate defines
defines.AppendLine("#define SHADERPASS {0}", pass.ShaderPassName);
foreach (var channel in pixelRequirements.requiresMeshUVs.Distinct())
private static bool GenerateShaderPass(UnlitMasterNode masterNode, Pass pass, GenerationMode mode, SurfaceMaterialOptions materialOptions, ShaderGenerator result)
{
var templateLocation = ShaderGenerator.GetTemplatePath(pass.TemplateName);
if (!File.Exists(templateLocation))
defines.AppendLine("#define ATTRIBUTES_NEED_TEXCOORD{0}", (int)channel);
defines.AppendLine("#define VARYINGS_NEED_TEXCOORD{0}", (int)channel);
// TODO: produce error here
return false;
if (masterNode.IsSlotConnected(PBRMasterNode.AlphaThresholdSlotId))
defines.AppendLine("#define _ALPHATEST_ON");
// ----------------------------------------------------- //
// START SURFACE DESCRIPTION //
// ----------------------------------------------------- //
// -------------------------------------
// Generate Input structure for Surface Description function
// Surface Description Input requirements are needed to exclude intermediate translation spaces
// grab all of the active nodes
var activeNodeList = ListPool<INode>.Get();
NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, masterNode, NodeUtils.IncludeSelf.Include, pass.PixelShaderSlots);
surfaceDescriptionInputStruct.AppendLine("struct SurfaceDescriptionInputs", false);
using(surfaceDescriptionInputStruct.BlockSemicolonScope())
{
ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(pixelRequirements.requiresNormal, InterpolatorType.Normal, surfaceDescriptionInputStruct);
ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(pixelRequirements.requiresTangent, InterpolatorType.Tangent, surfaceDescriptionInputStruct);
ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(pixelRequirements.requiresBitangent, InterpolatorType.BiTangent, surfaceDescriptionInputStruct);
ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(pixelRequirements.requiresViewDir, InterpolatorType.ViewDirection, surfaceDescriptionInputStruct);
ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(pixelRequirements.requiresPosition, InterpolatorType.Position, surfaceDescriptionInputStruct);
if (pixelRequirements.requiresVertexColor)
surfaceDescriptionInputStruct.AppendLine("float4 {0};", ShaderGeneratorNames.VertexColor);
// graph requirements describe what the graph itself requires
var graphRequirements = ShaderGraphRequirements.FromNodes(activeNodeList, ShaderStageCapability.All, true, true);
if (pixelRequirements.requiresScreenPosition)
surfaceDescriptionInputStruct.AppendLine("float4 {0};", ShaderGeneratorNames.ScreenPosition);
ShaderStringBuilder graphNodeFunctions = new ShaderStringBuilder();
graphNodeFunctions.IncreaseIndent();
var functionRegistry = new FunctionRegistry(graphNodeFunctions);
foreach (var channel in pixelRequirements.requiresMeshUVs.Distinct())
// Build the list of active slots based on what the pass requires
// TODO: this can be a shared function -- From here through GraphUtil.GenerateSurfaceDescription(..)
var activeSlots = new List<MaterialSlot>();
foreach (var id in pass.PixelShaderSlots)
surfaceDescriptionInputStruct.AppendLine("half4 {0};", channel.GetUVName());
MaterialSlot slot = masterNode.FindSlot<MaterialSlot>(id);
if (slot != null)
{
activeSlots.Add(slot);
// -------------------------------------
// Generate Output structure for Surface Description function
// build the graph outputs structure to hold the results of each active slots (and fill out activeFields to indicate they are active)
string graphInputStructName = "SurfaceDescriptionInputs";
string graphOutputStructName = "SurfaceDescription";
string graphEvalFunctionName = "SurfaceDescriptionFunction";
var graphEvalFunction = new ShaderStringBuilder();
var graphOutputs = new ShaderStringBuilder();
PropertyCollector graphProperties = new PropertyCollector();
GraphUtil.GenerateSurfaceDescriptionStruct(surfaceDescriptionStruct, pixelSlots, true);
// -------------------------------------
// Generate Surface Description function
// build the graph outputs structure, and populate activeFields with the fields of that structure
HashSet<string> activeFields = new HashSet<string>();
GraphUtil.GenerateSurfaceDescriptionStruct(graphOutputs, activeSlots, true);
// Build the graph evaluation code, to evaluate the specified slots
pixelNodes,
activeNodeList,
surfaceDescriptionFunction,
graphEvalFunction,
shaderProperties,
pixelRequirements,
graphProperties,
graphRequirements, // TODO : REMOVE UNUSED
"PopulateSurfaceData",
"SurfaceDescription",
graphEvalFunctionName,
graphOutputStructName,
pixelSlots);
activeSlots,
graphInputStructName);
// ----------------------------------------------------- //
// GENERATE VERTEX > PIXEL PIPELINE //
// ----------------------------------------------------- //
var blendCode = new ShaderStringBuilder();
var cullCode = new ShaderStringBuilder();
var zTestCode = new ShaderStringBuilder();
var zWriteCode = new ShaderStringBuilder();
var stencilCode = new ShaderStringBuilder();
var colorMaskCode = new ShaderStringBuilder();
HDSubShaderUtilities.BuildRenderStatesFromPassAndMaterialOptions(pass, materialOptions, blendCode, cullCode, zTestCode, zWriteCode, stencilCode, colorMaskCode);
if (masterNode.twoSided.isOn)
{
activeFields.Add("DoubleSided");
if (pass.ShaderPassName != "SHADERPASS_VELOCITY") // 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...
activeFields.Add("DoubleSided.Mirror"); // TODO: change this depending on what kind of normal flip you want..
activeFields.Add("FragInputs.isFrontFace"); // will need this for determining normal flip mode
}
}
if (pass.PixelShaderSlots != null)
{
foreach (var slotId in pass.PixelShaderSlots)
{
var slot = masterNode.FindSlot<MaterialSlot>(slotId);
if(slot != null)
{
var rawSlotName = slot.RawDisplayName().ToString();
var descriptionVar = string.Format("{0}.{1}", graphOutputStructName, rawSlotName);
activeFields.Add(descriptionVar);
}
}
}
// -------------------------------------
// TODO - Why is this not a full generation?
// Generate standard transformations
var packedInterpolatorCode = new ShaderGenerator();
var graphInputs = new ShaderGenerator();
HDRPShaderStructs.Generate(
packedInterpolatorCode,
graphInputs,
graphRequirements,
pass.RequiredFields,
CoordinateSpace.World,
activeFields);
foreach (var channel in pixelRequirements.requiresMeshUVs.Distinct())
pixelShaderSurfaceInputs.AppendLine("surfaceInput.{0} = {1};", channel.GetUVName(), string.Format("half4(input.texCoord{0}, 0, 0)", (int)channel));
// debug output all active fields
var interpolatorDefines = new ShaderGenerator();
{
interpolatorDefines.AddShaderChunk("// ACTIVE FIELDS:");
foreach (string f in activeFields)
{
interpolatorDefines.AddShaderChunk("// " + f);
}
}
// -------------------------------------
// Generate pixel shader surface remap
ShaderGenerator defines = new ShaderGenerator();
{
defines.AddShaderChunk(string.Format("#define SHADERPASS {0}", pass.ShaderPassName), true);
if (pass.ExtraDefines != null)
{
foreach (var define in pass.ExtraDefines)
defines.AddShaderChunk(define);
}
defines.AddGenerator(interpolatorDefines);
}
foreach (var slot in pixelSlots)
var shaderPassIncludes = new ShaderGenerator();
if (pass.Includes != null)
pixelShaderSurfaceRemap.AppendLine("{0} = surf.{0};", slot.shaderOutputName);
foreach (var include in pass.Includes)
shaderPassIncludes.AddShaderChunk(include);
// ----------------------------------------------------- //
// FINALIZE //
// ----------------------------------------------------- //
// -------------------------------------
// Combine Graph sections
graph.AppendLine(shaderProperties.GetPropertiesDeclaration(1));
// build graph code
var graph = new ShaderGenerator();
graph.AddShaderChunk("// Graph Inputs");
graph.Indent();
graph.AddGenerator(graphInputs);
graph.Deindent();
graph.AddShaderChunk("// Graph Outputs");
graph.Indent();
graph.AddShaderChunk(graphOutputs.ToString());
//graph.AddGenerator(graphOutputs);
graph.Deindent();
graph.AddShaderChunk("// Graph Properties (uniform inputs)");
graph.AddShaderChunk(graphProperties.GetPropertiesDeclaration(1));
graph.AddShaderChunk("// Graph Node Functions");
graph.AddShaderChunk(graphNodeFunctions.ToString());
graph.AddShaderChunk("// Graph Evaluation");
graph.Indent();
graph.AddShaderChunk(graphEvalFunction.ToString());
//graph.AddGenerator(graphEvalFunction);
graph.Deindent();
graph.AppendLine(functionBuilder.ToString());
// build the hash table of all named fragments TODO: could make this Dictionary<string, ShaderGenerator / string> ?
Dictionary<string, string> namedFragments = new Dictionary<string, string>();
namedFragments.Add("${Defines}", defines.GetShaderString(2, false));
namedFragments.Add("${Graph}", graph.GetShaderString(2, false));
namedFragments.Add("${LightMode}", pass.LightMode);
namedFragments.Add("${PassName}", pass.Name);
namedFragments.Add("${Includes}", shaderPassIncludes.GetShaderString(2, false));
namedFragments.Add("${InterpolatorPacking}", packedInterpolatorCode.GetShaderString(2, false));
namedFragments.Add("${Blending}", blendCode.ToString());
namedFragments.Add("${Culling}", cullCode.ToString());
namedFragments.Add("${ZTest}", zTestCode.ToString());
namedFragments.Add("${ZWrite}", zWriteCode.ToString());
namedFragments.Add("${Stencil}", stencilCode.ToString());
namedFragments.Add("${ColorMask}", colorMaskCode.ToString());
namedFragments.Add("${LOD}", materialOptions.lod.ToString());
namedFragments.Add("${VariantDefines}", GetVariantDefines(masterNode));
graph.AppendLine(surfaceDescriptionInputStruct.ToString());
graph.AppendLine(surfaceDescriptionStruct.ToString());
graph.AppendLine(surfaceDescriptionFunction.ToString());
// process the template to generate the shader code for this pass TODO: could make this a shared function
string[] templateLines = File.ReadAllLines(templateLocation);
System.Text.StringBuilder builder = new System.Text.StringBuilder();
foreach (string line in templateLines)
{
ShaderSpliceUtil.PreprocessShaderCode(line, activeFields, namedFragments, builder);
builder.AppendLine();
}
result.AddShaderChunk(builder.ToString(), false);
// -------------------------------------
// Generate final subshader
return true;
}
public string GetSubshader(IMasterNode inMasterNode, GenerationMode mode)
{
var masterNode = inMasterNode as UnlitMasterNode;
var subShader = new ShaderGenerator();
subShader.AddShaderChunk("SubShader", true);
subShader.AddShaderChunk("{", true);
subShader.Indent();
{
SurfaceMaterialOptions materialOptions = HDSubShaderUtilities.BuildMaterialOptions(masterNode.surfaceType, masterNode.alphaMode, masterNode.twoSided.isOn);
// Add tags at the SubShader level
{
var tagsVisitor = new ShaderStringBuilder();
materialOptions.GetTags(tagsVisitor);
subShader.AddShaderChunk(tagsVisitor.ToString(), false);
}
var resultPass = template.Replace("${Tags}", tagsBuilder.ToString());
resultPass = resultPass.Replace("${Blending}", blendingBuilder.ToString());
resultPass = resultPass.Replace("${Culling}", cullingBuilder.ToString());
resultPass = resultPass.Replace("${ZTest}", zTestBuilder.ToString());
resultPass = resultPass.Replace("${ZWrite}", zWriteBuilder.ToString());
resultPass = resultPass.Replace("${Defines}", defines.ToString());
resultPass = resultPass.Replace("${LOD}", "" + materialOptions.lod);
// generate the necessary shader passes
// bool opaque = (masterNode.surfaceType == SurfaceType.Opaque);
// bool transparent = (masterNode.surfaceType != SurfaceType.Opaque);
bool distortionActive = false;
resultPass = resultPass.Replace("${LightMode}", pass.Name);
resultPass = resultPass.Replace("${ShaderPassInclude}", pass.ShaderPassInclude);
resultPass = resultPass.Replace("${Graph}", graph.ToString());
GenerateShaderPass(masterNode, m_PassDepthOnly, mode, materialOptions, subShader);
GenerateShaderPass(masterNode, m_PassForward, mode, materialOptions, subShader);
GenerateShaderPass(masterNode, m_PassMETA, mode, materialOptions, subShader);
if (distortionActive)
{
GenerateShaderPass(masterNode, m_PassDistortion, mode, materialOptions, subShader);
}
}
subShader.Deindent();
subShader.AddShaderChunk("}", true);
resultPass = resultPass.Replace("${PixelShader}", pixelShader.ToString());
resultPass = resultPass.Replace("${PixelShaderSurfaceInputs}", pixelShaderSurfaceInputs.ToString());
resultPass = resultPass.Replace("${PixelShaderSurfaceRemap}", pixelShaderSurfaceRemap.ToString());
return resultPass;
return subShader.GetShaderString(0);
}
}

4
ShaderGraph/com.unity.shadergraph/Editor/Data/Graphs/ShaderGraphRequirements.cs


return newReqs;
}
public static ShaderGraphRequirements FromNodes<T>(List<T> nodes, ShaderStageCapability stageCapability = ShaderStageCapability.All, bool includeIntermediateSpaces = true)
public static ShaderGraphRequirements FromNodes<T>(List<T> nodes, ShaderStageCapability stageCapability = ShaderStageCapability.All, bool includeIntermediateSpaces = true, bool HDRPBehavior = false)
where T : class, INode
{
NeededCoordinateSpace requiresNormal = nodes.OfType<IMayRequireNormal>().Aggregate(NeededCoordinateSpace.None, (mask, node) => mask | node.RequiresNormal(stageCapability));

| requiresNormal;
var needsTangentSpace = (compoundSpaces & NeededCoordinateSpace.Tangent) > 0;
if (needsTangentSpace)
if (needsTangentSpace && !HDRPBehavior)
{
requiresBitangent |= NeededCoordinateSpace.World;
requiresNormal |= NeededCoordinateSpace.World;

2
ShaderGraph/com.unity.shadergraph/Editor/Data/Interfaces/IGeneratesFunction.cs


{
public interface IGeneratesFunction
{
void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode);
void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode);
}
}

2
ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Artistic/Adjustment/ChannelMixerNode.cs


});
}
public void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
public void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode)
{
registry.ProvideFunction(GetFunctionName(), s =>
{

4
ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Artistic/Adjustment/InvertColorsNode.cs


});
}
public void GenerateNodeFunction(ShaderGenerator visitor, GenerationMode generationMode)
public void GenerateNodeFunction(ShaderGenerator visitor, GraphContext graphContext, GenerationMode generationMode)
{
var sb = new ShaderStringBuilder();
sb.AppendLine("void {0}({1} In, {2} InvertColors, out {3} Out)",

visitor.AddShaderChunk(sb.ToString(), true);
}
public void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
public void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode)
{
registry.ProvideFunction(GetFunctionName(), s =>
{

2
ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Artistic/Mask/ChannelMaskNode.cs


return GetFunctionName() + " (" + inputValue + ", " + outputValue + ");";
}
public void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
public void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode)
{
ValidateChannelCount();
registry.ProvideFunction(GetFunctionName(), s =>

2
ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Artistic/Normal/NormalCreateNode.cs


visitor.AddShaderChunk(sb.ToString(), true);
}
public void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
public void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode)
{
registry.ProvideFunction(GetFunctionName(), s =>
{

4
ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Channel/FlipNode.cs


});
}
public void GenerateNodeFunction(ShaderGenerator visitor, GenerationMode generationMode)
public void GenerateNodeFunction(ShaderGenerator visitor, GraphContext graphContext, GenerationMode generationMode)
{
var sb = new ShaderStringBuilder();
sb.AppendLine("void {0}({1} In, {2} Flip, out {3} Out)",

visitor.AddShaderChunk(sb.ToString(), true);
}
public void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
public void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode)
{
registry.ProvideFunction(GetFunctionName(), s =>
{

2
ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/CodeFunctionNode.cs


return result;
}
public virtual void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
public virtual void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode)
{
registry.ProvideFunction(GetFunctionName(), s =>
{

2
ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Input/Gradient/GradientNode.cs


RemoveSlotsNameNotMatching(new[] { OutputSlotId });
}
public void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
public void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode)
{
if(generationMode == GenerationMode.Preview)
{

2
ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Input/Scene/FogNode.cs


GetVariableNameForSlot(OutputSlotId), GetVariableNameForSlot(OutputSlot1Id)), false);
}
public void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
public void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode)
{
registry.ProvideFunction(GetFunctionName(), s =>
{

2
ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Math/Basic/MultiplyNode.cs


FindInputSlot<MaterialSlot>(Input2SlotId).concreteValueType.ToString(precision));
}
public void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
public void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode)
{
registry.ProvideFunction(GetFunctionName(), s =>
{

2
ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Math/Matrix/MatrixConstructionNode.cs


visitor.AddShaderChunk(sb.ToString(), false);
}
public void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
public void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode)
{
registry.ProvideFunction(GetFunctionName(), s =>
{

4
ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Procedural/Noise/GradientNoiseNode.cs


return "{ Out = unity_gradientNoise(UV * Scale) + 0.5; }";
}
public override void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
public override void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode)
{
registry.ProvideFunction("unity_gradientNoise_dir", s => s.Append(@"
float2 unity_gradientNoise_dir(float2 p)

}
"));
base.GenerateNodeFunction(registry, generationMode);
base.GenerateNodeFunction(registry, graphContext, generationMode);
}
}
}

4
ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Procedural/Noise/SimpleNoiseNode.cs


";
}
public override void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
public override void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode)
{
registry.ProvideFunction("unity_noise_randomValue", s => s.Append(@"
inline float unity_noise_randomValue (float2 uv)

return t;
}"));
base.GenerateNodeFunction(registry, generationMode);
base.GenerateNodeFunction(registry, graphContext, generationMode);
}
}
}

4
ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Procedural/Noise/VoronoiNode.cs


";
}
public override void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
public override void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode)
{
registry.ProvideFunction("unity_voronoi_noise_randomVector", s => s.Append(@"
inline float2 unity_voronoi_noise_randomVector (float2 UV, float offset)

return float2(sin(UV.y*+offset)*0.5+0.5, cos(UV.x*offset)*0.5+0.5);
}
"));
base.GenerateNodeFunction(registry, generationMode);
base.GenerateNodeFunction(registry, graphContext, generationMode);
}
}
}

2
ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/UV/FlipbookNode.cs


});
}
public void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
public void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode)
{
registry.ProvideFunction(GetFunctionName(), s =>
{

6
ShaderGraph/com.unity.shadergraph/Editor/Data/Nodes/Utility/SubGraphNode.cs


return string.Format("sg_{0}_{1}", functionName, GuidEncoder.Encode(referencedGraph.guid));
}
public virtual void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
public virtual void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode)
referencedGraph.GenerateNodeFunction(registry, GenerationMode.ForReals);
referencedGraph.GenerateSubGraphFunction(SubGraphFunctionName(), registry, ShaderGraphRequirements.FromNodes(new List<INode> {this}), GenerationMode.ForReals);
referencedGraph.GenerateNodeFunction(registry, graphContext, GenerationMode.ForReals);
referencedGraph.GenerateSubGraphFunction(SubGraphFunctionName(), registry, graphContext, ShaderGraphRequirements.FromNodes(new List<INode> {this}), GenerationMode.ForReals);
}
public NeededCoordinateSpace RequiresNormal(ShaderStageCapability stageCapability)

6
ShaderGraph/com.unity.shadergraph/Editor/Data/SubGraph/SubGraph.cs


}
}
public void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
public void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode)
(node as IGeneratesFunction).GenerateNodeFunction(registry, generationMode);
(node as IGeneratesFunction).GenerateNodeFunction(registry, graphContext, generationMode);
}
}

}
}
public void GenerateSubGraphFunction(string functionName, FunctionRegistry registry, ShaderGraphRequirements reqs, GenerationMode generationMode)
public void GenerateSubGraphFunction(string functionName, FunctionRegistry registry, GraphContext graphContext, ShaderGraphRequirements reqs, GenerationMode generationMode)
{
registry.ProvideFunction(functionName, s =>
{

533
ShaderGraph/com.unity.shadergraph/Editor/Data/Util/GraphUtil.cs


using UnityEditor.Graphing;
using UnityEditor.Graphing.Util;
using UnityEngine;
using System.Reflection;
using System.Text.RegularExpressions;
// a structure used to track active variable dependencies in the shader code
// (i.e. the use of uv0 in the pixel shader means we need a uv0 interpolator, etc.)
public struct Dependency
{
public string name; // the name of the thing
public string dependsOn; // the thing above depends on this -- it reads it / calls it / requires it to be defined
public Dependency(string name, string dependsOn)
{
this.name = name;
this.dependsOn = dependsOn;
}
};
// attribute used to flag a field as needing an HLSL semantic applied
// i.e. float3 position : POSITION;
// ^ semantic
[System.AttributeUsage(System.AttributeTargets.Field)]
public class Semantic : System.Attribute
{
public string semantic;
public Semantic(string semantic)
{
this.semantic = semantic;
}
}
// attribute used to flag a field as being optional
// i.e. if it is not active, then we can omit it from the struct
[System.AttributeUsage(System.AttributeTargets.Field)]
public class Optional : System.Attribute
{
public Optional()
{
}
}
// attribute used to override the HLSL type of a field with a custom type string
[System.AttributeUsage(System.AttributeTargets.Field)]
public class OverrideType : System.Attribute
{
public string typeName;
public OverrideType(string typeName)
{
this.typeName = typeName;
}
}
// attribute used to disable a field using a preprocessor #if
[System.AttributeUsage(System.AttributeTargets.Field)]
public class PreprocessorIf : System.Attribute
{
public string conditional;
public PreprocessorIf(string conditional)
{
this.conditional = conditional;
}
}
public static class ShaderSpliceUtil
{
private static int GetFloatVectorCount(string typeName)
{
if (typeName.Equals("Vector4"))
{
return 4;
}
else if (typeName.Equals("Vector3"))
{
return 3;
}
else if (typeName.Equals("Vector2"))
{
return 2;
}
else if (typeName.Equals("float"))
{
return 1;
}
else
{
return 0;
}
}
private static string[] vectorTypeNames =
{
"unknown",
"float",
"float2",
"float3",
"float4"
};
private static char[] channelNames =
{ 'x', 'y', 'z', 'w' };
private static string GetChannelSwizzle(int firstChannel, int channelCount)
{
System.Text.StringBuilder result = new System.Text.StringBuilder();
int lastChannel = System.Math.Min(firstChannel + channelCount - 1, 4);
for (int index = firstChannel; index <= lastChannel; index++)
{
result.Append(channelNames[index]);
}
return result.ToString();
}
private static bool ShouldSpliceField(System.Type parentType, FieldInfo field, HashSet<string> activeFields, out bool isOptional)
{
bool fieldActive = true;
isOptional = field.IsDefined(typeof(Optional), false);
if (isOptional)
{
string fullName = parentType.Name + "." + field.Name;
if (!activeFields.Contains(fullName))
{
// not active, skip the optional field
fieldActive = false;
}
}
return fieldActive;
}
private static string GetFieldSemantic(FieldInfo field)
{
string semanticString = null;
object[] semantics = field.GetCustomAttributes(typeof(Semantic), false);
if (semantics.Length > 0)
{
Semantic firstSemantic = (Semantic) semantics[0];
semanticString = " : " + firstSemantic.semantic;
}
return semanticString;
}
private static string GetFieldType(FieldInfo field, out int floatVectorCount)
{
string fieldType;
object[] overrideType = field.GetCustomAttributes(typeof(OverrideType), false);
if (overrideType.Length > 0)
{
OverrideType first = (OverrideType)overrideType[0];
fieldType = first.typeName;
floatVectorCount = 0;
}
else
{
// TODO: handle non-float types
floatVectorCount = GetFloatVectorCount(field.FieldType.Name);
fieldType = vectorTypeNames[floatVectorCount];
}
return fieldType;
}
private static bool IsFloatVectorType(string type)
{
return GetFloatVectorCount(type) != 0;
}
private static string GetFieldConditional(FieldInfo field)
{
string conditional = null;
object[] overrideType = field.GetCustomAttributes(typeof(PreprocessorIf), false);
if (overrideType.Length > 0)
{
PreprocessorIf first = (PreprocessorIf) overrideType[0];
conditional = first.conditional;
}
return conditional;
}
public static void BuildType(System.Type t, HashSet<string> activeFields, ShaderGenerator result)
{
result.AddShaderChunk("struct " + t.Name + " {");
result.Indent();
foreach (FieldInfo field in t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
{
if (field.MemberType == MemberTypes.Field)
{
bool isOptional;
if (ShouldSpliceField(t, field, activeFields, out isOptional))
{
string semanticString = GetFieldSemantic(field);
int floatVectorCount;
string fieldType = GetFieldType(field, out floatVectorCount);
string conditional = GetFieldConditional(field);
if (conditional != null)
{
result.AddShaderChunk("#if " + conditional);
}
string fieldDecl = fieldType + " " + field.Name + semanticString + ";" + (isOptional ? " // optional" : string.Empty);
result.AddShaderChunk(fieldDecl);
if (conditional != null)
{
result.AddShaderChunk("#endif // " + conditional);
}
}
}
}
result.Deindent();
result.AddShaderChunk("};");
}
public static void BuildPackedType(System.Type unpacked, HashSet<string> activeFields, ShaderGenerator result)
{
// for each interpolator, the number of components used (up to 4 for a float4 interpolator)
List<int> packedCounts = new List<int>();
ShaderGenerator packer = new ShaderGenerator();
ShaderGenerator unpacker = new ShaderGenerator();
ShaderGenerator structEnd = new ShaderGenerator();
string unpackedStruct = unpacked.Name.ToString();
string packedStruct = "Packed" + unpacked.Name;
string packerFunction = "Pack" + unpacked.Name;
string unpackerFunction = "Unpack" + unpacked.Name;
// declare struct header:
// struct packedStruct {
result.AddShaderChunk("struct " + packedStruct + " {");
result.Indent();
// declare function headers:
// packedStruct packerFunction(unpackedStruct input)
// {
// packedStruct output;
packer.AddShaderChunk(packedStruct + " " + packerFunction + "(" + unpackedStruct + " input)");
packer.AddShaderChunk("{");
packer.Indent();
packer.AddShaderChunk(packedStruct + " output;");
// unpackedStruct unpackerFunction(packedStruct input)
// {
// unpackedStruct output;
unpacker.AddShaderChunk(unpackedStruct + " " + unpackerFunction + "(" + packedStruct + " input)");
unpacker.AddShaderChunk("{");
unpacker.Indent();
unpacker.AddShaderChunk(unpackedStruct + " output;");
// TODO: this could do a better job packing
// especially if we allowed breaking up fields across multiple interpolators (to pack them into remaining space...)
// though we would want to only do this if it improves final interpolator count, and is worth it on the target machine
foreach (FieldInfo field in unpacked.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
{
if (field.MemberType == MemberTypes.Field)
{
bool isOptional;
if (ShouldSpliceField(unpacked, field, activeFields, out isOptional))
{
string semanticString = GetFieldSemantic(field);
int floatVectorCount;
string fieldType = GetFieldType(field, out floatVectorCount);
string conditional = GetFieldConditional(field);
if ((semanticString != null) || (conditional != null) || (floatVectorCount == 0))
{
// not a packed value
if (conditional != null)
{
structEnd.AddShaderChunk("#if " + conditional);
packer.AddShaderChunk("#if " + conditional);
unpacker.AddShaderChunk("#if " + conditional);
}
structEnd.AddShaderChunk(fieldType + " " + field.Name + semanticString + "; // unpacked");
packer.AddShaderChunk("output." + field.Name + " = input." + field.Name + ";");
unpacker.AddShaderChunk("output." + field.Name + " = input." + field.Name + ";");
if (conditional != null)
{
structEnd.AddShaderChunk("#endif // " + conditional);
packer.AddShaderChunk("#endif // " + conditional);
unpacker.AddShaderChunk("#endif // " + conditional);
}
}
else
{
// pack float field
// super simple packing: use the first interpolator that has room for the whole value
int interpIndex = packedCounts.FindIndex(x => (x + floatVectorCount <= 4));
int firstChannel;
if (interpIndex < 0)
{
// allocate a new interpolator
interpIndex = packedCounts.Count;
firstChannel = 0;
packedCounts.Add(floatVectorCount);
}
else
{
// pack into existing interpolator
firstChannel = packedCounts[interpIndex];
packedCounts[interpIndex] += floatVectorCount;
}
// add code to packer and unpacker -- packed data declaration is handled later
string packedChannels = GetChannelSwizzle(firstChannel, floatVectorCount);
packer.AddShaderChunk(string.Format("output.interp{0:00}.{1} = input.{2};", interpIndex, packedChannels, field.Name));
unpacker.AddShaderChunk(string.Format("output.{0} = input.interp{1:00}.{2};", field.Name, interpIndex, packedChannels));
}
}
}
}
// add packed data declarations to struct, using the packedCounts
for (int index = 0; index < packedCounts.Count; index++)
{
int count = packedCounts[index];
result.AddShaderChunk(string.Format("{0} interp{1:00} : TEXCOORD{1}; // auto-packed", vectorTypeNames[count], index));
}
// add unpacked data declarations to struct (must be at end)
result.AddGenerator(structEnd);
// close declarations
result.Deindent();
result.AddShaderChunk("};");
packer.AddShaderChunk("return output;");
packer.Deindent();
packer.AddShaderChunk("}");
unpacker.AddShaderChunk("return output;");
unpacker.Deindent();
unpacker.AddShaderChunk("}");
// combine all of the code into the result
result.AddGenerator(packer);
result.AddGenerator(unpacker);
}
// an easier to use version of substring Append() -- explicit inclusion on each end, and checks for positive length
private static void AppendSubstring(System.Text.StringBuilder target, string str, int start, bool includeStart, int end, bool includeEnd)
{
if (!includeStart)
{
start++;
}
if (!includeEnd)
{
end--;
}
int count = end - start + 1;
if (count > 0)
{
target.Append(str, start, count);
}
}
public static System.Text.StringBuilder PreprocessShaderCode(string code, HashSet<string> activeFields, Dictionary<string, string> namedFragments = null, System.Text.StringBuilder result = null)
{
if (result == null)
{
result = new System.Text.StringBuilder();
}
int cur = 0;
int end = code.Length;
while (cur < end)
{
int dollar = code.IndexOf('$', cur);
if (dollar < 0)
{
// no escape sequence found -- just append the remaining part of the code verbatim
AppendSubstring(result, code, cur, true, end, false);
cur = end;
}
else
{
// found $ escape sequence
// first append everything before the beginning of the escape sequence
AppendSubstring(result, code, cur, true, dollar, false);
// next find the end of the line (or if none found, the end of the code)
int endln = code.IndexOf('\n', dollar + 1);
if (endln < 0)
{
endln = end;
}
// see if the character after '$' is '{', which would indicate a named fragment splice
if ((dollar + 1 < endln) && (code[dollar + 1] == '{'))
{
// named fragment splice
// search for the '}' within the current line
int curlystart = dollar + 1;
int curlyend = -1;
if (endln > curlystart + 1)
{
curlyend = code.IndexOf('}', curlystart + 1, endln - curlystart - 1);
}
int nameLength = curlyend - dollar + 1;
if ((curlyend < 0) || (nameLength <= 0))
{
// no } found, or zero length name
if (curlyend < 0)
{
result.Append("// ERROR: unterminated escape sequence ('${' and '}' must be matched)\n");
}
else
{
result.Append("// ERROR: name '${}' is empty\n");
}
// append the line (commented out) for context
result.Append("// ");
AppendSubstring(result, code, dollar, true, endln, false);
result.Append("\n");
}
else
{
// } found!
// ugh, this probably allocates memory -- wish we could do the name lookup direct from a substring
string name = code.Substring(dollar, nameLength);
string fragment;
if ((namedFragments != null) && namedFragments.TryGetValue(name, out fragment))
{
// splice the fragment
result.Append(fragment);
// advance to just after the '}'
cur = curlyend + 1;
}
else
{
// no named fragment found
result.AppendFormat("/* Could not find named fragment '{0}' */", name);
cur = curlyend + 1;
}
}
}
else
{
// it's a predicate
// search for the colon within the current line
int colon = -1;
if (endln > dollar + 1)
{
colon = code.IndexOf(':', dollar + 1, endln - dollar - 1);
}
int predicateLength = colon - dollar - 1;
if ((colon < 0) || (predicateLength <= 0))
{
// no colon found... error! Spit out error and context
if (colon < 0)
{
result.Append("// ERROR: unterminated escape sequence ('$' and ':' must be matched)\n");
}
else
{
result.Append("// ERROR: predicate is zero length\n");
}
// append the line (commented out) for context
result.Append("// ");
AppendSubstring(result, code, dollar, true, endln, false);
}
else
{
// colon found!
// ugh, this probably allocates memory -- wish we could do the field lookup direct from a substring
string predicate = code.Substring(dollar + 1, predicateLength);
if (activeFields.Contains(predicate))
{
// predicate is active, append the line
result.Append(' ', predicateLength + 2);
AppendSubstring(result, code, colon, false, endln, false);
}
else
{
// predicate is not active -- comment out line
result.Append("//");
result.Append(' ', predicateLength);
AppendSubstring(result, code, colon, false, endln, false);
}
}
cur = endln + 1;
}
}
}
return result;
}
public static void ApplyDependencies(HashSet<string> activeFields, List<Dependency[]> dependsList)
{
// add active fields to queue
Queue<string> fieldsToPropagate = new Queue<string>();
foreach (string f in activeFields)
{
fieldsToPropagate.Enqueue(f);
}
// foreach field in queue:
while (fieldsToPropagate.Count > 0)
{
string field = fieldsToPropagate.Dequeue();
if (activeFields.Contains(field)) // this should always be true
{
// find all dependencies of field that are not already active
foreach (Dependency[] dependArray in dependsList)
{
foreach (Dependency d in dependArray.Where(d => (d.name == field) && !activeFields.Contains(d.dependsOn)))
{
// activate them and add them to the queue
activeFields.Add(d.dependsOn);
fieldsToPropagate.Enqueue(d.dependsOn);
}
}
}
}
}
};
public static class GraphUtil
{
internal static string ConvertCamelCase(string text, bool preserveAcronyms)

string functionName = "PopulateSurfaceData",
string surfaceDescriptionName = "SurfaceDescription",
Vector1ShaderProperty outputIdProperty = null,
IEnumerable<MaterialSlot> slots = null)
IEnumerable<MaterialSlot> slots = null,
string graphInputStructName = "SurfaceDescriptionInputs")
GraphContext graphContext = new GraphContext(graphInputStructName);
graph.CollectShaderProperties(shaderProperties, mode);

if (activeNode is IGeneratesFunction)
{
functionRegistry.builder.currentNode = activeNode;
(activeNode as IGeneratesFunction).GenerateNodeFunction(functionRegistry, mode);
(activeNode as IGeneratesFunction).GenerateNodeFunction(functionRegistry, graphContext, mode);
}
if (activeNode is IGeneratesBodyCode)
(activeNode as IGeneratesBodyCode).GenerateNodeCode(sg, mode);

PropertyCollector shaderProperties,
GenerationMode mode,
List<AbstractMaterialNode> nodes,
List<MaterialSlot> slots)
List<MaterialSlot> slots,
string graphInputStructName = "VertexDescriptionInputs")
GraphContext graphContext = new GraphContext(graphInputStructName);
graph.CollectShaderProperties(shaderProperties, mode);

if (generatesFunction != null)
{
functionRegistry.builder.currentNode = node;
generatesFunction.GenerateNodeFunction(functionRegistry, mode);
generatesFunction.GenerateNodeFunction(functionRegistry, graphContext, mode);
}
var generatesBodyCode = node as IGeneratesBodyCode;
if (generatesBodyCode != null)

20
ShaderGraph/com.unity.shadergraph/Editor/Data/Util/ShaderGenerator.cs


return m_Pragma;
}
public void AddShaderChunk(string s, bool unique)
public void AddShaderChunk(string s, bool unique = false)
{
if (string.IsNullOrEmpty(s))
return;

m_ShaderChunks.Add(new ShaderChunk(m_IndentLevel, s));
}
public void AddGenerator(ShaderGenerator generator)
{
foreach (ShaderChunk chunk in generator.m_ShaderChunks)
{
m_ShaderChunks.Add(new ShaderChunk(m_IndentLevel + chunk.chunkIndentLevel, chunk.chunkString));
}
}
public void Indent()

m_IndentLevel = Math.Max(0, m_IndentLevel - 1);
}
public string GetShaderString(int baseIndentLevel)
public string GetShaderString(int baseIndentLevel, bool finalNewline = true)
bool appendedNewline = false;
// TODO: regex split is a slow way to handle this .. should build an iterator instead
var lines = Regex.Split(shaderChunk.chunkString, Environment.NewLine);
for (int index = 0; index < lines.Length; index++)
{

sb.AppendLine(line);
appendedNewline = true;
}
if (appendedNewline && !finalNewline)
{
// remove last newline if we didn't want it
sb.Length -= Environment.NewLine.Length;
}
return sb.ToString();
}

275
ShaderGraph/com.unity.shadergraph/Editor/Templates/HDUnlitPassForward.template


// Unlit shader always render in forward
Name "ForwardUnlit"
// based on HDUnlitPassForward.template
Name "${PassName}"
// Material options generated by graph
${Tags}
//-------------------------------------------------------------------------------------
// Render Modes (Blend, Cull, ZTest, Stencil, etc)
//-------------------------------------------------------------------------------------
HLSLPROGRAM
${Stencil}
${ColorMask}
//-------------------------------------------------------------------------------------
// End Render Modes
//-------------------------------------------------------------------------------------
HLSLPROGRAM
#pragma only_renderers d3d11 ps4 vulkan metal // TEMP: until we go further in dev
#pragma only_renderers d3d11 ps4 xboxone vulkan metal switch
//-------------------------------------------------------------------------------------
// Variant Definitions
//-------------------------------------------------------------------------------------
${VariantDefines}
//-------------------------------------------------------------------------------------
// End Variant
//-------------------------------------------------------------------------------------
// -------------------------------------
// HD Pipeline keywords
// Defines generated by graph
${Defines}
#include "CoreRP/ShaderLibrary/common.hlsl"
#include "HDRP/ShaderVariables.hlsl"
#include "CoreRP/ShaderLibrary/Common.hlsl"
// #include "CoreRP/ShaderLibrary/Wind.hlsl"
#include "ShaderGraphLibrary/Functions.hlsl"
// define FragInputs structure
#include "ShaderGraphLibrary/Functions.hlsl"
#include "HDRP/Material/Material.hlsl"
// This include will define the various Attributes/Varyings structure
#include "HDRP/ShaderPass/VaryingMesh.hlsl"
//-------------------------------------------------------------------------------------
// Defines
//-------------------------------------------------------------------------------------
${Defines}
// this translates the new dependency tracker into the old preprocessor definitions for the existing HDRP shader code
$AttributesMesh.normalOS: #define ATTRIBUTES_NEED_NORMAL
$AttributesMesh.tangentOS: #define ATTRIBUTES_NEED_TANGENT
$AttributesMesh.uv0: #define ATTRIBUTES_NEED_TEXCOORD0
$AttributesMesh.uv1: #define ATTRIBUTES_NEED_TEXCOORD1
$AttributesMesh.uv2: #define ATTRIBUTES_NEED_TEXCOORD2
$AttributesMesh.uv3: #define ATTRIBUTES_NEED_TEXCOORD3
$AttributesMesh.color: #define ATTRIBUTES_NEED_COLOR
$VaryingsMeshToPS.positionWS: #define VARYINGS_NEED_POSITION_WS
$VaryingsMeshToPS.normalWS: #define VARYINGS_NEED_TANGENT_TO_WORLD
$VaryingsMeshToPS.texCoord0: #define VARYINGS_NEED_TEXCOORD0
$VaryingsMeshToPS.texCoord1: #define VARYINGS_NEED_TEXCOORD1
$VaryingsMeshToPS.texCoord2: #define VARYINGS_NEED_TEXCOORD2
$VaryingsMeshToPS.texCoord3: #define VARYINGS_NEED_TEXCOORD3
$VaryingsMeshToPS.color: #define VARYINGS_NEED_COLOR
$VaryingsMeshToPS.cullFace: #define VARYINGS_NEED_CULLFACE
//-------------------------------------------------------------------------------------
// End Defines
//-------------------------------------------------------------------------------------
#include "HDRP/ShaderVariables.hlsl"
#ifdef DEBUG_DISPLAY
#include "HDRP/Debug/DebugDisplay.hlsl"
#endif
#if (SHADERPASS == SHADERPASS_FORWARD)
// used for shaders that want to do lighting (and materials)
#include "HDRP/Lighting/Lighting.hlsl"
#else
// used for shaders that don't need lighting
#include "HDRP/Material/Material.hlsl"
#endif
#include "HDRP/Material/MaterialUtilities.hlsl"
// this function assumes the bitangent flip is encoded in tangentWS.w
// TODO: move this function to HDRP shared file, once we merge with HDRP repo
float3x3 BuildWorldToTangent(float4 tangentWS, float3 normalWS)
{
// tangentWS must not be normalized (mikkts requirement)
// Normalize normalWS vector but keep the renormFactor to apply it to bitangent and tangent
float3 unnormalizedNormalWS = normalWS;
float renormFactor = 1.0 / length(unnormalizedNormalWS);
// bitangent on the fly option in xnormal to reduce vertex shader outputs.
// this is the mikktspace transformation (must use unnormalized attributes)
float3x3 worldToTangent = CreateWorldToTangent(unnormalizedNormalWS, tangentWS.xyz, tangentWS.w > 0.0 ? 1.0 : -1.0);
// surface gradient based formulation requires a unit length initial normal. We can maintain compliance with mikkts
// by uniformly scaling all 3 vectors since normalization of the perturbed normal will cancel it.
worldToTangent[0] = worldToTangent[0] * renormFactor;
worldToTangent[1] = worldToTangent[1] * renormFactor;
worldToTangent[2] = worldToTangent[2] * renormFactor; // normalizes the interpolated vertex normal
return worldToTangent;
}
//-------------------------------------------------------------------------------------
// Interpolator Packing And Struct Declarations
//-------------------------------------------------------------------------------------
${InterpolatorPacking}
//-------------------------------------------------------------------------------------
// End Interpolator Packing And Struct Declarations
//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
// Graph generated code
//-------------------------------------------------------------------------------------
void GetSurfaceAndBuiltinData(FragInputs input, float3 V, inout PositionInputs posInput, out SurfaceData surfaceData, out BuiltinData builtinData)
//-------------------------------------------------------------------------------------
// End graph generated code
//-------------------------------------------------------------------------------------
// TODO: Do we want to build include functionality for sharing these preprocessed functions across templates?
FragInputs BuildFragInputs(VaryingsMeshToPS input)
{
FragInputs output;
ZERO_INITIALIZE(FragInputs, output);
// Init to some default value to make the computer quiet (else it output 'divide by zero' warning even if value is not used).
// TODO: this is a really poor workaround, but the variable is used in a bunch of places
// to compute normals which are then passed on elsewhere to compute other values...
output.worldToTangent = k_identity3x3;
output.positionSS = input.positionCS; // input.positionCS is SV_Position
$FragInputs.positionWS: output.positionWS = input.positionWS;
$FragInputs.worldToTangent: output.worldToTangent = BuildWorldToTangent(input.tangentWS, input.normalWS);
$FragInputs.texCoord0: output.texCoord0 = input.texCoord0;
$FragInputs.texCoord1: output.texCoord1 = input.texCoord1;
$FragInputs.texCoord2: output.texCoord2 = input.texCoord2;
$FragInputs.texCoord3: output.texCoord3 = input.texCoord3;
$FragInputs.color: output.color = input.color;
#if SHADER_STAGE_FRAGMENT
$FragInputs.isFrontFace: output.isFrontFace = IS_FRONT_VFACE(input.cullFace, true, false); // TODO: SHADER_STAGE_FRAGMENT only
$FragInputs.isFrontFace: // Handle handness of the view matrix (In Unity view matrix default to a determinant of -1)
$FragInputs.isFrontFace: // when we render a cubemap the view matrix handness is flipped (due to convention used for cubemap) we have a determinant of +1
$FragInputs.isFrontFace: output.isFrontFace = _DetViewMatrix < 0.0 ? output.isFrontFace : !output.isFrontFace;
#endif // SHADER_STAGE_FRAGMENT
return output;
}
SurfaceDescriptionInputs FragInputsToSurfaceDescriptionInputs(FragInputs input, float3 viewWS)
{
SurfaceDescriptionInputs output;
ZERO_INITIALIZE(SurfaceDescriptionInputs, output);
$SurfaceDescriptionInputs.WorldSpaceNormal: output.WorldSpaceNormal = normalize(input.worldToTangent[2].xyz);
$SurfaceDescriptionInputs.ObjectSpaceNormal: output.ObjectSpaceNormal = mul(output.WorldSpaceNormal, (float3x3) unity_ObjectToWorld); // transposed multiplication by inverse matrix to handle normal scale
$SurfaceDescriptionInputs.ViewSpaceNormal: output.ViewSpaceNormal = mul(output.WorldSpaceNormal, (float3x3) UNITY_MATRIX_I_V); // transposed multiplication by inverse matrix to handle normal scale
$SurfaceDescriptionInputs.TangentSpaceNormal: output.TangentSpaceNormal = float3(0.0f, 0.0f, 1.0f);
$SurfaceDescriptionInputs.WorldSpaceTangent: output.WorldSpaceTangent = input.worldToTangent[0].xyz;
$SurfaceDescriptionInputs.ObjectSpaceTangent: output.ObjectSpaceTangent = mul((float3x3) unity_WorldToObject, output.WorldSpaceTangent);
$SurfaceDescriptionInputs.ViewSpaceTangent: output.ViewSpaceTangent = mul((float3x3) UNITY_MATRIX_V, output.WorldSpaceTangent);
$SurfaceDescriptionInputs.TangentSpaceTangent: output.TangentSpaceTangent = float3(1.0f, 0.0f, 0.0f);
$SurfaceDescriptionInputs.WorldSpaceBiTangent: output.WorldSpaceBiTangent = input.worldToTangent[1].xyz;
$SurfaceDescriptionInputs.ObjectSpaceBiTangent: output.ObjectSpaceBiTangent = mul((float3x3) unity_WorldToObject, output.WorldSpaceBiTangent);
$SurfaceDescriptionInputs.ViewSpaceBiTangent: output.ViewSpaceBiTangent = mul((float3x3) UNITY_MATRIX_V, output.WorldSpaceBiTangent);
$SurfaceDescriptionInputs.TangentSpaceBiTangent: output.TangentSpaceBiTangent = float3(0.0f, 1.0f, 0.0f);
$SurfaceDescriptionInputs.WorldSpaceViewDirection: output.WorldSpaceViewDirection = normalize(viewWS);
$SurfaceDescriptionInputs.ObjectSpaceViewDirection: output.ObjectSpaceViewDirection = mul((float3x3) unity_WorldToObject, output.WorldSpaceViewDirection);
$SurfaceDescriptionInputs.ViewSpaceViewDirection: output.ViewSpaceViewDirection = mul((float3x3) UNITY_MATRIX_V, output.WorldSpaceViewDirection);
$SurfaceDescriptionInputs.TangentSpaceViewDirection: float3x3 tangentSpaceTransform = float3x3(output.WorldSpaceTangent,output.WorldSpaceBiTangent,output.WorldSpaceNormal);
$SurfaceDescriptionInputs.TangentSpaceViewDirection: output.TangentSpaceViewDirection = mul(tangentSpaceTransform, output.WorldSpaceViewDirection);
// TODO: FragInputs.positionWS is badly named -- it's camera relative, not in world space
$SurfaceDescriptionInputs.WorldSpacePosition: output.WorldSpacePosition = input.positionWS + _WorldSpaceCameraPos;
$SurfaceDescriptionInputs.ObjectSpacePosition: output.ObjectSpacePosition = mul(unity_WorldToObject, float4(input.positionWS + _WorldSpaceCameraPos, 1.0f)).xyz;
$SurfaceDescriptionInputs.ViewSpacePosition: float4 posViewSpace = mul(UNITY_MATRIX_V, float4(input.positionWS, 1.0f));
$SurfaceDescriptionInputs.ViewSpacePosition: output.ViewSpacePosition = posViewSpace.xyz / posViewSpace.w;
$SurfaceDescriptionInputs.TangentSpacePosition: output.TangentSpacePosition = float3(0.0f, 0.0f, 0.0f);
$SurfaceDescriptionInputs.screenPosition: output.screenPosition = input.positionSS;
$SurfaceDescriptionInputs.uv0: output.uv0 = float4(input.texCoord0, 0.0f, 0.0f);
$SurfaceDescriptionInputs.uv1: output.uv1 = float4(input.texCoord1, 0.0f, 0.0f);
$SurfaceDescriptionInputs.uv2: output.uv2 = float4(input.texCoord2, 0.0f, 0.0f);
$SurfaceDescriptionInputs.uv3: output.uv3 = float4(input.texCoord3, 0.0f, 0.0f);
$SurfaceDescriptionInputs.vertexColor: output.vertexColor = input.color;
return output;
}
// existing HDRP code uses the combined function to go directly from packed to frag inputs
FragInputs UnpackVaryingsMeshToFragInputs(PackedVaryingsMeshToPS input)
{
VaryingsMeshToPS unpacked= UnpackVaryingsMeshToPS(input);
return BuildFragInputs(unpacked);
}
void BuildSurfaceData(FragInputs fragInputs, SurfaceDescription surfaceDescription, float3 V, out SurfaceData surfaceData)
{
// setup defaults -- these are used if the graph doesn't output a value
ZERO_INITIALIZE(SurfaceData, surfaceData);
// copy across graph values, if defined
$SurfaceDescription.Color: surfaceData.color = surfaceDescription.Color;
}
void GetSurfaceAndBuiltinData(FragInputs fragInputs, float3 V, inout PositionInputs posInput, out SurfaceData surfaceData, out BuiltinData builtinData)
// Pixel transformations performed by graph
${PixelShader}
SurfaceDescriptionInputs surfaceInput;
// Surface description inputs defined by graph
${PixelShaderSurfaceInputs}
SurfaceDescription surf = PopulateSurfaceData(surfaceInput);
// this applies the double sided tangent space correction -- see 'ApplyDoubleSidedFlipOrMirror()'
$DoubleSided: if (!fragInputs.isFrontFace) {
$DoubleSided.Flip: fragInputs.worldToTangent[1] = -fragInputs.worldToTangent[1]; // bitangent
$DoubleSided.Flip: fragInputs.worldToTangent[2] = -fragInputs.worldToTangent[2]; // normal
$DoubleSided.Mirror: fragInputs.worldToTangent[2] = -fragInputs.worldToTangent[2]; // normal
$DoubleSided: }
float3 Color = float3(0.5, 0.5, 0.5);
float Alpha = 1;
float AlphaClipThreshold = 0;
// Surface description remap performed by graph
${PixelShaderSurfaceRemap}
surfaceData.color = Color;
SurfaceDescriptionInputs surfaceDescriptionInputs = FragInputsToSurfaceDescriptionInputs(fragInputs, V);
SurfaceDescription surfaceDescription = SurfaceDescriptionFunction(surfaceDescriptionInputs);
#ifdef _ALPHATEST_ON
DoAlphaTest(Alpha, AlphaClipThreshold);
#endif
// Perform alpha test very early to save performance (a killed pixel will not sample textures)
// TODO: split graph evaluation to grab just alpha dependencies first? tricky..
#ifdef _ALPHATEST_ON
DoAlphaTest(surfaceDescription.Alpha, surfaceDescription.AlphaClipThreshold);
#endif
// Builtin Data
builtinData.opacity = Alpha;
builtinData.bakeDiffuseLighting = float3(0.0, 0.0, 0.0);
builtinData.emissiveIntensity = 0;
builtinData.emissiveColor = 0;
builtinData.velocity = float2(0.0, 0.0);
BuildSurfaceData(fragInputs, surfaceDescription, V, surfaceData);
// Builtin Data -- we don't call GetBuiltinData(fragInputs, surfaceData, ...)
// that function assumes there are specific global properties defined
// for shadergraph shaders, we fill it out here instead
ZERO_INITIALIZE(BuiltinData, builtinData);
builtinData.opacity = surfaceDescription.Alpha;
builtinData.bakeDiffuseLighting = float3(0.0, 0.0, 0.0);
builtinData.emissiveIntensity = 1.0f;
$SurfaceDescription.Emission: builtinData.emissiveColor = surfaceDescription.Emission;
builtinData.velocity = float2(0.0, 0.0);
builtinData.distortion = float2(0.0, 0.0);
builtinData.distortionBlur = 0.0;
builtinData.depthOffset = 0.0;
builtinData.distortion = float2(0.0, 0.0); // surfaceDescription.Distortion -- if distortion pass
builtinData.distortionBlur = 0.0; // surfaceDescription.DistortionBlur -- if distortion pass
builtinData.depthOffset = 0.0; // ApplyPerPixelDisplacement(input, V, layerTexCoord, blendMasks); #ifdef _DEPTHOFFSET_ON : ApplyDepthOffsetPositionInput(V, depthOffset, GetWorldToHClipMatrix(), posInput);
#include "HDRP/ShaderPass/${ShaderPassInclude}.hlsl"
//-------------------------------------------------------------------------------------
// Pass Includes
//-------------------------------------------------------------------------------------
${Includes}
//-------------------------------------------------------------------------------------
// End Pass Includes
//-------------------------------------------------------------------------------------
ENDHLSL
}

790
ShaderGraph/HDPipeline/HDPBRSubShader.cs


using System.Collections.Generic;
using System.Collections;
using System.IO;
using System.Linq;
using UnityEditor.Graphing;
namespace UnityEditor.ShaderGraph
{
public class HDPBRSubShader : IPBRSubShader
{
Pass m_PassGBuffer = new Pass()
{
Name = "GBuffer",
LightMode = "GBuffer",
TemplateName = "HDPBRPass.template",
ShaderPassName = "SHADERPASS_GBUFFER",
StencilOverride = new List<string>()
{
"// Stencil setup for gbuffer",
"Stencil",
"{",
" WriteMask 7", // [_StencilWriteMask] // default: StencilMask.Lighting (fixed at compile time)
" Ref 2", // [_StencilRef] // default: StencilLightingUsage.RegularLighting (fixed at compile time)
" Comp Always",
" Pass Replace",
"}"
},
ExtraDefines = new List<string>()
{
"#pragma multi_compile _ DEBUG_DISPLAY",
"#pragma multi_compile _ LIGHTMAP_ON",
"#pragma multi_compile _ DIRLIGHTMAP_COMBINED",
"#pragma multi_compile _ DYNAMICLIGHTMAP_ON",
"#pragma multi_compile _ SHADOWS_SHADOWMASK",
},
Includes = new List<string>()
{
"#include \"HDRP/ShaderPass/ShaderPassGBuffer.hlsl\"",
},
RequiredFields = new List<string>()
{
"FragInputs.worldToTangent",
"FragInputs.positionWS",
},
PixelShaderSlots = new List<int>()
{
PBRMasterNode.AlbedoSlotId,
PBRMasterNode.NormalSlotId,
PBRMasterNode.MetallicSlotId,
PBRMasterNode.SpecularSlotId,
PBRMasterNode.EmissionSlotId,
PBRMasterNode.SmoothnessSlotId,
PBRMasterNode.OcclusionSlotId,
PBRMasterNode.AlphaSlotId,
PBRMasterNode.AlphaThresholdSlotId
},
};
Pass m_PassGBufferWithPrepass = new Pass()
{
Name = "GBufferWithPrepass",
LightMode = "GBufferWithPrepass",
TemplateName = "HDPBRPass.template",
ShaderPassName = "SHADERPASS_GBUFFER",
StencilOverride = new List<string>()
{
"// Stencil setup for GBufferWithPrepass",
"Stencil",
"{",
" WriteMask 7", // _StencilWriteMask // StencilMask.Lighting (fixed at compile time)
" Ref 2", // _StencilRef // StencilLightingUsage.RegularLighting (fixed at compile time)
" Comp Always",
" Pass Replace",
"}"
},
ExtraDefines = new List<string>()
{
"#pragma multi_compile _ DEBUG_DISPLAY",
"#pragma multi_compile _ LIGHTMAP_ON",
"#pragma multi_compile _ DIRLIGHTMAP_COMBINED",
"#pragma multi_compile _ DYNAMICLIGHTMAP_ON",
"#pragma multi_compile _ SHADOWS_SHADOWMASK",
"#define SHADERPASS_GBUFFER_BYPASS_ALPHA_TEST",
},
Includes = new List<string>()
{
"#include \"HDRP/ShaderPass/ShaderPassGBuffer.hlsl\"",
},
RequiredFields = new List<string>()
{
// "FragInputs.worldToTangent",
// "FragInputs.positionWS",
},
PixelShaderSlots = new List<int>()
{
PBRMasterNode.AlbedoSlotId,
PBRMasterNode.NormalSlotId,
PBRMasterNode.MetallicSlotId,
PBRMasterNode.SpecularSlotId,
PBRMasterNode.EmissionSlotId,
PBRMasterNode.SmoothnessSlotId,
PBRMasterNode.OcclusionSlotId,
PBRMasterNode.AlphaSlotId,
PBRMasterNode.AlphaThresholdSlotId
},
};
Pass m_PassMETA = new Pass()
{
Name = "META",
LightMode = "Meta",
TemplateName = "HDPBRPass.template",
ShaderPassName = "SHADERPASS_LIGHT_TRANSPORT",
CullOverride = "Cull Off",
Includes = new List<string>()
{
"#include \"HDRP/ShaderPass/ShaderPassLightTransport.hlsl\"",
},
RequiredFields = new List<string>()
{
"AttributesMesh.normalOS",
"AttributesMesh.tangentOS", // Always present as we require it also in case of anisotropic lighting
"AttributesMesh.uv0",
"AttributesMesh.uv1",
"AttributesMesh.color",
"AttributesMesh.uv2", // SHADERPASS_LIGHT_TRANSPORT always uses uv2
// "FragInputs.worldToTangent",
// "FragInputs.positionWS",
},
PixelShaderSlots = new List<int>()
{
PBRMasterNode.AlbedoSlotId,
PBRMasterNode.NormalSlotId,
PBRMasterNode.MetallicSlotId,
PBRMasterNode.SpecularSlotId,
PBRMasterNode.EmissionSlotId,
PBRMasterNode.SmoothnessSlotId,
PBRMasterNode.OcclusionSlotId,
PBRMasterNode.AlphaSlotId,
PBRMasterNode.AlphaThresholdSlotId
}
};
Pass m_PassShadowCaster = new Pass()
{
Name = "ShadowCaster",
LightMode = "ShadowCaster",
TemplateName = "HDPBRPass.template",
ShaderPassName = "SHADERPASS_SHADOWS",
ColorMaskOverride = "ColorMask 0",
ExtraDefines = new List<string>()
{
"#define USE_LEGACY_UNITY_MATRIX_VARIABLES",
},
Includes = new List<string>()
{
"#include \"HDRP/ShaderPass/ShaderPassDepthOnly.hlsl\"",
},
RequiredFields = new List<string>()
{
// "FragInputs.worldToTangent",
// "FragInputs.positionWS",
},
PixelShaderSlots = new List<int>()
{
PBRMasterNode.AlphaSlotId,
PBRMasterNode.AlphaThresholdSlotId
}
};
Pass m_PassDepthOnly = new Pass()
{
Name = "DepthOnly",
LightMode = "DepthOnly",
TemplateName = "HDPBRPass.template",
ShaderPassName = "SHADERPASS_DEPTH_ONLY",
ColorMaskOverride = "ColorMask 0",
Includes = new List<string>()
{
"#include \"HDRP/ShaderPass/ShaderPassDepthOnly.hlsl\"",
},
RequiredFields = new List<string>()
{
// "FragInputs.worldToTangent",
// "FragInputs.positionWS",
},
PixelShaderSlots = new List<int>()
{
PBRMasterNode.AlphaSlotId,
PBRMasterNode.AlphaThresholdSlotId
}
};
Pass m_PassMotionVectors = new Pass()
{
Name = "Motion Vectors",
LightMode = "MotionVectors",
TemplateName = "HDPBRPass.template",
ShaderPassName = "SHADERPASS_VELOCITY",
Includes = new List<string>()
{
"#include \"HDRP/ShaderPass/ShaderPassVelocity.hlsl\"",
},
RequiredFields = new List<string>()
{
"FragInputs.positionWS",
},
PixelShaderSlots = new List<int>()
{
PBRMasterNode.AlphaSlotId,
PBRMasterNode.AlphaThresholdSlotId
},
StencilOverride = new List<string>()
{
"// If velocity pass (motion vectors) is enabled we tag the stencil so it don't perform CameraMotionVelocity",
"Stencil",
"{",
" WriteMask 128", // [_StencilWriteMaskMV] (int) HDRenderPipeline.StencilBitMask.ObjectVelocity // this requires us to pull in the HD Pipeline assembly...
" Ref 128", // [_StencilRefMV]
" Comp Always",
" Pass Replace",
"}"
}
};
Pass m_PassDistortion = new Pass()
{
Name = "Distortion",
LightMode = "DistortionVectors",
TemplateName = "HDPBRPass.template",
ShaderPassName = "SHADERPASS_DISTORTION",
BlendOverride = "Blend One One, One One", // [_DistortionSrcBlend] [_DistortionDstBlend], [_DistortionBlurSrcBlend] [_DistortionBlurDstBlend]
BlendOpOverride = "BlendOp Add, Add", // Add, [_DistortionBlurBlendOp]
ZTestOverride = "ZTest LEqual", // [_ZTestModeDistortion]
ZWriteOverride = "ZWrite Off",
Includes = new List<string>()
{
"#include \"HDRP/ShaderPass/ShaderPassDistortion.hlsl\"",
},
RequiredFields = new List<string>()
{
// "FragInputs.worldToTangent",
// "FragInputs.positionWS",
},
PixelShaderSlots = new List<int>()
{
PBRMasterNode.AlphaSlotId,
PBRMasterNode.AlphaThresholdSlotId
}
};
Pass m_PassTransparentDepthPrepass = new Pass()
{
Name = "TransparentDepthPrepass",
LightMode = "TransparentDepthPrepass",
TemplateName = "HDPBRPass.template",
ShaderPassName = "SHADERPASS_DEPTH_ONLY",
ColorMaskOverride = "ColorMask 0",
ExtraDefines = new List<string>()
{
"#define CUTOFF_TRANSPARENT_DEPTH_PREPASS",
},
Includes = new List<string>()
{
"#include \"HDRP/ShaderPass/ShaderPassDepthOnly.hlsl\"",
},
RequiredFields = new List<string>()
{
// "FragInputs.worldToTangent",
// "FragInputs.positionWS",
},
PixelShaderSlots = new List<int>()
{
PBRMasterNode.AlphaSlotId,
PBRMasterNode.AlphaThresholdSlotId
}
};
Pass m_PassTransparentBackface = new Pass()
{
Name = "TransparentBackface",
LightMode = "TransparentBackface",
TemplateName = "HDPBRPass.template",
ShaderPassName = "SHADERPASS_FORWARD",
CullOverride = "Cull Front",
ExtraDefines = new List<string>()
{
"#pragma multi_compile _ DEBUG_DISPLAY",
"#pragma multi_compile _ LIGHTMAP_ON",
"#pragma multi_compile _ DIRLIGHTMAP_COMBINED",
"#pragma multi_compile _ DYNAMICLIGHTMAP_ON",
"#pragma multi_compile _ SHADOWS_SHADOWMASK",
"#pragma multi_compile LIGHTLOOP_SINGLE_PASS LIGHTLOOP_TILE_PASS",
"#pragma multi_compile USE_FPTL_LIGHTLIST USE_CLUSTERED_LIGHTLIST",
},
Includes = new List<string>()
{
"#include \"HDRP/ShaderPass/ShaderPassForward.hlsl\"",
},
RequiredFields = new List<string>()
{
// "FragInputs.worldToTangent",
// "FragInputs.positionWS",
},
PixelShaderSlots = new List<int>()
{
PBRMasterNode.AlbedoSlotId,
PBRMasterNode.NormalSlotId,
PBRMasterNode.MetallicSlotId,
PBRMasterNode.SpecularSlotId,
PBRMasterNode.EmissionSlotId,
PBRMasterNode.SmoothnessSlotId,
PBRMasterNode.OcclusionSlotId,
PBRMasterNode.AlphaSlotId,
PBRMasterNode.AlphaThresholdSlotId
}
};
Pass m_PassForward = new Pass()
{
Name = "Forward",
LightMode = "Forward",
TemplateName = "HDPBRPass.template",
ShaderPassName = "SHADERPASS_FORWARD",
ExtraDefines = new List<string>()
{
"#pragma multi_compile _ DEBUG_DISPLAY",
"#pragma multi_compile _ LIGHTMAP_ON",
"#pragma multi_compile _ DIRLIGHTMAP_COMBINED",
"#pragma multi_compile _ DYNAMICLIGHTMAP_ON",
"#pragma multi_compile _ SHADOWS_SHADOWMASK",
"#pragma multi_compile LIGHTLOOP_SINGLE_PASS LIGHTLOOP_TILE_PASS",
"#pragma multi_compile USE_FPTL_LIGHTLIST USE_CLUSTERED_LIGHTLIST"
},
StencilOverride = new List<string>()
{
"// Stencil setup for forward",
"Stencil",
"{",
" WriteMask 7", // [_StencilWriteMask] // default: StencilMask.Lighting (fixed at compile time)
" Ref 2", // [_StencilRef] // default: StencilLightingUsage.RegularLighting (fixed at compile time)
" Comp Always",
" Pass Replace",
"}"
},
Includes = new List<string>()
{
"#include \"HDRP/ShaderPass/ShaderPassForward.hlsl\"",
},
RequiredFields = new List<string>()
{
"FragInputs.worldToTangent",
// "FragInputs.positionWS",
},
PixelShaderSlots = new List<int>()
{
PBRMasterNode.AlbedoSlotId,
PBRMasterNode.NormalSlotId,
PBRMasterNode.MetallicSlotId,
PBRMasterNode.SpecularSlotId,
PBRMasterNode.EmissionSlotId,
PBRMasterNode.SmoothnessSlotId,
PBRMasterNode.OcclusionSlotId,
PBRMasterNode.AlphaSlotId,
PBRMasterNode.AlphaThresholdSlotId
}
};
Pass m_PassTransparentDepthPostpass = new Pass()
{
Name = "TransparentDepthPostpass",
LightMode = "TransparentDepthPostpass",
TemplateName = "HDPBRPass.template",
ShaderPassName = "SHADERPASS_DEPTH_ONLY",
ColorMaskOverride = "ColorMask 0",
ExtraDefines = new List<string>()
{
"#define CUTOFF_TRANSPARENT_DEPTH_POSTPASS",
},
Includes = new List<string>()
{
"#include \"HDRP/ShaderPass/ShaderPassDepthOnly.hlsl\"",
},
RequiredFields = new List<string>()
{
// "FragInputs.worldToTangent",
// "FragInputs.positionWS",
},
PixelShaderSlots = new List<int>()
{
PBRMasterNode.AlphaSlotId,
PBRMasterNode.AlphaThresholdSlotId
}
};
private static string GetVariantDefines(PBRMasterNode masterNode)
{
ShaderGenerator defines = new ShaderGenerator();
// TODO:
// _MATERIAL_FEATURE_SUBSURFACE_SCATTERING
// _MATERIAL_FEATURE_TRANSMISSION
// _MATERIAL_FEATURE_ANISOTROPY
// _MATERIAL_FEATURE_CLEAR_COAT
// _MATERIAL_FEATURE_IRIDESCENCE
switch (masterNode.model)
{
case PBRMasterNode.Model.Metallic:
break;
case PBRMasterNode.Model.Specular:
defines.AddShaderChunk("#define _MATERIAL_FEATURE_SPECULAR_COLOR 1", true);
break;
default:
// TODO: error!
break;
}
// #pragma shader_feature _ALPHATEST_ON
float constantAlpha = 0.0f;
if (masterNode.IsSlotConnected(PBRMasterNode.AlphaThresholdSlotId) ||
(float.TryParse(masterNode.GetSlotValue(PBRMasterNode.AlphaThresholdSlotId, GenerationMode.ForReals), out constantAlpha) && (constantAlpha > 0.0f)))
{
defines.AddShaderChunk("#define _ALPHATEST_ON 1", true);
}
// if (kTesselationMode != TessellationMode.None)
// {
// defines.AddShaderChunk("#define _TESSELLATION_PHONG 1", true);
// }
// #pragma shader_feature _ _VERTEX_DISPLACEMENT _PIXEL_DISPLACEMENT
// switch (kDisplacementMode)
// {
// case DisplacementMode.None:
// break;
// case DisplacementMode.Vertex:
// defines.AddShaderChunk("#define _VERTEX_DISPLACEMENT 1", true);
// break;
// case DisplacementMode.Pixel:
// defines.AddShaderChunk("#define _PIXEL_DISPLACEMENT 1", true);
// Depth offset is only enabled if per pixel displacement is
// if (kDepthOffsetEnable)
// {
// // #pragma shader_feature _DEPTHOFFSET_ON
// defines.AddShaderChunk("#define _DEPTHOFFSET_ON 1", true);
// }
// break;
// case DisplacementMode.Tessellation:
// if (kTessellationEnabled)
// {
// defines.AddShaderChunk("#define _TESSELLATION_DISPLACEMENT 1", true);
// }
// break;
// }
// #pragma shader_feature _VERTEX_DISPLACEMENT_LOCK_OBJECT_SCALE
// #pragma shader_feature _DISPLACEMENT_LOCK_TILING_SCALE
// #pragma shader_feature _PIXEL_DISPLACEMENT_LOCK_OBJECT_SCALE
// #pragma shader_feature _VERTEX_WIND
// #pragma shader_feature _ _REFRACTION_PLANE _REFRACTION_SPHERE
//
// #pragma shader_feature _ _MAPPING_PLANAR _MAPPING_TRIPLANAR // MOVE to a node
// #pragma shader_feature _NORMALMAP_TANGENT_SPACE
// #pragma shader_feature _ _REQUIRE_UV2 _REQUIRE_UV3
//
// #pragma shader_feature _NORMALMAP
if (masterNode.IsSlotConnected(PBRMasterNode.NormalSlotId))
{
defines.AddShaderChunk("#define _NORMALMAP 1", true);
}
// #pragma shader_feature _MASKMAP
// #pragma shader_feature _BENTNORMALMAP
// #pragma shader_feature _EMISSIVE_COLOR_MAP
// #pragma shader_feature _ENABLESPECULAROCCLUSION
// #pragma shader_feature _HEIGHTMAP
// #pragma shader_feature _TANGENTMAP
// #pragma shader_feature _ANISOTROPYMAP
// #pragma shader_feature _DETAIL_MAP // MOVE to a node
// #pragma shader_feature _SUBSURFACE_RADIUS_MAP
// #pragma shader_feature _THICKNESSMAP
// #pragma shader_feature _SPECULARCOLORMAP
// #pragma shader_feature _TRANSMITTANCECOLORMAP
// Keywords for transparent
// #pragma shader_feature _SURFACE_TYPE_TRANSPARENT
if (masterNode.surfaceType != SurfaceType.Opaque)
{
// transparent-only defines
defines.AddShaderChunk("#define _SURFACE_TYPE_TRANSPARENT 1", true);
// #pragma shader_feature _ _BLENDMODE_ALPHA _BLENDMODE_ADD _BLENDMODE_PRE_MULTIPLY
if (masterNode.alphaMode == AlphaMode.Alpha)
{
defines.AddShaderChunk("#define _BLENDMODE_ALPHA 1", true);
}
else if (masterNode.alphaMode == AlphaMode.Additive)
{
defines.AddShaderChunk("#define _BLENDMODE_ADD 1", true);
}
// else if (masterNode.alphaMode == PBRMasterNode.AlphaMode.PremultiplyAlpha) // TODO
// {
// defines.AddShaderChunk("#define _BLENDMODE_PRE_MULTIPLY 1", true);
// }
// #pragma shader_feature _BLENDMODE_PRESERVE_SPECULAR_LIGHTING
// if (kEnableBlendModePreserveSpecularLighting)
// {
// defines.AddShaderChunk("#define _BLENDMODE_PRESERVE_SPECULAR_LIGHTING 1", true);
// }
// #pragma shader_feature _ENABLE_FOG_ON_TRANSPARENT
// if (kEnableFogOnTransparent)
// {
// defines.AddShaderChunk("#define _ENABLE_FOG_ON_TRANSPARENT 1", true);
// }
}
else
{
// opaque-only defines
}
// MaterialId are used as shader feature to allow compiler to optimize properly
// Note _MATID_STANDARD is not define as there is always the default case "_". We assign default as _MATID_STANDARD, so we never test _MATID_STANDARD
// #pragma shader_feature _ _MATID_SSS _MATID_ANISO _MATID_SPECULAR _MATID_CLEARCOAT
// enable dithering LOD crossfade
// #pragma multi_compile _ LOD_FADE_CROSSFADE
// TODO: We should have this keyword only if VelocityInGBuffer is enable, how to do that ?
//#pragma multi_compile VELOCITYOUTPUT_OFF VELOCITYOUTPUT_ON
return defines.GetShaderString(2);
}
private static bool GenerateShaderPass(PBRMasterNode masterNode, Pass pass, GenerationMode mode, SurfaceMaterialOptions materialOptions, ShaderGenerator result)
{
var templateLocation = ShaderGenerator.GetTemplatePath(pass.TemplateName);
if (!File.Exists(templateLocation))
{
// TODO: produce error here
return false;
}
// grab all of the active nodes
var activeNodeList = ListPool<INode>.Get();
NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, masterNode, NodeUtils.IncludeSelf.Include, pass.PixelShaderSlots);
// graph requirements describe what the graph itself requires
var graphRequirements = ShaderGraphRequirements.FromNodes(activeNodeList, ShaderStageCapability.Fragment);
ShaderStringBuilder graphNodeFunctions = new ShaderStringBuilder();
graphNodeFunctions.IncreaseIndent();
var functionRegistry = new FunctionRegistry(graphNodeFunctions);
// Build the list of active slots based on what the pass requires
// TODO: this can be a shared function -- From here through GraphUtil.GenerateSurfaceDescription(..)
var activeSlots = new List<MaterialSlot>();
foreach (var id in pass.PixelShaderSlots)
{
MaterialSlot slot = masterNode.FindSlot<MaterialSlot>(id);
if (slot != null)
{
activeSlots.Add(slot);
}
}
// build the graph outputs structure to hold the results of each active slots (and fill out activeFields to indicate they are active)
string graphInputStructName = "SurfaceDescriptionInputs";
string graphOutputStructName = "SurfaceDescription";
string graphEvalFunctionName = "SurfaceDescriptionFunction";
ShaderStringBuilder graphEvalFunction = new ShaderStringBuilder();
ShaderStringBuilder graphOutputs = new ShaderStringBuilder();
PropertyCollector graphProperties = new PropertyCollector();
// build the graph outputs structure, and populate activeFields with the fields of that structure
HashSet<string> activeFields = new HashSet<string>();
GraphUtil.GenerateSurfaceDescriptionStruct(graphOutputs, activeSlots, true);
//GraphUtil.GenerateSurfaceDescriptionStruct(graphOutputs, activeSlots, true, graphOutputStructName, activeFields);
// Build the graph evaluation code, to evaluate the specified slots
GraphUtil.GenerateSurfaceDescriptionFunction(
activeNodeList,
masterNode,
masterNode.owner as AbstractMaterialGraph,
graphEvalFunction,
functionRegistry,
graphProperties,
graphRequirements, // TODO : REMOVE UNUSED
mode,
graphEvalFunctionName,
graphOutputStructName,
null,
activeSlots,
graphInputStructName);
var blendCode = new ShaderStringBuilder();
var cullCode = new ShaderStringBuilder();
var zTestCode = new ShaderStringBuilder();
var zWriteCode = new ShaderStringBuilder();
var stencilCode = new ShaderStringBuilder();
var colorMaskCode = new ShaderStringBuilder();
HDSubShaderUtilities.BuildRenderStatesFromPassAndMaterialOptions(pass, materialOptions, blendCode, cullCode, zTestCode, zWriteCode, stencilCode, colorMaskCode);
if (masterNode.twoSided.isOn)
{
activeFields.Add("DoubleSided");
if (pass.ShaderPassName != "SHADERPASS_VELOCITY") // 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...
activeFields.Add("DoubleSided.Mirror"); // TODO: change this depending on what kind of normal flip you want..
activeFields.Add("FragInputs.isFrontFace"); // will need this for determining normal flip mode
}
}
if (pass.PixelShaderSlots != null)
{
foreach (var slotId in pass.PixelShaderSlots)
{
var slot = masterNode.FindSlot<MaterialSlot>(slotId);
if(slot != null)
{
var rawSlotName = slot.RawDisplayName().ToString();
var descriptionVar = string.Format("{0}.{1}", graphOutputStructName, rawSlotName);
activeFields.Add(descriptionVar);
}
}
}
var packedInterpolatorCode = new ShaderGenerator();
var graphInputs = new ShaderGenerator();
HDRPShaderStructs.Generate(
packedInterpolatorCode,
graphInputs,
graphRequirements,
pass.RequiredFields,
CoordinateSpace.World,
activeFields);
// debug output all active fields
var interpolatorDefines = new ShaderGenerator();
{
interpolatorDefines.AddShaderChunk("// ACTIVE FIELDS:");
foreach (string f in activeFields)
{
interpolatorDefines.AddShaderChunk("// " + f);
}
}
ShaderGenerator defines = new ShaderGenerator();
{
defines.AddShaderChunk(string.Format("#define SHADERPASS {0}", pass.ShaderPassName), true);
if (pass.ExtraDefines != null)
{
foreach (var define in pass.ExtraDefines)
defines.AddShaderChunk(define);
}
defines.AddGenerator(interpolatorDefines);
}
var shaderPassIncludes = new ShaderGenerator();
if (pass.Includes != null)
{
foreach (var include in pass.Includes)
shaderPassIncludes.AddShaderChunk(include);
}
// build graph code
var graph = new ShaderGenerator();
graph.AddShaderChunk("// Graph Inputs");
graph.Indent();
graph.AddGenerator(graphInputs);
graph.Deindent();
graph.AddShaderChunk("// Graph Outputs");
graph.Indent();
graph.AddShaderChunk(graphOutputs.ToString());
//graph.AddGenerator(graphOutputs);
graph.Deindent();
graph.AddShaderChunk("// Graph Properties (uniform inputs)");
graph.AddShaderChunk(graphProperties.GetPropertiesDeclaration(1));
graph.AddShaderChunk("// Graph Node Functions");
graph.AddShaderChunk(graphNodeFunctions.ToString());
graph.AddShaderChunk("// Graph Evaluation");
graph.Indent();
graph.AddShaderChunk(graphEvalFunction.ToString());
//graph.AddGenerator(graphEvalFunction);
graph.Deindent();
// build the hash table of all named fragments TODO: could make this Dictionary<string, ShaderGenerator / string> ?
Dictionary<string, string> namedFragments = new Dictionary<string, string>();
namedFragments.Add("${Defines}", defines.GetShaderString(2, false));
namedFragments.Add("${Graph}", graph.GetShaderString(2, false));
namedFragments.Add("${LightMode}", pass.LightMode);
namedFragments.Add("${PassName}", pass.Name);
namedFragments.Add("${Includes}", shaderPassIncludes.GetShaderString(2, false));
namedFragments.Add("${InterpolatorPacking}", packedInterpolatorCode.GetShaderString(2, false));
namedFragments.Add("${Blending}", blendCode.ToString());
namedFragments.Add("${Culling}", cullCode.ToString());
namedFragments.Add("${ZTest}", zTestCode.ToString());
namedFragments.Add("${ZWrite}", zWriteCode.ToString());
namedFragments.Add("${Stencil}", stencilCode.ToString());
namedFragments.Add("${ColorMask}", colorMaskCode.ToString());
namedFragments.Add("${LOD}", materialOptions.lod.ToString());
namedFragments.Add("${VariantDefines}", GetVariantDefines(masterNode));
// process the template to generate the shader code for this pass TODO: could make this a shared function
string[] templateLines = File.ReadAllLines(templateLocation);
System.Text.StringBuilder builder = new System.Text.StringBuilder();
foreach (string line in templateLines)
{
ShaderSpliceUtil.PreprocessShaderCode(line, activeFields, namedFragments, builder);
builder.AppendLine();
}
result.AddShaderChunk(builder.ToString(), false);
return true;
}
public string GetSubshader(IMasterNode iMasterNode, GenerationMode mode)
{
var masterNode = iMasterNode as PBRMasterNode;
var subShader = new ShaderGenerator();
subShader.AddShaderChunk("SubShader", true);
subShader.AddShaderChunk("{", true);
subShader.Indent();
{
SurfaceMaterialOptions materialOptions = HDSubShaderUtilities.BuildMaterialOptions(masterNode.surfaceType, masterNode.alphaMode, masterNode.twoSided.isOn);
// Add tags at the SubShader level
{
var tagsVisitor = new ShaderStringBuilder();
materialOptions.GetTags(tagsVisitor);
subShader.AddShaderChunk(tagsVisitor.ToString(), false);
}
// generate the necessary shader passes
bool opaque = (masterNode.surfaceType == SurfaceType.Opaque);
bool transparent = (masterNode.surfaceType != SurfaceType.Opaque);
bool distortionActive = false;
bool transparentDepthPrepassActive = transparent && false;
bool transparentBackfaceActive = transparent && false;
bool transparentDepthPostpassActive = transparent && false;
if (opaque)
{
GenerateShaderPass(masterNode, m_PassGBuffer, mode, materialOptions, subShader);
GenerateShaderPass(masterNode, m_PassGBufferWithPrepass, mode, materialOptions, subShader);
}
GenerateShaderPass(masterNode, m_PassMETA, mode, materialOptions, subShader);
GenerateShaderPass(masterNode, m_PassShadowCaster, mode, materialOptions, subShader);
if (opaque)
{
GenerateShaderPass(masterNode, m_PassDepthOnly, mode, materialOptions, subShader);
GenerateShaderPass(masterNode, m_PassMotionVectors, mode, materialOptions, subShader);
}
if (distortionActive)
{
GenerateShaderPass(masterNode, m_PassDistortion, mode, materialOptions, subShader);
}
if (transparentDepthPrepassActive)
{
GenerateShaderPass(masterNode, m_PassTransparentDepthPrepass, mode, materialOptions, subShader);
}
if (transparentBackfaceActive)
{
GenerateShaderPass(masterNode, m_PassTransparentBackface, mode, materialOptions, subShader);
}
GenerateShaderPass(masterNode, m_PassForward, mode, materialOptions, subShader);
if (transparentDepthPostpassActive)
{
GenerateShaderPass(masterNode, m_PassTransparentDepthPostpass, mode, materialOptions, subShader);
}
}
subShader.Deindent();
subShader.AddShaderChunk("}", true);
return subShader.GetShaderString(0);
}
}
}

11
ShaderGraph/HDPipeline/HDPBRSubShader.cs.meta


fileFormatVersion: 2
guid: c4e8610eb7ce19747bb637c68acc55cd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

444
ShaderGraph/HDPipeline/HDSubShaderUtilities.cs


using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor.Graphing;
using UnityEngine; // Vector3,4
using UnityEditor.ShaderGraph;
namespace UnityEditor.ShaderGraph
{
public static class HDRPShaderStructs
{
struct AttributesMesh
{
[Semantic("POSITION")] Vector3 positionOS;
[Semantic("NORMAL")][Optional] Vector3 normalOS;
[Semantic("TANGENT")][Optional] Vector4 tangentOS; // Stores bi-tangent sign in w
[Semantic("TEXCOORD0")][Optional] Vector2 uv0;
[Semantic("TEXCOORD1")][Optional] Vector2 uv1;
[Semantic("TEXCOORD2")][Optional] Vector2 uv2;
[Semantic("TEXCOORD3")][Optional] Vector2 uv3;
[Semantic("COLOR")][Optional] Vector4 color;
};
struct VaryingsMeshToPS
{
[Semantic("SV_Position")] Vector4 positionCS;
[Optional] Vector3 positionWS;
[Optional] Vector3 normalWS;
[Optional] Vector4 tangentWS; // w contain mirror sign
[Optional] Vector2 texCoord0;
[Optional] Vector2 texCoord1;
[Optional] Vector2 texCoord2;
[Optional] Vector2 texCoord3;
[Optional] Vector4 color;
[Optional] [Semantic("FRONT_FACE_SEMANTIC")] [OverrideType("FRONT_FACE_TYPE")] [PreprocessorIf("SHADER_STAGE_FRAGMENT")]
bool cullFace;
public static Dependency[] tessellationDependencies = new Dependency[]
{
new Dependency("VaryingsMeshToPS.positionWS", "VaryingsMeshToDS.positionWS"),
new Dependency("VaryingsMeshToPS.normalWS", "VaryingsMeshToDS.normalWS"),
new Dependency("VaryingsMeshToPS.tangentWS", "VaryingsMeshToDS.tangentWS"),
new Dependency("VaryingsMeshToPS.texCoord0", "VaryingsMeshToDS.texCoord0"),
new Dependency("VaryingsMeshToPS.texCoord1", "VaryingsMeshToDS.texCoord1"),
new Dependency("VaryingsMeshToPS.texCoord2", "VaryingsMeshToDS.texCoord2"),
new Dependency("VaryingsMeshToPS.texCoord3", "VaryingsMeshToDS.texCoord3"),
new Dependency("VaryingsMeshToPS.color", "VaryingsMeshToDS.color"),
};
public static Dependency[] standardDependencies = new Dependency[]
{
new Dependency("VaryingsMeshToPS.positionWS", "AttributesMesh.positionOS"),
new Dependency("VaryingsMeshToPS.normalWS", "AttributesMesh.normalOS"),
new Dependency("VaryingsMeshToPS.tangentWS", "AttributesMesh.tangentOS"),
new Dependency("VaryingsMeshToPS.texCoord0", "AttributesMesh.uv0"),
new Dependency("VaryingsMeshToPS.texCoord1", "AttributesMesh.uv1"),
new Dependency("VaryingsMeshToPS.texCoord2", "AttributesMesh.uv2"),
new Dependency("VaryingsMeshToPS.texCoord3", "AttributesMesh.uv3"),
new Dependency("VaryingsMeshToPS.color", "AttributesMesh.color"),
};
};
struct VaryingsMeshToDS
{
Vector3 positionWS;
Vector3 normalWS;
[Optional] Vector4 tangentWS;
[Optional] Vector2 texCoord0;
[Optional] Vector2 texCoord1;
[Optional] Vector2 texCoord2;
[Optional] Vector2 texCoord3;
[Optional] Vector4 color;
public static Dependency[] tessellationDependencies = new Dependency[]
{
new Dependency("VaryingsMeshToDS.tangentWS", "VaryingsMeshToPS.tangentWS"),
new Dependency("VaryingsMeshToDS.texCoord0", "VaryingsMeshToPS.texCoord0"),
new Dependency("VaryingsMeshToDS.texCoord1", "VaryingsMeshToPS.texCoord1"),
new Dependency("VaryingsMeshToDS.texCoord2", "VaryingsMeshToPS.texCoord2"),
new Dependency("VaryingsMeshToDS.texCoord3", "VaryingsMeshToPS.texCoord3"),
new Dependency("VaryingsMeshToDS.color", "VaryingsMeshToPS.color"),
};
};
struct FragInputs
{
public static Dependency[] dependencies = new Dependency[]
{
new Dependency("FragInputs.positionWS", "VaryingsMeshToPS.positionWS"),
new Dependency("FragInputs.worldToTangent", "VaryingsMeshToPS.tangentWS"),
new Dependency("FragInputs.worldToTangent", "VaryingsMeshToPS.normalWS"),
new Dependency("FragInputs.texCoord0", "VaryingsMeshToPS.texCoord0"),
new Dependency("FragInputs.texCoord1", "VaryingsMeshToPS.texCoord1"),
new Dependency("FragInputs.texCoord2", "VaryingsMeshToPS.texCoord2"),
new Dependency("FragInputs.texCoord3", "VaryingsMeshToPS.texCoord3"),
new Dependency("FragInputs.color", "VaryingsMeshToPS.color"),
new Dependency("FragInputs.isFrontFace", "VaryingsMeshToPS.cullFace"),
};
};
struct SurfaceDescriptionInputs
{
[Optional] Vector3 ObjectSpaceNormal;
[Optional] Vector3 ViewSpaceNormal;
[Optional] Vector3 WorldSpaceNormal;
[Optional] Vector3 TangentSpaceNormal;
[Optional] Vector3 ObjectSpaceTangent;
[Optional] Vector3 ViewSpaceTangent;
[Optional] Vector3 WorldSpaceTangent;
[Optional] Vector3 TangentSpaceTangent;
[Optional] Vector3 ObjectSpaceBiTangent;
[Optional] Vector3 ViewSpaceBiTangent;
[Optional] Vector3 WorldSpaceBiTangent;
[Optional] Vector3 TangentSpaceBiTangent;
[Optional] Vector3 ObjectSpaceViewDirection;
[Optional] Vector3 ViewSpaceViewDirection;
[Optional] Vector3 WorldSpaceViewDirection;
[Optional] Vector3 TangentSpaceViewDirection;
[Optional] Vector3 ObjectSpacePosition;
[Optional] Vector3 ViewSpacePosition;
[Optional] Vector3 WorldSpacePosition;
[Optional] Vector3 TangentSpacePosition;
[Optional] Vector4 screenPosition;
[Optional] Vector4 uv0;
[Optional] Vector4 uv1;
[Optional] Vector4 uv2;
[Optional] Vector4 uv3;
[Optional] Vector4 vertexColor;
public static Dependency[] dependencies = new Dependency[]
{
new Dependency("SurfaceDescriptionInputs.WorldSpaceNormal", "FragInputs.worldToTangent"),
new Dependency("SurfaceDescriptionInputs.ObjectSpaceNormal", "SurfaceDescriptionInputs.WorldSpaceNormal"),
new Dependency("SurfaceDescriptionInputs.ViewSpaceNormal", "SurfaceDescriptionInputs.WorldSpaceNormal"),
new Dependency("SurfaceDescriptionInputs.WorldSpaceTangent", "FragInputs.worldToTangent"),
new Dependency("SurfaceDescriptionInputs.ObjectSpaceTangent", "SurfaceDescriptionInputs.WorldSpaceTangent"),
new Dependency("SurfaceDescriptionInputs.ViewSpaceTangent", "SurfaceDescriptionInputs.WorldSpaceTangent"),
new Dependency("SurfaceDescriptionInputs.WorldSpaceBiTangent", "FragInputs.worldToTangent"),
new Dependency("SurfaceDescriptionInputs.ObjectSpaceBiTangent", "SurfaceDescriptionInputs.WorldSpaceBiTangent"),
new Dependency("SurfaceDescriptionInputs.ViewSpaceBiTangent", "SurfaceDescriptionInputs.WorldSpaceBiTangent"),
new Dependency("SurfaceDescriptionInputs.WorldSpacePosition", "FragInputs.positionWS"),
new Dependency("SurfaceDescriptionInputs.ObjectSpacePosition", "FragInputs.positionWS"),
new Dependency("SurfaceDescriptionInputs.ViewSpacePosition", "FragInputs.positionWS"),
new Dependency("SurfaceDescriptionInputs.WorldSpaceViewDirection", "FragInputs.positionWS"), // we build WorldSpaceViewDirection using FragInputs.positionWS in GetWorldSpaceNormalizeViewDir()
new Dependency("SurfaceDescriptionInputs.ObjectSpaceViewDirection", "SurfaceDescriptionInputs.WorldSpaceViewDirection"),
new Dependency("SurfaceDescriptionInputs.ViewSpaceViewDirection", "SurfaceDescriptionInputs.WorldSpaceViewDirection"),
new Dependency("SurfaceDescriptionInputs.TangentSpaceViewDirection", "SurfaceDescriptionInputs.WorldSpaceViewDirection"),
new Dependency("SurfaceDescriptionInputs.TangentSpaceViewDirection", "SurfaceDescriptionInputs.WorldSpaceTangent"),
new Dependency("SurfaceDescriptionInputs.TangentSpaceViewDirection", "SurfaceDescriptionInputs.WorldSpaceBiTangent"),
new Dependency("SurfaceDescriptionInputs.TangentSpaceViewDirection", "SurfaceDescriptionInputs.WorldSpaceNormal"),
new Dependency("SurfaceDescriptionInputs.screenPosition", "FragInputs.positionSS"),
new Dependency("SurfaceDescriptionInputs.uv0", "FragInputs.texCoord0"),
new Dependency("SurfaceDescriptionInputs.uv1", "FragInputs.texCoord1"),
new Dependency("SurfaceDescriptionInputs.uv2", "FragInputs.texCoord2"),
new Dependency("SurfaceDescriptionInputs.uv3", "FragInputs.texCoord3"),
new Dependency("SurfaceDescriptionInputs.vertexColor", "FragInputs.color"),
};
};
static void AddActiveFieldsFromGraphRequirements(HashSet<string> activeFields, ShaderGraphRequirements requirements)
{
if (requirements.requiresScreenPosition)
{
activeFields.Add("SurfaceDescriptionInputs.screenPosition");
}
if (requirements.requiresVertexColor)
{
activeFields.Add("SurfaceDescriptionInputs.vertexColor");
}
if (requirements.requiresNormal != 0)
{
if ((requirements.requiresNormal & NeededCoordinateSpace.Object) > 0)
activeFields.Add("SurfaceDescriptionInputs.ObjectSpaceNormal");
if ((requirements.requiresNormal & NeededCoordinateSpace.View) > 0)
activeFields.Add("SurfaceDescriptionInputs.ViewSpaceNormal");
if ((requirements.requiresNormal & NeededCoordinateSpace.World) > 0)
activeFields.Add("SurfaceDescriptionInputs.WorldSpaceNormal");
if ((requirements.requiresNormal & NeededCoordinateSpace.Tangent) > 0)
activeFields.Add("SurfaceDescriptionInputs.TangentSpaceNormal");
}
if (requirements.requiresTangent != 0)
{
if ((requirements.requiresTangent & NeededCoordinateSpace.Object) > 0)
activeFields.Add("SurfaceDescriptionInputs.ObjectSpaceTangent");
if ((requirements.requiresTangent & NeededCoordinateSpace.View) > 0)
activeFields.Add("SurfaceDescriptionInputs.ViewSpaceTangent");
if ((requirements.requiresTangent & NeededCoordinateSpace.World) > 0)
activeFields.Add("SurfaceDescriptionInputs.WorldSpaceTangent");
if ((requirements.requiresTangent & NeededCoordinateSpace.Tangent) > 0)
activeFields.Add("SurfaceDescriptionInputs.TangentSpaceTangent");
}
if (requirements.requiresBitangent != 0)
{
if ((requirements.requiresBitangent & NeededCoordinateSpace.Object) > 0)
activeFields.Add("SurfaceDescriptionInputs.ObjectSpaceBiTangent");
if ((requirements.requiresBitangent & NeededCoordinateSpace.View) > 0)
activeFields.Add("SurfaceDescriptionInputs.ViewSpaceBiTangent");
if ((requirements.requiresBitangent & NeededCoordinateSpace.World) > 0)
activeFields.Add("SurfaceDescriptionInputs.WorldSpaceBiTangent");
if ((requirements.requiresBitangent & NeededCoordinateSpace.Tangent) > 0)
activeFields.Add("SurfaceDescriptionInputs.TangentSpaceBiTangent");
}
if (requirements.requiresViewDir != 0)
{
if ((requirements.requiresViewDir & NeededCoordinateSpace.Object) > 0)
activeFields.Add("SurfaceDescriptionInputs.ObjectSpaceViewDirection");
if ((requirements.requiresViewDir & NeededCoordinateSpace.View) > 0)
activeFields.Add("SurfaceDescriptionInputs.ViewSpaceViewDirection");
if ((requirements.requiresViewDir & NeededCoordinateSpace.World) > 0)
activeFields.Add("SurfaceDescriptionInputs.WorldSpaceViewDirection");
if ((requirements.requiresViewDir & NeededCoordinateSpace.Tangent) > 0)
activeFields.Add("SurfaceDescriptionInputs.TangentSpaceViewDirection");
}
if (requirements.requiresPosition != 0)
{
if ((requirements.requiresPosition & NeededCoordinateSpace.Object) > 0)
activeFields.Add("SurfaceDescriptionInputs.ObjectSpacePosition");
if ((requirements.requiresPosition & NeededCoordinateSpace.View) > 0)
activeFields.Add("SurfaceDescriptionInputs.ViewSpacePosition");
if ((requirements.requiresPosition & NeededCoordinateSpace.World) > 0)
activeFields.Add("SurfaceDescriptionInputs.WorldSpacePosition");
if ((requirements.requiresPosition & NeededCoordinateSpace.Tangent) > 0)
activeFields.Add("SurfaceDescriptionInputs.TangentSpacePosition");
}
foreach (var channel in requirements.requiresMeshUVs.Distinct())
{
activeFields.Add("SurfaceDescriptionInputs." + channel.GetUVName());
}
}
// TODO : split this function into buildActiveFields and buildHLSLTypeDeclaration functions
public static void Generate(
ShaderGenerator codeResult,
ShaderGenerator graphInputsResult,
ShaderGraphRequirements graphRequirements,
List<string> passRequiredFields, // fields the pass requires
CoordinateSpace preferedCoordinateSpace,
HashSet<string> activeFields)
{
if (preferedCoordinateSpace == CoordinateSpace.Tangent)
preferedCoordinateSpace = CoordinateSpace.World;
// build initial requirements
AddActiveFieldsFromGraphRequirements(activeFields, graphRequirements);
if (passRequiredFields != null)
{
foreach (var requiredField in passRequiredFields)
{
activeFields.Add(requiredField);
}
}
// propagate requirements using dependencies
{
ShaderSpliceUtil.ApplyDependencies(
activeFields,
new List<Dependency[]>()
{
FragInputs.dependencies,
VaryingsMeshToPS.standardDependencies,
SurfaceDescriptionInputs.dependencies,
});
}
// generate code based on requirements
ShaderSpliceUtil.BuildType(typeof(AttributesMesh), activeFields, codeResult);
ShaderSpliceUtil.BuildType(typeof(VaryingsMeshToPS), activeFields, codeResult);
ShaderSpliceUtil.BuildType(typeof(VaryingsMeshToDS), activeFields, codeResult);
ShaderSpliceUtil.BuildPackedType(typeof(VaryingsMeshToPS), activeFields, codeResult);
ShaderSpliceUtil.BuildPackedType(typeof(VaryingsMeshToDS), activeFields, codeResult);
ShaderSpliceUtil.BuildType(typeof(SurfaceDescriptionInputs), activeFields, graphInputsResult);
}
};
public struct Pass
{
public string Name;
public string LightMode;
public string ShaderPassName;
public List<string> Includes;
public string TemplateName;
public List<string> ExtraDefines;
public List<int> VertexShaderSlots; // These control what slots are used by the pass vertex shader
public List<int> PixelShaderSlots; // These control what slots are used by the pass pixel shader
public string CullOverride;
public string BlendOverride;
public string BlendOpOverride;
public string ZTestOverride;
public string ZWriteOverride;
public string ColorMaskOverride;
public List<string> StencilOverride;
public List<string> RequiredFields; // feeds into the dependency analysis
public ShaderGraphRequirements requirements;
};
public static class HDSubShaderUtilities
{
public static void BuildRenderStatesFromPassAndMaterialOptions(
Pass pass,
SurfaceMaterialOptions materialOptions,
ShaderStringBuilder blendCode,
ShaderStringBuilder cullCode,
ShaderStringBuilder zTestCode,
ShaderStringBuilder zWriteCode,
ShaderStringBuilder stencilCode,
ShaderStringBuilder colorMaskCode)
{
if (pass.BlendOverride != null)
{
blendCode.AppendLine(pass.BlendOverride);
}
else
{
materialOptions.GetBlend(blendCode);
}
if (pass.BlendOpOverride != null)
{
blendCode.AppendLine(pass.BlendOpOverride);
}
if (pass.CullOverride != null)
{
cullCode.AppendLine(pass.CullOverride);
}
else
{
materialOptions.GetCull(cullCode);
}
if (pass.ZTestOverride != null)
{
zTestCode.AppendLine(pass.ZTestOverride);
}
else
{
materialOptions.GetDepthTest(zTestCode);
}
if (pass.ZWriteOverride != null)
{
zWriteCode.AppendLine(pass.ZWriteOverride);
}
else
{
materialOptions.GetDepthWrite(zWriteCode);
}
if (pass.ColorMaskOverride != null)
{
colorMaskCode.AppendLine(pass.ColorMaskOverride);
}
else
{
// material option default is to not declare anything for color mask
}
if (pass.StencilOverride != null)
{
foreach (var str in pass.StencilOverride)
{
stencilCode.AppendLine(str);
}
}
else
{
stencilCode.AppendLine("// Default Stencil");
}
}
public static SurfaceMaterialOptions BuildMaterialOptions(SurfaceType surfaceType, AlphaMode alphaMode, bool twoSided)
{
SurfaceMaterialOptions materialOptions = new SurfaceMaterialOptions();
if (surfaceType == SurfaceType.Opaque)
{
materialOptions.srcBlend = SurfaceMaterialOptions.BlendMode.One;
materialOptions.dstBlend = SurfaceMaterialOptions.BlendMode.Zero;
materialOptions.zTest = SurfaceMaterialOptions.ZTest.LEqual;
materialOptions.zWrite = SurfaceMaterialOptions.ZWrite.On;
materialOptions.renderQueue = SurfaceMaterialOptions.RenderQueue.Geometry;
materialOptions.renderType = SurfaceMaterialOptions.RenderType.Opaque;
}
else
{
switch (alphaMode)
{
case AlphaMode.Alpha:
materialOptions.srcBlend = SurfaceMaterialOptions.BlendMode.SrcAlpha;
materialOptions.dstBlend = SurfaceMaterialOptions.BlendMode.OneMinusSrcAlpha;
materialOptions.zTest = SurfaceMaterialOptions.ZTest.LEqual;
materialOptions.zWrite = SurfaceMaterialOptions.ZWrite.Off;
materialOptions.renderQueue = SurfaceMaterialOptions.RenderQueue.Transparent;
materialOptions.renderType = SurfaceMaterialOptions.RenderType.Transparent;
break;
case AlphaMode.Additive:
materialOptions.srcBlend = SurfaceMaterialOptions.BlendMode.One;
materialOptions.dstBlend = SurfaceMaterialOptions.BlendMode.One;
materialOptions.zTest = SurfaceMaterialOptions.ZTest.LEqual;
materialOptions.zWrite = SurfaceMaterialOptions.ZWrite.Off;
materialOptions.renderQueue = SurfaceMaterialOptions.RenderQueue.Transparent;
materialOptions.renderType = SurfaceMaterialOptions.RenderType.Transparent;
break;
// TODO: other blend modes
}
}
materialOptions.cullMode = twoSided ? SurfaceMaterialOptions.CullMode.Off : SurfaceMaterialOptions.CullMode.Back;
return materialOptions;
}
}
}

11
ShaderGraph/HDPipeline/HDSubShaderUtilities.cs.meta


fileFormatVersion: 2
guid: 713ced4e6eef4a44799a4dd59041484b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

21
ShaderGraph/com.unity.shadergraph/Editor/Data/Util/GraphContext.cs


using System;
using UnityEngine;
namespace UnityEditor.ShaderGraph
{
public class GraphContext
{
public GraphContext(string inputStructName)
{
graphInputStructName = inputStructName;
}
public string graphInputStructName
{
get { return m_GraphInputStructName; }
set { m_GraphInputStructName = value; }
}
string m_GraphInputStructName;
}
}

11
ShaderGraph/com.unity.shadergraph/Editor/Data/Util/GraphContext.cs.meta


fileFormatVersion: 2
guid: 9af661b17b85d264b9549380599875a6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

366
ShaderGraph/com.unity.shadergraph/Editor/Templates/HDPBRPass.template


Pass
{
// based on HDPBRPass.template
Name "${PassName}"
Tags { "LightMode" = "${LightMode}" }
//-------------------------------------------------------------------------------------
// Render Modes (Blend, Cull, ZTest, Stencil, etc)
//-------------------------------------------------------------------------------------
${Blending}
${Culling}
${ZTest}
${ZWrite}
${Stencil}
${ColorMask}
//-------------------------------------------------------------------------------------
// End Render Modes
//-------------------------------------------------------------------------------------
HLSLPROGRAM
#pragma target 4.5
#pragma only_renderers d3d11 ps4 xboxone vulkan metal switch
//#pragma enable_d3d11_debug_symbols
//-------------------------------------------------------------------------------------
// Variant Definitions
//-------------------------------------------------------------------------------------
${VariantDefines}
//-------------------------------------------------------------------------------------
// End Variant
//-------------------------------------------------------------------------------------
#pragma vertex Vert
#pragma fragment Frag
#define UNITY_MATERIAL_LIT // Need to be define before including Material.hlsl
// Use surface gradient normal mapping as it handle correctly triplanar normal mapping and multiple UVSet
// this modifies the normal calculation
// #define SURFACE_GRADIENT
// This shader support vertex modification (or not)
// TODO - move to PBR shader control
// #define HAVE_VERTEX_MODIFICATION
// If we use subsurface scattering, enable output split lighting (for forward pass)
#if defined(_MATID_SSS) && !defined(_SURFACE_TYPE_TRANSPARENT)
#define OUTPUT_SPLIT_LIGHTING
#endif
#include "CoreRP/ShaderLibrary/Common.hlsl"
#include "CoreRP/ShaderLibrary/Wind.hlsl"
#include "CoreRP/ShaderLibrary/NormalSurfaceGradient.hlsl"
#include "ShaderGraphLibrary/Functions.hlsl"
// define FragInputs structure
#include "HDRP/ShaderPass/FragInputs.hlsl"
#include "HDRP/ShaderPass/ShaderPass.cs.hlsl"
//-------------------------------------------------------------------------------------
// Defines
//-------------------------------------------------------------------------------------
${Defines}
// this translates the new dependency tracker into the old preprocessor definitions for the existing HDRP shader code
$AttributesMesh.normalOS: #define ATTRIBUTES_NEED_NORMAL
$AttributesMesh.tangentOS: #define ATTRIBUTES_NEED_TANGENT
$AttributesMesh.uv0: #define ATTRIBUTES_NEED_TEXCOORD0
$AttributesMesh.uv1: #define ATTRIBUTES_NEED_TEXCOORD1
$AttributesMesh.uv2: #define ATTRIBUTES_NEED_TEXCOORD2
$AttributesMesh.uv3: #define ATTRIBUTES_NEED_TEXCOORD3
$AttributesMesh.color: #define ATTRIBUTES_NEED_COLOR
$VaryingsMeshToPS.positionWS: #define VARYINGS_NEED_POSITION_WS
$VaryingsMeshToPS.normalWS: #define VARYINGS_NEED_TANGENT_TO_WORLD
$VaryingsMeshToPS.texCoord0: #define VARYINGS_NEED_TEXCOORD0
$VaryingsMeshToPS.texCoord1: #define VARYINGS_NEED_TEXCOORD1
$VaryingsMeshToPS.texCoord2: #define VARYINGS_NEED_TEXCOORD2
$VaryingsMeshToPS.texCoord3: #define VARYINGS_NEED_TEXCOORD3
$VaryingsMeshToPS.color: #define VARYINGS_NEED_COLOR
$VaryingsMeshToPS.cullFace: #define VARYINGS_NEED_CULLFACE
//-------------------------------------------------------------------------------------
// End Defines
//-------------------------------------------------------------------------------------
#include "HDRP/ShaderVariables.hlsl"
#ifdef DEBUG_DISPLAY
#include "HDRP/Debug/DebugDisplay.hlsl"
#endif
#if (SHADERPASS == SHADERPASS_FORWARD)
// used for shaders that want to do lighting (and materials)
#include "HDRP/Lighting/Lighting.hlsl"
#else
// used for shaders that don't need lighting
#include "HDRP/Material/Material.hlsl"
#endif
#include "HDRP/Material/MaterialUtilities.hlsl"
// this function assumes the bitangent flip is encoded in tangentWS.w
// TODO: move this function to HDRP shared file, once we merge with HDRP repo
float3x3 BuildWorldToTangent(float4 tangentWS, float3 normalWS)
{
// tangentWS must not be normalized (mikkts requirement)
// Normalize normalWS vector but keep the renormFactor to apply it to bitangent and tangent
float3 unnormalizedNormalWS = normalWS;
float renormFactor = 1.0 / length(unnormalizedNormalWS);
// bitangent on the fly option in xnormal to reduce vertex shader outputs.
// this is the mikktspace transformation (must use unnormalized attributes)
float3x3 worldToTangent = CreateWorldToTangent(unnormalizedNormalWS, tangentWS.xyz, tangentWS.w > 0.0 ? 1.0 : -1.0);
// surface gradient based formulation requires a unit length initial normal. We can maintain compliance with mikkts
// by uniformly scaling all 3 vectors since normalization of the perturbed normal will cancel it.
worldToTangent[0] = worldToTangent[0] * renormFactor;
worldToTangent[1] = worldToTangent[1] * renormFactor;
worldToTangent[2] = worldToTangent[2] * renormFactor; // normalizes the interpolated vertex normal
return worldToTangent;
}
//-------------------------------------------------------------------------------------
// Interpolator Packing And Struct Declarations
//-------------------------------------------------------------------------------------
${InterpolatorPacking}
//-------------------------------------------------------------------------------------
// End Interpolator Packing And Struct Declarations
//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
// Graph generated code
//-------------------------------------------------------------------------------------
${Graph}
//-------------------------------------------------------------------------------------
// End graph generated code
//-------------------------------------------------------------------------------------
// TODO: Do we want to build include functionality for sharing these preprocessed functions across templates?
FragInputs BuildFragInputs(VaryingsMeshToPS input)
{
FragInputs output;
ZERO_INITIALIZE(FragInputs, output);
// Init to some default value to make the computer quiet (else it output 'divide by zero' warning even if value is not used).
// TODO: this is a really poor workaround, but the variable is used in a bunch of places
// to compute normals which are then passed on elsewhere to compute other values...
output.worldToTangent = k_identity3x3;
output.positionSS = input.positionCS; // input.positionCS is SV_Position
$FragInputs.positionWS: output.positionWS = input.positionWS;
$FragInputs.worldToTangent: output.worldToTangent = BuildWorldToTangent(input.tangentWS, input.normalWS);
$FragInputs.texCoord0: output.texCoord0 = input.texCoord0;
$FragInputs.texCoord1: output.texCoord1 = input.texCoord1;
$FragInputs.texCoord2: output.texCoord2 = input.texCoord2;
$FragInputs.texCoord3: output.texCoord3 = input.texCoord3;
$FragInputs.color: output.color = input.color;
#if SHADER_STAGE_FRAGMENT
$FragInputs.isFrontFace: output.isFrontFace = IS_FRONT_VFACE(input.cullFace, true, false); // TODO: SHADER_STAGE_FRAGMENT only
$FragInputs.isFrontFace: // Handle handness of the view matrix (In Unity view matrix default to a determinant of -1)
$FragInputs.isFrontFace: // when we render a cubemap the view matrix handness is flipped (due to convention used for cubemap) we have a determinant of +1
$FragInputs.isFrontFace: output.isFrontFace = _DetViewMatrix < 0.0 ? output.isFrontFace : !output.isFrontFace;
#endif // SHADER_STAGE_FRAGMENT
return output;
}
SurfaceDescriptionInputs FragInputsToSurfaceDescriptionInputs(FragInputs input, float3 viewWS)
{
SurfaceDescriptionInputs output;
ZERO_INITIALIZE(SurfaceDescriptionInputs, output);
$SurfaceDescriptionInputs.WorldSpaceNormal: output.WorldSpaceNormal = normalize(input.worldToTangent[2].xyz);
$SurfaceDescriptionInputs.ObjectSpaceNormal: output.ObjectSpaceNormal = mul(output.WorldSpaceNormal, (float3x3) unity_ObjectToWorld); // transposed multiplication by inverse matrix to handle normal scale
$SurfaceDescriptionInputs.ViewSpaceNormal: output.ViewSpaceNormal = mul(output.WorldSpaceNormal, (float3x3) UNITY_MATRIX_I_V); // transposed multiplication by inverse matrix to handle normal scale
$SurfaceDescriptionInputs.TangentSpaceNormal: output.TangentSpaceNormal = float3(0.0f, 0.0f, 1.0f);
$SurfaceDescriptionInputs.WorldSpaceTangent: output.WorldSpaceTangent = input.worldToTangent[0].xyz;
$SurfaceDescriptionInputs.ObjectSpaceTangent: output.ObjectSpaceTangent = mul((float3x3) unity_WorldToObject, output.WorldSpaceTangent);
$SurfaceDescriptionInputs.ViewSpaceTangent: output.ViewSpaceTangent = mul((float3x3) UNITY_MATRIX_V, output.WorldSpaceTangent);
$SurfaceDescriptionInputs.TangentSpaceTangent: output.TangentSpaceTangent = float3(1.0f, 0.0f, 0.0f);
$SurfaceDescriptionInputs.WorldSpaceBiTangent: output.WorldSpaceBiTangent = input.worldToTangent[1].xyz;
$SurfaceDescriptionInputs.ObjectSpaceBiTangent: output.ObjectSpaceBiTangent = mul((float3x3) unity_WorldToObject, output.WorldSpaceBiTangent);
$SurfaceDescriptionInputs.ViewSpaceBiTangent: output.ViewSpaceBiTangent = mul((float3x3) UNITY_MATRIX_V, output.WorldSpaceBiTangent);
$SurfaceDescriptionInputs.TangentSpaceBiTangent: output.TangentSpaceBiTangent = float3(0.0f, 1.0f, 0.0f);
$SurfaceDescriptionInputs.WorldSpaceViewDirection: output.WorldSpaceViewDirection = normalize(viewWS);
$SurfaceDescriptionInputs.ObjectSpaceViewDirection: output.ObjectSpaceViewDirection = mul((float3x3) unity_WorldToObject, output.WorldSpaceViewDirection);
$SurfaceDescriptionInputs.ViewSpaceViewDirection: output.ViewSpaceViewDirection = mul((float3x3) UNITY_MATRIX_V, output.WorldSpaceViewDirection);
$SurfaceDescriptionInputs.TangentSpaceViewDirection: float3x3 tangentSpaceTransform = float3x3(output.WorldSpaceTangent,output.WorldSpaceBiTangent,output.WorldSpaceNormal);
$SurfaceDescriptionInputs.TangentSpaceViewDirection: output.TangentSpaceViewDirection = mul(tangentSpaceTransform, output.WorldSpaceViewDirection);
// TODO: FragInputs.positionWS is badly named -- it's camera relative, not in world space
// we have to fix it up here to match graph input expectations
$SurfaceDescriptionInputs.WorldSpacePosition: output.WorldSpacePosition = input.positionWS + _WorldSpaceCameraPos;
$SurfaceDescriptionInputs.ObjectSpacePosition: output.ObjectSpacePosition = mul(unity_WorldToObject, float4(input.positionWS + _WorldSpaceCameraPos, 1.0f)).xyz;
$SurfaceDescriptionInputs.ViewSpacePosition: float4 posViewSpace = mul(UNITY_MATRIX_V, float4(input.positionWS, 1.0f));
$SurfaceDescriptionInputs.ViewSpacePosition: output.ViewSpacePosition = posViewSpace.xyz / posViewSpace.w;
$SurfaceDescriptionInputs.TangentSpacePosition: output.TangentSpacePosition = float3(0.0f, 0.0f, 0.0f);
// TODO: positionSS is SV_Position, graph input expects screenPosition to be 0..1 across the active viewport (?)
$SurfaceDescriptionInputs.screenPosition: output.screenPosition = input.positionSS;
$SurfaceDescriptionInputs.uv0: output.uv0 = float4(input.texCoord0, 0.0f, 0.0f);
$SurfaceDescriptionInputs.uv1: output.uv1 = float4(input.texCoord1, 0.0f, 0.0f);
$SurfaceDescriptionInputs.uv2: output.uv2 = float4(input.texCoord2, 0.0f, 0.0f);
$SurfaceDescriptionInputs.uv3: output.uv3 = float4(input.texCoord3, 0.0f, 0.0f);
$SurfaceDescriptionInputs.vertexColor: output.vertexColor = input.color;
return output;
}
// existing HDRP code uses the combined function to go directly from packed to frag inputs
FragInputs UnpackVaryingsMeshToFragInputs(PackedVaryingsMeshToPS input)
{
VaryingsMeshToPS unpacked= UnpackVaryingsMeshToPS(input);
return BuildFragInputs(unpacked);
}
void BuildSurfaceData(FragInputs fragInputs, SurfaceDescription surfaceDescription, float3 V, out SurfaceData surfaceData)
{
// setup defaults -- these are used if the graph doesn't output a value
ZERO_INITIALIZE(SurfaceData, surfaceData);
surfaceData.ambientOcclusion = 1.0f;
surfaceData.subsurfaceMask = 1.0f;
// copy across graph values, if defined
$SurfaceDescription.Albedo: surfaceData.baseColor = surfaceDescription.Albedo;
$SurfaceDescription.Smoothness: surfaceData.perceptualSmoothness = surfaceDescription.Smoothness;
$SurfaceDescription.Occlusion: surfaceData.ambientOcclusion = surfaceDescription.Occlusion;
$SurfaceDescription.Metallic: surfaceData.metallic = surfaceDescription.Metallic;
// surfaceData.thickness = surfaceDescription.Thickness;
// surfaceData.diffusionProfile = surfaceDescription.DiffusionProfile;
// surfaceData.subsurfaceMask = surfaceDescription.SubsurfaceMask;
$SurfaceDescription.Specular: surfaceData.specularColor = surfaceDescription.Specular;
// These static material feature allow compile time optimization
surfaceData.materialFeatures = MATERIALFEATUREFLAGS_LIT_STANDARD;
#ifdef _MATERIAL_FEATURE_SUBSURFACE_SCATTERING
surfaceData.materialFeatures |= MATERIALFEATUREFLAGS_LIT_SUBSURFACE_SCATTERING;
#endif
#ifdef _MATERIAL_FEATURE_TRANSMISSION
surfaceData.materialFeatures |= MATERIALFEATUREFLAGS_LIT_TRANSMISSION;
#endif
#ifdef _MATERIAL_FEATURE_ANISOTROPY
surfaceData.materialFeatures |= MATERIALFEATUREFLAGS_LIT_ANISOTROPY;
#endif
#ifdef _MATERIAL_FEATURE_CLEAR_COAT
surfaceData.materialFeatures |= MATERIALFEATUREFLAGS_LIT_CLEAR_COAT;
#endif
#ifdef _MATERIAL_FEATURE_IRIDESCENCE
surfaceData.materialFeatures |= MATERIALFEATUREFLAGS_LIT_IRIDESCENCE;
#endif
#ifdef _MATERIAL_FEATURE_SPECULAR_COLOR
surfaceData.materialFeatures |= MATERIALFEATUREFLAGS_LIT_SPECULAR_COLOR;
#endif
// tangent-space normal
float3 normalTS = float3(0.0f, 0.0f, 1.0f);
$SurfaceDescription.Normal: normalTS = surfaceDescription.Normal;
// compute world space normal
GetNormalWS(fragInputs, V, normalTS, surfaceData.normalWS);
// TODO: use surfaceDescription tangent definition for anisotropy
surfaceData.tangentWS = normalize(fragInputs.worldToTangent[0].xyz); // The tangent is not normalize in worldToTangent for mikkt. TODO: Check if it expected that we normalize with Morten. Tag: SURFACE_GRADIENT
surfaceData.tangentWS = Orthonormalize(surfaceData.tangentWS, surfaceData.normalWS);
// Init other parameters
surfaceData.anisotropy = 0;
surfaceData.coatMask = 0.0f;
surfaceData.iridescenceThickness = 0.0;
surfaceData.iridescenceMask = 1.0;
// Transparency parameters
// Use thickness from SSS
surfaceData.ior = 1.0;
surfaceData.transmittanceColor = float3(1.0, 1.0, 1.0);
surfaceData.atDistance = 1000000.0;
surfaceData.transmittanceMask = 0.0;
// By default we use the ambient occlusion with Tri-ace trick (apply outside) for specular occlusion.
// If user provide bent normal then we process a better term
surfaceData.specularOcclusion = 1.0;
#if defined(_BENTNORMALMAP) && defined(_ENABLESPECULAROCCLUSION)
// If we have bent normal and ambient occlusion, process a specular occlusion
surfaceData.specularOcclusion = GetSpecularOcclusionFromBentAO(V, bentNormalWS, surfaceData);
#elif defined(_MASKMAP)
surfaceData.specularOcclusion = GetSpecularOcclusionFromAmbientOcclusion(NdotV, surfaceData.ambientOcclusion, PerceptualSmoothnessToRoughness(surfaceData.perceptualSmoothness));
#endif
}
void GetSurfaceAndBuiltinData(FragInputs fragInputs, float3 V, inout PositionInputs posInput, out SurfaceData surfaceData, out BuiltinData builtinData)
{
// this applies the double sided tangent space correction -- see 'ApplyDoubleSidedFlipOrMirror()'
$DoubleSided: if (!fragInputs.isFrontFace) {
$DoubleSided.Flip: fragInputs.worldToTangent[1] = -fragInputs.worldToTangent[1]; // bitangent
$DoubleSided.Flip: fragInputs.worldToTangent[2] = -fragInputs.worldToTangent[2]; // normal
$DoubleSided.Mirror: fragInputs.worldToTangent[2] = -fragInputs.worldToTangent[2]; // normal
$DoubleSided: }
SurfaceDescriptionInputs surfaceDescriptionInputs = FragInputsToSurfaceDescriptionInputs(fragInputs, V);
SurfaceDescription surfaceDescription = SurfaceDescriptionFunction(surfaceDescriptionInputs);
// Perform alpha test very early to save performance (a killed pixel will not sample textures)
// TODO: split graph evaluation to grab just alpha dependencies first? tricky..
#ifdef _ALPHATEST_ON
DoAlphaTest(surfaceDescription.Alpha, surfaceDescription.AlphaClipThreshold);
#endif
BuildSurfaceData(fragInputs, surfaceDescription, V, surfaceData);
// Builtin Data -- we don't call GetBuiltinData(fragInputs, surfaceData, ...)
// that function assumes there are specific global properties defined
// for shadergraph shaders, we fill it out here instead
ZERO_INITIALIZE(BuiltinData, builtinData);
float3 bentNormalWS = surfaceData.normalWS; // TODO : make bent normals work
builtinData.opacity = surfaceDescription.Alpha;
builtinData.bakeDiffuseLighting = SampleBakedGI(fragInputs.positionWS, bentNormalWS, fragInputs.texCoord1, fragInputs.texCoord2); // see GetBuiltinData()
// It is safe to call this function here as surfaceData have been filled
// We want to know if we must enable transmission on GI for SSS material, if the material have no SSS, this code will be remove by the compiler.
BSDFData bsdfData = ConvertSurfaceDataToBSDFData(surfaceData);
if (HasFeatureFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_LIT_TRANSMISSION))
{
// For now simply recall the function with inverted normal, the compiler should be able to optimize the lightmap case to not resample the directional lightmap
// however it will not optimize the lightprobe case due to the proxy volume relying on dynamic if (we rely must get right of this dynamic if), not a problem for SH9, but a problem for proxy volume.
// TODO: optimize more this code.
// Add GI transmission contribution by resampling the GI for inverted vertex normal
builtinData.bakeDiffuseLighting += SampleBakedGI(fragInputs.positionWS, -fragInputs.worldToTangent[2], fragInputs.texCoord1, fragInputs.texCoord2) * bsdfData.transmittance;
}
builtinData.emissiveIntensity = 1.0f;
$SurfaceDescription.Emission: builtinData.emissiveColor = surfaceDescription.Emission;
builtinData.velocity = float2(0.0, 0.0);
#ifdef SHADOWS_SHADOWMASK
float4 shadowMask = SampleShadowMask(fragInputs.positionWS, fragInputs.texCoord1);
builtinData.shadowMask0 = shadowMask.x;
builtinData.shadowMask1 = shadowMask.y;
builtinData.shadowMask2 = shadowMask.z;
builtinData.shadowMask3 = shadowMask.w;
#else
builtinData.shadowMask0 = 0.0;
builtinData.shadowMask1 = 0.0;
builtinData.shadowMask2 = 0.0;
builtinData.shadowMask3 = 0.0;
#endif
builtinData.distortion = float2(0.0, 0.0); // surfaceDescription.Distortion -- if distortion pass
builtinData.distortionBlur = 0.0; // surfaceDescription.DistortionBlur -- if distortion pass
builtinData.depthOffset = 0.0; // ApplyPerPixelDisplacement(input, V, layerTexCoord, blendMasks); #ifdef _DEPTHOFFSET_ON : ApplyDepthOffsetPositionInput(V, depthOffset, GetWorldToHClipMatrix(), posInput);
}
//-------------------------------------------------------------------------------------
// Pass Includes
//-------------------------------------------------------------------------------------
${Includes}
//-------------------------------------------------------------------------------------
// End Pass Includes
//-------------------------------------------------------------------------------------
ENDHLSL
}

8
ShaderGraph/com.unity.shadergraph/Editor/Templates/HDPBRPass.template.meta


fileFormatVersion: 2
guid: 165a06313a2a64e258a559021bf37077
timeCreated: 1481194716
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存