using UnityEngine; using UnityEngine.Rendering; using static Misaki.HdrpToon.UTSPropertyName; namespace Misaki.HdrpToon { internal class UTSAPI { private enum RenderPriority { Opaque = RenderQueue.Geometry, OpaqueDecal = RenderQueue.Geometry + 225, // Opaque Decal mean Opaque that can receive decal OpaqueAlphaTest = RenderQueue.AlphaTest, OpaqueDecalAlphaTest = RenderQueue.AlphaTest + 25, // Warning: we must not change Geometry last value to stay compatible with occlusion OpaqueLast = RenderQueue.GeometryLast, AfterPostprocessOpaque = RenderQueue.GeometryLast + 1, AfterPostprocessOpaqueAlphaTest = RenderQueue.GeometryLast + 10, TransparentFirst = RenderQueue.Transparent - 100, Transparent = RenderQueue.Transparent, TransparentLast = RenderQueue.Transparent + 100, } public static void SetupPass(Material material) { material.SetShaderPassEnabled(UTSPassName.HAIR_SHADOW_CASTER_PASS_NAME, (MaterialType)material.GetInteger(SurfaceOptions.MATERIAL_TYPE) == MaterialType.FrontHair); material.SetShaderPassEnabled(UTSPassName.HAIR_BLENDING_TARGET_PASS_NAME, material.GetInteger(SurfaceOptions.HAIR_BLENDING_TARGET) == 1); } public static void SetupKeywords(Material material) { var surfaceType = (SurfaceType)material.GetInteger(SurfaceOptions.SURFACE_TYPE); var cullMode = (CullMode)material.GetInteger(SurfaceOptions.CULL_MODE); var doubleSidedEnable = cullMode == CullMode.Off; CoreUtils.SetKeyword(material, "_SURFACE_TYPE_TRANSPARENT", surfaceType == SurfaceType.Transparent); CoreUtils.SetKeyword(material, "_DOUBLESIDED_ON", doubleSidedEnable); } public static void SetupProperties(Material material) { var surfaceType = (SurfaceType)material.GetInteger(SurfaceOptions.SURFACE_TYPE); var cullMode = (CullMode)material.GetInteger(SurfaceOptions.CULL_MODE); // Surface type var isTransparent = surfaceType == SurfaceType.Transparent; var isAlphaClip = material.GetInteger(SurfaceOptions.ALPHA_CLIP_ENABLE) == 1; var renderQueue = isTransparent ? RenderPriority.Transparent : (isAlphaClip ? RenderPriority.OpaqueAlphaTest : RenderPriority.Opaque); material.SetInteger(InternalProperties.SURFACE_TYPE, isTransparent ? 1 : 0); material.renderQueue = (int)renderQueue; // We only do clip for alpha clip in depth pre-pass once. Z test for GBuffer and forward pass need to set to equal to make sure alpha clip works correctly. if (isAlphaClip) { material.SetInteger(InternalProperties.ZTEST_GBUFFER, (int)CompareFunction.Equal); } else { material.SetInteger(InternalProperties.ZTEST_GBUFFER, (int)CompareFunction.LessEqual); } if (isTransparent) { material.SetInteger(InternalProperties.ZTEST_DEPTH_EQUAL_FOR_OPAQUE, material.GetInteger(InternalProperties.ZTEST_TRANSPARENT)); material.SetOverrideTag("RenderType", "Transparent"); // TODO: Z write for transparent. material.SetInteger(InternalProperties.ZWRITE, 0); material.SetInteger(InternalProperties.DEST_BLEND2, (int)BlendMode.OneMinusSrcAlpha); //TODO: Implement additive and pre-multiply mode. material.SetInteger(InternalProperties.SRC_BLEND, (int)BlendMode.One); material.SetInteger(InternalProperties.DEST_BLEND, (int)BlendMode.OneMinusSrcAlpha); material.SetInteger(InternalProperties.ALPHA_SRC_BLEND, (int)BlendMode.One); material.SetInteger(InternalProperties.ALPHA_DEST_BLEND, (int)BlendMode.OneMinusSrcAlpha); } else { material.SetInteger(InternalProperties.ZTEST_DEPTH_EQUAL_FOR_OPAQUE, (int)CompareFunction.Equal); material.SetOverrideTag("RenderType", isAlphaClip ? "TransparentCutout" : ""); material.SetInteger(InternalProperties.SRC_BLEND, (int)BlendMode.One); material.SetInteger(InternalProperties.DEST_BLEND, (int)BlendMode.Zero); material.SetInteger(InternalProperties.DEST_BLEND2, (int)BlendMode.Zero); // Caution: we need to setup One for src and Zero for Dst for all element as users could switch from transparent to Opaque and keep remaining value. // Unity will disable Blending based on these default value. // Note that for after postprocess we setup 0 in opacity inside the shaders, so we correctly end with 0 in opacity for the compositing pass material.SetInteger(InternalProperties.ALPHA_SRC_BLEND, (int)BlendMode.One); material.SetInteger(InternalProperties.ALPHA_DEST_BLEND, (int)BlendMode.Zero); material.SetInteger(InternalProperties.ZWRITE, 1); } // Double sided var isBackFaceEnable = material.GetInteger(InternalProperties.TRANSPARENT_BACKFACE_ENABLE) == 1 && surfaceType == SurfaceType.Transparent; var doubleSidedEnable = cullMode == CullMode.Off; if (doubleSidedEnable) { var doubleSidedNormalMode = (DoubleSidedMode)material.GetInteger(SurfaceOptions.DOUBLE_SIDED_NORMAL_MODE); switch (doubleSidedNormalMode) { case DoubleSidedMode.None: material.SetVector(InternalProperties.DOUBLE_SIDED_CONSTANTS, new Vector4(1.0f, 1.0f, 1.0f, 0.0f)); break; case DoubleSidedMode.Mirror: material.SetVector(InternalProperties.DOUBLE_SIDED_CONSTANTS, new Vector4(1.0f, 1.0f, -1.0f, 0.0f)); break; case DoubleSidedMode.Flip: material.SetVector(InternalProperties.DOUBLE_SIDED_CONSTANTS, new Vector4(-1.0f, -1.0f, -1.0f, 0.0f)); break; } } if (isBackFaceEnable) { material.SetInt(InternalProperties.CULL_MODE_FORWARD, (int)CullMode.Back); } else { material.SetInteger(InternalProperties.CULL_MODE_FORWARD, (int)(doubleSidedEnable ? CullMode.Off : cullMode)); } // TODO: Setup stencil value. Currently it produce noticeable jittering when surface type is transparent. // This is caused by taa, _StencilWriteMask need to include StencilUsage.ExcludeFromTUAndAA when surface type is transparent. } } }