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

390 lines
19 KiB
C#

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