using System.Collections.Generic ;
using System.Collections ;
using System.IO ;
using System.Linq ;
using UnityEditor.Graphing ;
PBRMasterNode . AlphaSlotId ,
PBRMasterNode . AlphaThresholdSlotId
} ,
VertexShaderSlots = new List < int > ( )
{
PBRMasterNode . PositionSlotId
}
} ;
Pass m_PassGBufferWithPrepass = new Pass ( )
PBRMasterNode . AlphaSlotId ,
PBRMasterNode . AlphaThresholdSlotId
} ,
VertexShaderSlots = new List < int > ( )
{
PBRMasterNode . PositionSlotId
}
} ;
Pass m_PassMETA = new Pass ( )
"AttributesMesh.uv1" ,
"AttributesMesh.color" ,
"AttributesMesh.uv2" , // SHADERPASS_LIGHT_TRANSPORT always uses uv2
// "FragInputs.worldToTangent",
// "FragInputs.positionWS",
} ,
PixelShaderSlots = new List < int > ( )
{
PBRMasterNode . OcclusionSlotId ,
PBRMasterNode . AlphaSlotId ,
PBRMasterNode . AlphaThresholdSlotId
}
} ,
// VertexShaderSlots = new List<int>()
// {
// PBRMasterNode.PositionSlotId
// }
} ;
Pass m_PassShadowCaster = new Pass ( )
{
PBRMasterNode . AlphaSlotId ,
PBRMasterNode . AlphaThresholdSlotId
} ,
VertexShaderSlots = new List < int > ( )
{
PBRMasterNode . PositionSlotId
}
} ;
{
PBRMasterNode . AlphaSlotId ,
PBRMasterNode . AlphaThresholdSlotId
} ,
VertexShaderSlots = new List < int > ( )
{
PBRMasterNode . PositionSlotId
}
} ;
{
"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" ,
" Comp Always" ,
" Pass Replace" ,
"}"
}
} ,
PixelShaderSlots = new List < int > ( )
{
PBRMasterNode . AlphaSlotId ,
PBRMasterNode . AlphaThresholdSlotId
} ,
VertexShaderSlots = new List < int > ( )
{
PBRMasterNode . PositionSlotId
} ,
} ;
Pass m_PassDistortion = new Pass ( )
{
PBRMasterNode . AlphaSlotId ,
PBRMasterNode . AlphaThresholdSlotId
}
} ,
VertexShaderSlots = new List < int > ( )
{
PBRMasterNode . PositionSlotId
} ,
} ;
Pass m_PassTransparentDepthPrepass = new Pass ( )
{
PBRMasterNode . AlphaSlotId ,
PBRMasterNode . AlphaThresholdSlotId
}
} ,
VertexShaderSlots = new List < int > ( )
{
PBRMasterNode . PositionSlotId
} ,
} ;
Pass m_PassTransparentBackface = new Pass ( )
PBRMasterNode . OcclusionSlotId ,
PBRMasterNode . AlphaSlotId ,
PBRMasterNode . AlphaThresholdSlotId
}
} ,
VertexShaderSlots = new List < int > ( )
{
PBRMasterNode . PositionSlotId
} ,
} ;
Pass m_PassForward = new Pass ( )
PBRMasterNode . OcclusionSlotId ,
PBRMasterNode . AlphaSlotId ,
PBRMasterNode . AlphaThresholdSlotId
}
} ,
VertexShaderSlots = new List < int > ( )
{
PBRMasterNode . PositionSlotId
} ,
} ;
Pass m_PassTransparentDepthPostpass = new Pass ( )
{
PBRMasterNode . AlphaSlotId ,
PBRMasterNode . AlphaThresholdSlotId
}
} ,
VertexShaderSlots = new List < int > ( )
{
PBRMasterNode . PositionSlotId
} ,
private static string GetVariantDefines ( PBRMasterNode masterNode )
private static HashSet < string > GetActiveFieldsFromMasterNode ( INode iMasterNode , Pass pass )
ShaderGenerator defines = new ShaderGenerator ( ) ;
HashSet < string > activeFields = new HashSet < string > ( ) ;
PBRMasterNode masterNode = iMasterNode as PBRMasterNode ;
if ( masterNode = = null )
{
return activeFields ;
}
// TODO:
// _MATERIAL_FEATURE_SUBSURFACE_SCATTERING
// _MATERIAL_FEATURE_TRANSMISSION
// _MATERIAL_FEATURE_ANISOTROPY
// _MATERIAL_FEATURE_CLEAR_COAT
// _MATERIAL_FEATURE_IRIDESCENCE
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
}
}
switch ( masterNode . model )
{
defines . AddShaderChunk ( "#define _MATERIAL_FEATURE_SPECULAR_COLOR 1" , true ) ;
activeFields . Add ( "Material.SpecularColor" ) ;
break ;
default :
// TODO: error!
// #pragma shader_feature _ALPHATEST_ON
defines . AddShaderChunk ( "#define _ALPHATEST_ON 1" , true ) ;
activeFields . Add ( "AlphaTest" ) ;
}
// if (kTesselationMode != TessellationMode.None)
// #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 _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
if ( masterNode . surfaceType ! = SurfaceType . Opaque )
{
// transparent-only defines
define s. AddShaderChunk ( "#define _SURFACE_TYPE_TRANSPARENT 1" , true ) ;
activeField s. Add ( "SurfaceType.Transparent" ) ;
define s. AddShaderChunk ( "#define _BLENDMODE_ALPHA 1" , true ) ;
activeField s. Add ( "BlendMode.Alpha" ) ;
define s. AddShaderChunk ( "#define _BLENDMODE_ADD 1" , true ) ;
activeField s. Add ( "BlendMode.Add" ) ;
}
// else if (masterNode.alphaMode == PBRMasterNode.AlphaMode.PremultiplyAlpha) // TODO
// {
{
// 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
return defines . GetShaderString ( 2 ) ;
return activeFields ;
private static bool GenerateShaderPass ( PBRMasterNode masterNode , Pass pass , GenerationMode mode , SurfaceMaterialOptions materialOptions , ShaderGenerator result , List < string > sourceAssetDependencyPaths )
private static bool GenerateShaderPassLit ( AbstractMaterialNode masterNode , Pass pass , GenerationMode mode , SurfaceMaterialOptions materialOptions , ShaderGenerator result , List < string > sourceAssetDependencyPaths )
{
var templateLocation = Path . Combine ( Path . Combine ( Path . Combine ( HDEditorUtils . GetHDRenderPipelinePath ( ) , "Editor" ) , "ShaderGraph" ) , pass . TemplateName ) ;
if ( ! File . Exists ( templateLocation ) )
if ( sourceAssetDependencyPaths ! = null )
sourceAssetDependencyPaths . Add ( templateLocation ) ;
// grab all of the active nodes
var activeNodeList = ListPool < INode > . Get ( ) ;
NodeUtils . DepthFirstCollectNodesFromNode ( activeNodeList , masterNode , NodeUtils . IncludeSelf . Include , pass . PixelShaderSlots ) ;
// grab all of the active nodes (for pixel and vertex graphs)
var vertexNodes = ListPool < INode > . Get ( ) ;
NodeUtils . DepthFirstCollectNodesFromNode ( vertexNodes , masterNode , NodeUtils . IncludeSelf . Include , pass . VertexShaderSlots ) ;
var pixelNodes = ListPool < INode > . Get ( ) ;
NodeUtils . DepthFirstCollectNodesFromNode ( pixelNodes , masterNode , NodeUtils . IncludeSelf . Include , pass . PixelShaderSlots ) ;
var graphRequirements = ShaderGraphRequirements . FromNodes ( activeNodeList , ShaderStageCapability . Fragment ) ;
var pixelRequirements = ShaderGraphRequirements . FromNodes ( pixelNodes , ShaderStageCapability . Fragment , false ) ; // TODO: is ShaderStageCapability.Fragment correct?
var vertexRequirements = ShaderGraphRequirements . FromNodes ( vertexNodes , ShaderStageCapability . Vertex , false ) ;
// Function Registry tracks functions to remove duplicates, it wraps a string builder that stores the combined function string
// TODO: this can be a shared function for all HDRP master nodes -- From here through GraphUtil.GenerateSurfaceDescription(..)
// 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 ) ;
}
}
var pixelSlots = HDSubShaderUtilities . FindMaterialSlotsOnNode ( pass . PixelShaderSlots , masterNode ) ;
var vertexSlots = HDSubShaderUtilities . FindMaterialSlotsOnNode ( pass . VertexShaderSlots , masterNode ) ;
// properties used by either pixel and vertex shader
PropertyCollector sharedProperties = new PropertyCollector ( ) ;
string graphInputStructName = "SurfaceDescriptionInputs" ;
string graphOutputStructName = "SurfaceDescription" ;
string graphEvalFunctionName = "SurfaceDescriptionFunction" ;
ShaderStringBuilder graphEvalFunction = new ShaderStringBuilder ( ) ;
ShaderStringBuilder graphOutputs = new ShaderStringBuilder ( ) ;
PropertyCollector graphProperties = new PropertyCollector ( ) ;
string pixelGraphInputStructName = "SurfaceDescriptionInputs" ;
string pixelGraphOutputStructName = "SurfaceDescription" ;
string pixelGraphEvalFunctionName = "SurfaceDescriptionFunction" ;
ShaderStringBuilder pixelGraphEvalFunction = new ShaderStringBuilder ( ) ;
ShaderStringBuilder pixelGraphOutputs = new ShaderStringBuilder ( ) ;
// dependency tracker -- set of active fields
HashSet < string > activeFields = GetActiveFieldsFromMasterNode ( masterNode , pass ) ;
// build initial requirements
HDRPShaderStructs . AddActiveFieldsFromPixelGraphRequirements ( activeFields , pixelRequirements ) ;
HashSet < string > activeFields = new HashSet < string > ( ) ;
GraphUtil . GenerateSurfaceDescriptionStruct ( graphOutputs , activeSlots , true ) ;
//GraphUtil.GenerateSurfaceDescriptionStruct(graphOutputs, activeSlots, true, graphOutputStructName, activeFields);
GraphUtil . GenerateSurfaceDescriptionStruct ( pixelGraphOutputs , pixelSlots , true , pixelGraphOutputStructName , activeFields ) ;
activeNodeList ,
pixelNodes ,
graphEvalFunction ,
pixelGraphEvalFunction ,
graphProperties ,
graphRequirements , // TODO : REMOVE UNUSED
sharedProperties ,
pixelRequirements , // TODO : REMOVE UNUSED
graphEvalFunctionName ,
graphOutputStructName ,
pixelGraphEvalFunctionName ,
pixelGraphOutputStructName ,
activeSlots ,
graphInputStructName ) ;
pixelSlots ,
pixelGraphInputStructName ) ;
string vertexGraphInputStructName = "VertexDescriptionInputs" ;
string vertexGraphOutputStructName = "VertexDescription" ;
string vertexGraphEvalFunctionName = "VertexDescriptionFunction" ;
ShaderStringBuilder vertexGraphEvalFunction = new ShaderStringBuilder ( ) ;
ShaderStringBuilder vertexGraphOutputs = new ShaderStringBuilder ( ) ;
// check for vertex animation -- enables HAVE_VERTEX_MODIFICATION
bool vertexActive = false ;
if ( masterNode . IsSlotConnected ( PBRMasterNode . PositionSlotId ) )
{
vertexActive = true ;
activeFields . Add ( "features.modifyMesh" ) ;
HDRPShaderStructs . AddActiveFieldsFromVertexGraphRequirements ( activeFields , vertexRequirements ) ;
// -------------------------------------
// Generate Output structure for Vertex Description function
GraphUtil . GenerateVertexDescriptionStruct ( vertexGraphOutputs , vertexSlots , vertexGraphOutputStructName , activeFields ) ;
// -------------------------------------
// Generate Vertex Description function
GraphUtil . GenerateVertexDescriptionFunction (
masterNode . owner as AbstractMaterialGraph ,
vertexGraphEvalFunction ,
functionRegistry ,
sharedProperties ,
mode ,
vertexNodes ,
vertexSlots ,
vertexGraphInputStructName ,
vertexGraphEvalFunctionName ,
vertexGraphOutputStructName ) ;
}
var blendCode = new ShaderStringBuilder ( ) ;
var cullCode = 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
}
}
HDRPShaderStructs . AddRequiredFields ( pass . RequiredFields , activeFields ) ;
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 ) ;
}
}
}
// apply dependencies to the active fields, and build interpolators (TODO: split this function)
var graphInputs = new ShaderGenerator ( ) ;
graphInputs ,
graphRequirements ,
pass . RequiredFields ,
CoordinateSpace . World ,
activeFields ) ;
// debug output all active fields
}
}
// build graph inputs structures
ShaderGenerator pixelGraphInputs = new ShaderGenerator ( ) ;
ShaderSpliceUtil . BuildType ( typeof ( HDRPShaderStructs . SurfaceDescriptionInputs ) , activeFields , pixelGraphInputs ) ;
ShaderGenerator vertexGraphInputs = new ShaderGenerator ( ) ;
ShaderSpliceUtil . BuildType ( typeof ( HDRPShaderStructs . VertexDescriptionInputs ) , activeFields , vertexGraphInputs ) ;
ShaderGenerator defines = new ShaderGenerator ( ) ;
{
defines . AddShaderChunk ( string . Format ( "#define SHADERPASS {0}" , pass . ShaderPassName ) , true ) ;
// 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 . AddShaderChunk ( "// Shared Graph Properties (uniform inputs)" ) ;
graph . AddShaderChunk ( sharedProperties . GetPropertiesDeclaration ( 1 ) ) ;
if ( vertexActive )
{
graph . AddShaderChunk ( "// Vertex Graph Inputs" ) ;
graph . Indent ( ) ;
graph . AddGenerator ( vertexGraphInputs ) ;
graph . Deindent ( ) ;
graph . AddShaderChunk ( "// Vertex Graph Outputs" ) ;
graph . Indent ( ) ;
graph . AddShaderChunk ( vertexGraphOutputs . ToString ( ) ) ;
graph . Deindent ( ) ;
}
graph . AddShaderChunk ( "// Pixel Graph Inputs" ) ;
graph . Indent ( ) ;
graph . AddGenerator ( pixelGraphInputs ) ;
graph . Deindent ( ) ;
graph . AddShaderChunk ( "// Pixel Graph Outputs" ) ;
graph . Indent ( ) ;
graph . AddShaderChunk ( pixelGraphOutputs . ToString ( ) ) ;
graph . Deindent ( ) ;
graph . AddShaderChunk ( "// Shared Graph Node Functions" ) ;
graph . AddShaderChunk ( graphNodeFunctions . ToString ( ) ) ;
if ( vertexActive )
{
graph . AddShaderChunk ( "// Vertex Graph Evaluation" ) ;
graph . Indent ( ) ;
graph . AddShaderChunk ( vertexGraphEvalFunction . ToString ( ) ) ;
graph . Deindent ( ) ;
}
graph . AddShaderChunk ( "// Pixel Graph Evaluation" ) ;
graph . Indent ( ) ;
graph . AddShaderChunk ( pixelGraphEvalFunction . ToString ( ) ) ;
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 ( "${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 ) ;
if ( opaque )
{
GenerateShaderPass ( masterNode , m_PassGBuffer , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPass ( masterNode , m_PassGBufferWithPrepass , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPassLit ( masterNode , m_PassGBuffer , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPassLit ( masterNode , m_PassGBufferWithPrepass , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPass ( masterNode , m_PassMETA , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPass ( masterNode , m_PassShadowCaster , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPassLit ( masterNode , m_PassMETA , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPassLit ( masterNode , m_PassShadowCaster , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPass ( masterNode , m_PassDepthOnly , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPass ( masterNode , m_PassMotionVectors , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPassLit ( masterNode , m_PassDepthOnly , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPassLit ( masterNode , m_PassMotionVectors , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPass ( masterNode , m_PassDistortion , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPassLit ( masterNode , m_PassDistortion , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPass ( masterNode , m_PassTransparentDepthPrepass , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPassLit ( masterNode , m_PassTransparentDepthPrepass , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPass ( masterNode , m_PassTransparentBackface , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPassLit ( masterNode , m_PassTransparentBackface , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPass ( masterNode , m_PassForward , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPassLit ( masterNode , m_PassForward , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPass ( masterNode , m_PassTransparentDepthPostpass , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
GenerateShaderPassLit ( masterNode , m_PassTransparentDepthPostpass , mode , materialOptions , subShader , sourceAssetDependencyPaths ) ;
}
}
subShader . Deindent ( ) ;