2021-09-09 20:42:29 -04:00

589 lines
30 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using UnityEditor.ShaderGraph;
using UnityEditor.ShaderGraph.Internal;
using UnityEngine;
using UnityEngine.Rendering;
using UnityObject = UnityEngine.Object;
namespace UnityEditor.VFX
{
class VFXShaderGraphParticleOutput : VFXAbstractParticleOutput
{
//"protected" is only to be listed by VFXModel.GetSettings, we should always use GetOrRefreshShaderGraphObject
[SerializeField, VFXSetting]
protected ShaderGraphVfxAsset shaderGraph;
public override void OnEnable()
{
base.OnEnable();
}
public ShaderGraphVfxAsset GetOrRefreshShaderGraphObject()
{
//This is the only place where shaderGraph property is updated or read
if (shaderGraph == null && !object.ReferenceEquals(shaderGraph, null))
{
string assetPath = AssetDatabase.GetAssetPath(shaderGraph.GetInstanceID());
var newShaderGraph = AssetDatabase.LoadAssetAtPath<ShaderGraphVfxAsset>(assetPath);
if (newShaderGraph != null)
{
shaderGraph = newShaderGraph;
}
}
return shaderGraph;
}
public override void GetImportDependentAssets(HashSet<int> dependencies)
{
base.GetImportDependentAssets(dependencies);
if (!object.ReferenceEquals(shaderGraph, null))
dependencies.Add(shaderGraph.GetInstanceID());
}
protected VFXShaderGraphParticleOutput(bool strip = false) : base(strip) {}
static Type GetSGPropertyType(AbstractShaderProperty property)
{
switch (property.propertyType)
{
case PropertyType.Color:
return typeof(Color);
case PropertyType.Texture2D:
return typeof(Texture2D);
case PropertyType.Texture2DArray:
return typeof(Texture2DArray);
case PropertyType.Texture3D:
return typeof(Texture3D);
case PropertyType.Cubemap:
return typeof(Cubemap);
case PropertyType.Gradient:
return null;
case PropertyType.Boolean:
return typeof(bool);
case PropertyType.Float:
return typeof(float);
case PropertyType.Vector2:
return typeof(Vector2);
case PropertyType.Vector3:
return typeof(Vector3);
case PropertyType.Vector4:
return typeof(Vector4);
case PropertyType.Matrix2:
return null;
case PropertyType.Matrix3:
return null;
case PropertyType.Matrix4:
return typeof(Matrix4x4);
case PropertyType.SamplerState:
default:
return null;
}
}
public static object GetSGPropertyValue(AbstractShaderProperty property)
{
switch (property.propertyType)
{
case PropertyType.Texture2D:
return ((Texture2DShaderProperty)property).value.texture;
case PropertyType.Texture3D:
return ((Texture3DShaderProperty)property).value.texture;
case PropertyType.Cubemap:
return ((CubemapShaderProperty)property).value.cubemap;
case PropertyType.Texture2DArray:
return ((Texture2DArrayShaderProperty)property).value.textureArray;
default:
{
var type = GetSGPropertyType(property);
PropertyInfo info = property.GetType().GetProperty("value", BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
return VFXConverter.ConvertTo(info?.GetValue(property), type);
}
}
}
protected override IEnumerable<string> filteredOutSettings
{
get
{
foreach (var setting in base.filteredOutSettings)
yield return setting;
if (GetOrRefreshShaderGraphObject() != null)
{
yield return "colorMapping";
yield return "useAlphaClipping";
}
if (!VFXViewPreference.displayExperimentalOperator)
yield return "shaderGraph";
}
}
public override bool supportsUV => base.supportsUV && GetOrRefreshShaderGraphObject() == null;
public override bool exposeAlphaThreshold
{
get
{
var shaderGraph = GetOrRefreshShaderGraphObject();
if (shaderGraph == null)
{
if (base.exposeAlphaThreshold)
return true;
}
else
{
if (!shaderGraph.alphaClipping)
{
//alpha clipping isn't enabled in shaderGraph, we implicitly still allows clipping for shadow & motion vector passes.
if (!isBlendModeOpaque && (hasMotionVector || hasShadowCasting))
return true;
}
}
return false;
}
}
public override bool supportSoftParticles => base.supportSoftParticles && GetOrRefreshShaderGraphObject() == null;
public override bool hasAlphaClipping
{
get
{
var shaderGraph = GetOrRefreshShaderGraphObject();
bool noShaderGraphAlphaThreshold = shaderGraph == null && useAlphaClipping;
bool ShaderGraphAlphaThreshold = shaderGraph != null && shaderGraph.alphaClipping;
return noShaderGraphAlphaThreshold || ShaderGraphAlphaThreshold;
}
}
public override void CheckGraphBeforeImport()
{
base.CheckGraphBeforeImport();
// If the graph is reimported it can be because one of its depedency such as the shadergraphs, has been changed.
ResyncSlots(true);
}
protected override IEnumerable<VFXPropertyWithValue> inputProperties
{
get
{
IEnumerable<VFXPropertyWithValue> properties = base.inputProperties;
var shaderGraph = GetOrRefreshShaderGraphObject();
if (shaderGraph != null)
{
var shaderGraphProperties = new List<VFXPropertyWithValue>();
foreach (var property in shaderGraph.properties
.Where(t => !t.hidden)
.Select(t => new { property = t, type = GetSGPropertyType(t) })
.Where(t => t.type != null))
{
if (property.property.propertyType == PropertyType.Float)
{
var prop = property.property as Vector1ShaderProperty;
if (prop != null)
{
if (prop.floatType == FloatType.Slider)
shaderGraphProperties.Add(new VFXPropertyWithValue(new VFXProperty(property.type, property.property.referenceName, new RangeAttribute(prop.rangeValues.x, prop.rangeValues.y)), GetSGPropertyValue(property.property)));
else if (prop.floatType == FloatType.Integer)
shaderGraphProperties.Add(new VFXPropertyWithValue(new VFXProperty(typeof(int), property.property.referenceName), VFXConverter.ConvertTo(GetSGPropertyValue(property.property), typeof(int))));
else
shaderGraphProperties.Add(new VFXPropertyWithValue(new VFXProperty(property.type, property.property.referenceName), GetSGPropertyValue(property.property)));
}
}
else
shaderGraphProperties.Add(new VFXPropertyWithValue(new VFXProperty(property.type, property.property.referenceName), GetSGPropertyValue(property.property)));
}
properties = properties.Concat(shaderGraphProperties);
}
return properties;
}
}
protected class PassInfo
{
public int[] vertexPorts;
public int[] pixelPorts;
}
protected class RPInfo
{
public Dictionary<string, PassInfo> passInfos;
HashSet<int> m_AllPorts;
public IEnumerable<int> allPorts
{
get
{
if (m_AllPorts == null)
{
m_AllPorts = new HashSet<int>();
foreach (var pass in passInfos.Values)
{
foreach (var port in pass.vertexPorts)
m_AllPorts.Add(port);
foreach (var port in pass.pixelPorts)
m_AllPorts.Add(port);
}
}
return m_AllPorts;
}
}
}
protected static readonly RPInfo hdrpInfo = new RPInfo
{
passInfos = new Dictionary<string, PassInfo>()
{
{ "Forward", new PassInfo() { vertexPorts = new int[] {}, pixelPorts = new int[] { ShaderGraphVfxAsset.ColorSlotId, ShaderGraphVfxAsset.EmissiveSlotId, ShaderGraphVfxAsset.AlphaSlotId, ShaderGraphVfxAsset.AlphaThresholdSlotId } } },
{ "DepthOnly", new PassInfo() { vertexPorts = new int[] {}, pixelPorts = new int[] { ShaderGraphVfxAsset.AlphaSlotId, ShaderGraphVfxAsset.AlphaThresholdSlotId } } }
}
};
protected static readonly RPInfo hdrpLitInfo = new RPInfo
{
passInfos = new Dictionary<string, PassInfo>()
{
{ "GBuffer", new PassInfo() { vertexPorts = new int[] {}, pixelPorts = new int[] { ShaderGraphVfxAsset.BaseColorSlotId, ShaderGraphVfxAsset.AlphaSlotId, ShaderGraphVfxAsset.MetallicSlotId, ShaderGraphVfxAsset.SmoothnessSlotId, ShaderGraphVfxAsset.EmissiveSlotId, ShaderGraphVfxAsset.NormalSlotId, ShaderGraphVfxAsset.AlphaThresholdSlotId } } },
{ "Forward", new PassInfo() { vertexPorts = new int[] {}, pixelPorts = new int[] { ShaderGraphVfxAsset.BaseColorSlotId, ShaderGraphVfxAsset.AlphaSlotId, ShaderGraphVfxAsset.MetallicSlotId, ShaderGraphVfxAsset.SmoothnessSlotId, ShaderGraphVfxAsset.EmissiveSlotId, ShaderGraphVfxAsset.NormalSlotId, ShaderGraphVfxAsset.AlphaThresholdSlotId } } },
{ "DepthOnly", new PassInfo() { vertexPorts = new int[] {}, pixelPorts = new int[] { ShaderGraphVfxAsset.AlphaSlotId, ShaderGraphVfxAsset.AlphaThresholdSlotId } } }
}
};
protected override IEnumerable<VFXNamedExpression> CollectGPUExpressions(IEnumerable<VFXNamedExpression> slotExpressions)
{
foreach (var exp in base.CollectGPUExpressions(slotExpressions))
yield return exp;
var shaderGraph = GetOrRefreshShaderGraphObject();
if (shaderGraph != null)
{
foreach (var sgProperty in shaderGraph.properties)
{
if (inputSlots.Any(t => t.property.name == sgProperty.referenceName))
yield return slotExpressions.First(o => o.name == sgProperty.referenceName);
}
}
}
public override IEnumerable<string> additionalDefines
{
get
{
foreach (var def in base.additionalDefines)
yield return def;
var shaderGraph = GetOrRefreshShaderGraphObject();
if (shaderGraph != null)
{
yield return "VFX_SHADERGRAPH";
RPInfo info = currentRP;
foreach (var port in info.allPorts)
{
var portInfo = shaderGraph.GetOutput(port);
if (!string.IsNullOrEmpty(portInfo.referenceName))
yield return $"HAS_SHADERGRAPH_PARAM_{portInfo.referenceName.ToUpper(CultureInfo.InvariantCulture)}";
}
bool needsPosWS = false;
// Per pass define
foreach (var kvPass in graphCodes)
{
GraphCode graphCode = kvPass.Value;
var pixelPorts = currentRP.passInfos[kvPass.Key].pixelPorts;
bool readsNormal = (graphCode.requirements.requiresNormal & ~NeededCoordinateSpace.Tangent) != 0;
bool readsTangent = (graphCode.requirements.requiresTangent & ~NeededCoordinateSpace.Tangent) != 0 ||
(graphCode.requirements.requiresBitangent & ~NeededCoordinateSpace.Tangent) != 0 ||
(graphCode.requirements.requiresViewDir & NeededCoordinateSpace.Tangent) != 0;
bool hasNormalPort = pixelPorts.Any(t => t == ShaderGraphVfxAsset.NormalSlotId) && shaderGraph.HasOutput(ShaderGraphVfxAsset.NormalSlotId);
if (readsNormal || readsTangent || hasNormalPort) // needs normal
yield return $"SHADERGRAPH_NEEDS_NORMAL_{kvPass.Key.ToUpper(CultureInfo.InvariantCulture)}";
if (readsTangent || hasNormalPort) // needs tangent
yield return $"SHADERGRAPH_NEEDS_TANGENT_{kvPass.Key.ToUpper(CultureInfo.InvariantCulture)}";
needsPosWS |= graphCode.requirements.requiresPosition != NeededCoordinateSpace.None ||
graphCode.requirements.requiresScreenPosition ||
graphCode.requirements.requiresViewDir != NeededCoordinateSpace.None;
}
// TODO Put that per pass ?
if (needsPosWS)
yield return "VFX_NEEDS_POSWS_INTERPOLATOR";
}
}
}
protected virtual RPInfo currentRP
{
get { return hdrpInfo; }
}
public override VFXExpressionMapper GetExpressionMapper(VFXDeviceTarget target)
{
var mapper = base.GetExpressionMapper(target);
switch (target)
{
case VFXDeviceTarget.CPU:
break;
case VFXDeviceTarget.GPU:
var shaderGraph = GetOrRefreshShaderGraphObject();
if (shaderGraph != null)
{
foreach (var tex in shaderGraph.textureInfos)
{
switch (tex.dimension)
{
default:
case TextureDimension.Tex2D:
mapper.AddExpression(new VFXTexture2DValue(tex.instanceID, VFXValue.Mode.Variable), tex.name, -1);
break;
case TextureDimension.Tex3D:
mapper.AddExpression(new VFXTexture3DValue(tex.instanceID, VFXValue.Mode.Variable), tex.name, -1);
break;
case TextureDimension.Cube:
mapper.AddExpression(new VFXTextureCubeValue(tex.instanceID, VFXValue.Mode.Variable), tex.name, -1);
break;
case TextureDimension.Tex2DArray:
mapper.AddExpression(new VFXTexture2DArrayValue(tex.instanceID, VFXValue.Mode.Variable), tex.name, -1);
break;
case TextureDimension.CubeArray:
mapper.AddExpression(new VFXTextureCubeArrayValue(tex.instanceID, VFXValue.Mode.Variable), tex.name, -1);
break;
}
}
}
break;
}
return mapper;
}
static bool IsTexture(PropertyType type)
{
switch (type)
{
case PropertyType.Texture2D:
case PropertyType.Texture2DArray:
case PropertyType.Texture3D:
case PropertyType.Cubemap:
return true;
default:
return false;
}
}
public override IEnumerable<string> fragmentParameters
{
get
{
var shaderGraph = GetOrRefreshShaderGraphObject();
if (shaderGraph != null)
foreach (var param in shaderGraph.properties)
if (!IsTexture(param.propertyType)) // Remove exposed textures from list of interpolants
yield return param.referenceName;
}
}
public virtual bool isLitShader { get => false; }
Dictionary<string, GraphCode> graphCodes;
public override bool SetupCompilation()
{
if (!base.SetupCompilation()) return false;
var shaderGraph = GetOrRefreshShaderGraphObject();
if (shaderGraph != null)
{
if (!isLitShader && shaderGraph.lit)
{
Debug.LogError("You must use an unlit vfx master node with an unlit output");
return false;
}
if (isLitShader && !shaderGraph.lit)
{
Debug.LogError("You must use a lit vfx master node with a lit output");
return false;
}
graphCodes = currentRP.passInfos.ToDictionary(t => t.Key, t => shaderGraph.GetCode(t.Value.pixelPorts.Select(u => shaderGraph.GetOutput(u)).Where(u => !string.IsNullOrEmpty(u.referenceName)).ToArray()));
}
return true;
}
public override void EndCompilation()
{
if (graphCodes != null)
graphCodes.Clear();
}
public override IEnumerable<KeyValuePair<string, VFXShaderWriter>> additionalReplacements
{
get
{
foreach (var rep in base.additionalReplacements)
yield return rep;
var shaderGraph = GetOrRefreshShaderGraphObject();
if (shaderGraph != null)
{
RPInfo info = currentRP;
foreach (var port in info.allPorts)
{
var portInfo = shaderGraph.GetOutput(port);
if (!string.IsNullOrEmpty(portInfo.referenceName))
yield return new KeyValuePair<string, VFXShaderWriter>($"${{SHADERGRAPH_PARAM_{portInfo.referenceName.ToUpper(CultureInfo.InvariantCulture)}}}", new VFXShaderWriter($"{portInfo.referenceName}_{portInfo.id}"));
}
foreach (var kvPass in graphCodes)
{
GraphCode graphCode = kvPass.Value;
var preProcess = new VFXShaderWriter();
if (graphCode.requirements.requiresCameraOpaqueTexture)
preProcess.WriteLine("#define REQUIRE_OPAQUE_TEXTURE");
if (graphCode.requirements.requiresDepthTexture)
preProcess.WriteLine("#define REQUIRE_DEPTH_TEXTURE");
preProcess.WriteLine("${VFXShaderGraphFunctionsInclude}\n");
yield return new KeyValuePair<string, VFXShaderWriter>("${SHADERGRAPH_PIXEL_CODE_" + kvPass.Key.ToUpper(CultureInfo.InvariantCulture) + "}", new VFXShaderWriter(preProcess.ToString() + graphCode.code));
var callSG = new VFXShaderWriter("//Call Shader Graph\n");
callSG.builder.AppendLine($"{shaderGraph.inputStructName} INSG = ({shaderGraph.inputStructName})0;");
if (graphCode.requirements.requiresNormal != NeededCoordinateSpace.None)
{
callSG.builder.AppendLine("float3 WorldSpaceNormal = normalize(normalWS.xyz);");
if ((graphCode.requirements.requiresNormal & NeededCoordinateSpace.World) != 0)
callSG.builder.AppendLine("INSG.WorldSpaceNormal = WorldSpaceNormal;");
if ((graphCode.requirements.requiresNormal & NeededCoordinateSpace.Object) != 0)
callSG.builder.AppendLine("INSG.ObjectSpaceNormal = mul(WorldSpaceNormal, (float3x3)UNITY_MATRIX_M);");
if ((graphCode.requirements.requiresNormal & NeededCoordinateSpace.View) != 0)
callSG.builder.AppendLine("INSG.ViewSpaceNormal = mul(WorldSpaceNormal, (float3x3)UNITY_MATRIX_I_V);");
if ((graphCode.requirements.requiresNormal & NeededCoordinateSpace.Tangent) != 0)
callSG.builder.AppendLine("INSG.TangentSpaceNormal = float3(0.0f, 0.0f, 1.0f);");
}
if (graphCode.requirements.requiresTangent != NeededCoordinateSpace.None)
{
callSG.builder.AppendLine("float3 WorldSpaceTangent = normalize(tangentWS.xyz);");
if ((graphCode.requirements.requiresTangent & NeededCoordinateSpace.World) != 0)
callSG.builder.AppendLine("INSG.WorldSpaceTangent = WorldSpaceTangent;");
if ((graphCode.requirements.requiresTangent & NeededCoordinateSpace.Object) != 0)
callSG.builder.AppendLine("INSG.ObjectSpaceTangent = TransformWorldToObjectDir(WorldSpaceTangent);");
if ((graphCode.requirements.requiresTangent & NeededCoordinateSpace.View) != 0)
callSG.builder.AppendLine("INSG.ViewSpaceTangent = TransformWorldToViewDir(WorldSpaceTangent);");
if ((graphCode.requirements.requiresTangent & NeededCoordinateSpace.Tangent) != 0)
callSG.builder.AppendLine("INSG.TangentSpaceTangent = float3(1.0f, 0.0f, 0.0f);");
}
if (graphCode.requirements.requiresBitangent != NeededCoordinateSpace.None)
{
callSG.builder.AppendLine("float3 WorldSpaceBiTangent = normalize(bitangentWS.xyz);");
if ((graphCode.requirements.requiresBitangent & NeededCoordinateSpace.World) != 0)
callSG.builder.AppendLine("INSG.WorldSpaceBiTangent = WorldSpaceBiTangent;");
if ((graphCode.requirements.requiresBitangent & NeededCoordinateSpace.Object) != 0)
callSG.builder.AppendLine("INSG.ObjectSpaceBiTangent = TransformWorldToObjectDir(WorldSpaceBiTangent);");
if ((graphCode.requirements.requiresBitangent & NeededCoordinateSpace.View) != 0)
callSG.builder.AppendLine("INSG.ViewSpaceBiTangent = TransformWorldToViewDir(WorldSpaceBiTangent);");
if ((graphCode.requirements.requiresBitangent & NeededCoordinateSpace.Tangent) != 0)
callSG.builder.AppendLine("INSG.TangentSpaceBiTangent = float3(0.0f, 1.0f, 0.0f);");
}
if (graphCode.requirements.requiresPosition != NeededCoordinateSpace.None || graphCode.requirements.requiresScreenPosition || graphCode.requirements.requiresViewDir != NeededCoordinateSpace.None)
{
callSG.builder.AppendLine("float3 posRelativeWS = VFXGetPositionRWS(i.VFX_VARYING_POSWS);");
callSG.builder.AppendLine("float3 posAbsoluteWS = VFXGetPositionAWS(i.VFX_VARYING_POSWS);");
if ((graphCode.requirements.requiresPosition & NeededCoordinateSpace.World) != 0)
callSG.builder.AppendLine("INSG.WorldSpacePosition = posRelativeWS;");
if ((graphCode.requirements.requiresPosition & NeededCoordinateSpace.Object) != 0)
callSG.builder.AppendLine("INSG.ObjectSpacePosition = TransformWorldToObject(posRelativeWS);");
if ((graphCode.requirements.requiresPosition & NeededCoordinateSpace.View) != 0)
callSG.builder.AppendLine("INSG.ViewSpacePosition = VFXTransformPositionWorldToView(posRelativeWS);");
if ((graphCode.requirements.requiresPosition & NeededCoordinateSpace.Tangent) != 0)
callSG.builder.AppendLine("INSG.TangentSpacePosition = float3(0.0f, 0.0f, 0.0f);");
if ((graphCode.requirements.requiresPosition & NeededCoordinateSpace.AbsoluteWorld) != 0)
callSG.builder.AppendLine("INSG.AbsoluteWorldSpacePosition = posAbsoluteWS;");
if (graphCode.requirements.requiresScreenPosition)
callSG.builder.AppendLine("INSG.ScreenPosition = ComputeScreenPos(VFXTransformPositionWorldToClip(i.VFX_VARYING_POSWS), _ProjectionParams.x);");
if (graphCode.requirements.requiresViewDir != NeededCoordinateSpace.None)
{
callSG.builder.AppendLine("float3 V = GetWorldSpaceNormalizeViewDir(VFXGetPositionRWS(i.VFX_VARYING_POSWS));");
if ((graphCode.requirements.requiresViewDir & NeededCoordinateSpace.World) != 0)
callSG.builder.AppendLine("INSG.WorldSpaceViewDirection = V;");
if ((graphCode.requirements.requiresViewDir & NeededCoordinateSpace.Object) != 0)
callSG.builder.AppendLine("INSG.ObjectSpaceViewDirection = TransformWorldToObjectDir(V);");
if ((graphCode.requirements.requiresViewDir & NeededCoordinateSpace.View) != 0)
callSG.builder.AppendLine("INSG.ViewSpaceViewDirection = TransformWorldToViewDir(V);");
if ((graphCode.requirements.requiresViewDir & NeededCoordinateSpace.Tangent) != 0)
callSG.builder.AppendLine("INSG.TangentSpaceViewDirection = mul(tbn, V);");
}
}
if (graphCode.requirements.requiresMeshUVs.Contains(UVChannel.UV0))
{
callSG.builder.AppendLine("INSG.uv0.xy = i.uv;");
}
if (graphCode.requirements.requiresTime)
{
callSG.builder.AppendLine("INSG.TimeParameters = _TimeParameters.xyz;");
}
if (taskType == VFXTaskType.ParticleMeshOutput)
{
for (UVChannel uv = UVChannel.UV1; uv <= UVChannel.UV3; ++uv)
{
if (graphCode.requirements.requiresMeshUVs.Contains(uv))
{
int uvi = (int)uv;
yield return new KeyValuePair<string, VFXShaderWriter>($"VFX_SHADERGRAPH_HAS_UV{uvi}", new VFXShaderWriter("1")); // TODO put that in additionalDefines
callSG.builder.AppendLine($"INSG.uv{uvi} = i.uv{uvi};");
}
}
if (graphCode.requirements.requiresVertexColor)
{
yield return new KeyValuePair<string, VFXShaderWriter>($"VFX_SHADERGRAPH_HAS_COLOR", new VFXShaderWriter("1")); // TODO put that in additionalDefines
callSG.builder.AppendLine($"INSG.VertexColor = i.vertexColor;");
}
}
callSG.builder.Append($"\n{shaderGraph.outputStructName} OUTSG = {shaderGraph.evaluationFunctionName}(INSG");
if (graphCode.properties.Any())
callSG.builder.Append("," + graphCode.properties.Select(t => t.GetHLSLVariableName(true)).Aggregate((s, t) => s + ", " + t));
callSG.builder.AppendLine(");");
var pixelPorts = currentRP.passInfos[kvPass.Key].pixelPorts;
if (pixelPorts.Any(t => t == ShaderGraphVfxAsset.AlphaThresholdSlotId) && shaderGraph.alphaClipping)
{
callSG.builder.AppendLine(
@"#if (USE_ALPHA_TEST || VFX_FEATURE_MOTION_VECTORS_FORWARD) && defined(VFX_VARYING_ALPHATHRESHOLD)
i.VFX_VARYING_ALPHATHRESHOLD = OUTSG.AlphaClipThreshold_7;
#endif");
}
yield return new KeyValuePair<string, VFXShaderWriter>("${SHADERGRAPH_PIXEL_CALL_" + kvPass.Key.ToUpper(CultureInfo.InvariantCulture) + "}", callSG);
}
}
}
}
}
}