using System.Collections.Generic; using UnityEditor.Graphing; using UnityEditor.ShaderGraph; using UnityEngine.Experimental.Rendering; using UnityEngine.Experimental.Rendering.HDPipeline; namespace UnityEditor.Experimental.Rendering.HDPipeline { public class HDUnlitSubShader : IUnlitSubShader { Pass m_PassDepthOnly = new Pass() { Name = "Depth prepass", LightMode = "DepthForwardOnly", TemplateName = "HDUnlitPassForward.template", ShaderPassName = "SHADERPASS_DEPTH_ONLY", ZWriteOverride = "ZWrite On", Includes = new List() { "#include \"HDRP/ShaderPass/ShaderPassDepthOnly.hlsl\"", }, PixelShaderSlots = new List() { UnlitMasterNode.AlphaSlotId, UnlitMasterNode.AlphaThresholdSlotId }, VertexShaderSlots = new List() { PBRMasterNode.PositionSlotId } }; Pass m_PassForward = new Pass() { Name = "Forward Unlit", LightMode = "ForwardOnly", TemplateName = "HDUnlitPassForward.template", ShaderPassName = "SHADERPASS_FORWARD_UNLIT", ExtraDefines = new List() { "#pragma multi_compile _ DEBUG_DISPLAY" }, Includes = new List() { "#include \"HDRP/ShaderPass/ShaderPassForwardUnlit.hlsl\"", }, PixelShaderSlots = new List() { UnlitMasterNode.ColorSlotId, UnlitMasterNode.AlphaSlotId, UnlitMasterNode.AlphaThresholdSlotId }, VertexShaderSlots = new List() { PBRMasterNode.PositionSlotId } }; Pass m_PassMETA = new Pass() { Name = "META", LightMode = "Meta", TemplateName = "HDUnlitPassForward.template", ShaderPassName = "SHADERPASS_LIGHT_TRANSPORT", CullOverride = "Cull Off", Includes = new List() { "#include \"HDRP/ShaderPass/ShaderPassLightTransport.hlsl\"", }, RequiredFields = new List() { "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() { UnlitMasterNode.ColorSlotId, UnlitMasterNode.AlphaSlotId, UnlitMasterNode.AlphaThresholdSlotId }, VertexShaderSlots = new List() { //PBRMasterNode.PositionSlotId } }; Pass m_PassDistortion = new Pass() { 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() { "#include \"HDRP/ShaderPass/ShaderPassDistortion.hlsl\"", }, PixelShaderSlots = new List() { PBRMasterNode.AlphaSlotId, PBRMasterNode.AlphaThresholdSlotId }, VertexShaderSlots = new List() { PBRMasterNode.PositionSlotId }, }; private static HashSet GetActiveFieldsFromMasterNode(INode iMasterNode, Pass pass) { HashSet activeFields = new HashSet(); UnlitMasterNode masterNode = iMasterNode as UnlitMasterNode; if (masterNode == null) { return activeFields; } 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 } } float constantAlpha = 0.0f; if (masterNode.IsSlotConnected(PBRMasterNode.AlphaThresholdSlotId) || (float.TryParse(masterNode.GetSlotValue(PBRMasterNode.AlphaThresholdSlotId, GenerationMode.ForReals), out constantAlpha) && (constantAlpha > 0.0f))) { activeFields.Add("AlphaTest"); } // Keywords for transparent // #pragma shader_feature _SURFACE_TYPE_TRANSPARENT if (masterNode.surfaceType != SurfaceType.Opaque) { // transparent-only defines activeFields.Add("SurfaceType.Transparent"); // #pragma shader_feature _ _BLENDMODE_ALPHA _BLENDMODE_ADD _BLENDMODE_PRE_MULTIPLY if (masterNode.alphaMode == AlphaMode.Alpha) { activeFields.Add("BlendMode.Alpha"); } else if (masterNode.alphaMode == AlphaMode.Additive) { activeFields.Add("BlendMode.Add"); } // else if (masterNode.alphaMode == PBRMasterNode.AlphaMode.PremultiplyAlpha) // TODO // { // defines.AddShaderChunk("#define _BLENDMODE_PRE_MULTIPLY 1", true); // } } else { // opaque-only defines } // 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 activeFields; } private static bool GenerateShaderPassUnlit(AbstractMaterialNode masterNode, Pass pass, GenerationMode mode, SurfaceMaterialOptions materialOptions, ShaderGenerator result, List sourceAssetDependencyPaths) { // apply master node options to active fields HashSet activeFields = GetActiveFieldsFromMasterNode(masterNode, pass); // use standard shader pass generation return HDSubShaderUtilities.GenerateShaderPass(masterNode, pass, mode, materialOptions, activeFields, result, sourceAssetDependencyPaths); } public string GetSubshader(IMasterNode iMasterNode, GenerationMode mode, List sourceAssetDependencyPaths = null) { if (sourceAssetDependencyPaths != null) { // HDUnlitSubShader.cs sourceAssetDependencyPaths.Add(AssetDatabase.GUIDToAssetPath("292c6a3c80161fa4cb49a9d11d35cbe9")); // HDSubShaderUtilities.cs sourceAssetDependencyPaths.Add(AssetDatabase.GUIDToAssetPath("713ced4e6eef4a44799a4dd59041484b")); } var masterNode = iMasterNode 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); } // generate the necessary shader passes // bool opaque = (masterNode.surfaceType == SurfaceType.Opaque); // bool transparent = (masterNode.surfaceType != SurfaceType.Opaque); bool distortionActive = false; GenerateShaderPassUnlit(masterNode, m_PassDepthOnly, mode, materialOptions, subShader, sourceAssetDependencyPaths); GenerateShaderPassUnlit(masterNode, m_PassForward, mode, materialOptions, subShader, sourceAssetDependencyPaths); GenerateShaderPassUnlit(masterNode, m_PassMETA, mode, materialOptions, subShader, sourceAssetDependencyPaths); if (distortionActive) { GenerateShaderPassUnlit(masterNode, m_PassDistortion, mode, materialOptions, subShader, sourceAssetDependencyPaths); } } subShader.Deindent(); subShader.AddShaderChunk("}", true); return subShader.GetShaderString(0); } public bool IsPipelineCompatible(RenderPipelineAsset renderPipelineAsset) { return renderPipelineAsset is HDRenderPipelineAsset; } } }