using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; using static Misaki.HdrpToon.UTSPropertyName; namespace Misaki.HdrpToon { internal 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, } internal enum StencilUsage { Clear = 0, // Note: first bit is free and can still be used by both phases. // --- Following bits are used before transparent rendering --- IsUnlit = (1 << 0), // Unlit materials (shader and shader graph) except for the shadow matte RequiresDeferredLighting = (1 << 1), SubsurfaceScattering = (1 << 2), // SSS, Split Lighting TraceReflectionRay = (1 << 3), // SSR or RTR - slot is reuse in transparent Decals = (1 << 4), // Use to tag when an Opaque Decal is render into DBuffer ObjectMotionVector = (1 << 5), // Animated object (for motion blur, SSR, SSAO, TAA) // --- Stencil is cleared after opaque rendering has finished --- // --- Following bits are used exclusively for what happens after opaque --- WaterExclusion = (1 << 0), // Prevents water surface from being rendered. ExcludeFromTUAndAA = (1 << 1), // Disable Temporal Upscaling and Antialiasing for certain objects DistortionVectors = (1 << 2), // Distortion pass - reset after distortion pass, shared with SMAA SMAA = (1 << 2), // Subpixel Morphological Antialiasing // Reserved TraceReflectionRay = (1 << 3) for transparent SSR or RTR Refractive = (1 << 4), // Indicates there's a refractive object WaterSurface = (1 << 5), // Reserved for water surface usage (If update the value of 'STENCILUSAGE_WATER_SURFACE' in LensFlareCommon.hlsl) // --- Following are user bits, we don't touch them inside HDRP and is up to the user to handle them --- UserBit0 = (1 << 6), UserBit1 = (1 << 7), HDRPReservedBits = 255 & ~(UserBit0 | UserBit1), } internal class UTSAPI { public static void SetupPass(Material material) { var surfaceType = (SurfaceType)material.GetInteger(SurfaceOptions.SURFACE_TYPE); var isTransparent = surfaceType == SurfaceType.Transparent; 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); var depthWriteEnable = material.GetInteger(InternalProperties.TRANSPARENT_DEPTH_PREPASS_ENABLE) == 1; var ssrTransparent = material.GetInteger(SurfaceOptions.RECEIVES_SSR_TRANSPARENT) == 1; var backFaceEnable = material.GetInteger(SurfaceOptions.TRANSPARENT_BACKFACE_ENABLE) == 1; material.SetShaderPassEnabled(HDShaderPassNames.s_TransparentDepthPrepassStr, isTransparent && (depthWriteEnable || ssrTransparent)); material.SetShaderPassEnabled(HDShaderPassNames.s_TransparentDepthPostpassStr, material.GetInteger(InternalProperties.TRANSPARENT_DEPTH_POSTPASS_ENABLE) == 1 && isTransparent); material.SetShaderPassEnabled(HDShaderPassNames.s_TransparentBackfaceStr, backFaceEnable && isTransparent); } 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); CoreUtils.SetKeyword(material, "_DISABLE_SSR_TRANSPARENT", material.GetInteger(SurfaceOptions.RECEIVES_SSR_TRANSPARENT) == 0); } static public void ComputeStencilProperties(bool receivesLighting, bool forwardOnly, bool receivesSSR, bool useSplitLighting, bool hasRefraction, bool excludeFromTUAndAA, out int stencilRef, out int stencilWriteMask, out int stencilRefDepth, out int stencilWriteMaskDepth, out int stencilRefGBuffer, out int stencilWriteMaskGBuffer, out int stencilRefMV, out int stencilWriteMaskMV) { // Stencil usage rules: // TraceReflectionRay need to be tagged during depth prepass // RequiresDeferredLighting need to be tagged during GBuffer // SubsurfaceScattering need to be tagged during either GBuffer or Forward pass // ObjectMotionVectors need to be tagged in velocity pass. // As motion vectors pass can be use as a replacement of depth prepass it also need to have TraceReflectionRay // As GBuffer pass can have no depth prepass, it also need to have TraceReflectionRay // Object motion vectors is always render after a full depth buffer (if there is no depth prepass for GBuffer all object motion vectors are render after GBuffer) // so we have a guarantee than when we write object motion vectors no other object will be draw on top (and so would have require to overwrite motion vectors). // Final combination is: // Prepass: TraceReflectionRay // Motion vectors: TraceReflectionRay, ObjectVelocity // GBuffer: LightingMask, ObjectVelocity // Forward: LightingMask stencilRef = (int)StencilUsage.Clear; // Forward case stencilWriteMask = (int)StencilUsage.RequiresDeferredLighting | (int)StencilUsage.SubsurfaceScattering; stencilRefDepth = 0; stencilWriteMaskDepth = 0; stencilRefGBuffer = (int)StencilUsage.RequiresDeferredLighting; stencilWriteMaskGBuffer = (int)StencilUsage.RequiresDeferredLighting | (int)StencilUsage.SubsurfaceScattering; stencilRefMV = (int)StencilUsage.ObjectMotionVector; stencilWriteMaskMV = (int)StencilUsage.ObjectMotionVector; // ForwardOnly materials with motion vectors are rendered after GBuffer, so we need to clear the deferred bit in the stencil if (forwardOnly) stencilWriteMaskMV |= (int)StencilUsage.RequiresDeferredLighting; if (useSplitLighting) { stencilRefGBuffer |= (int)StencilUsage.SubsurfaceScattering; stencilRef |= (int)StencilUsage.SubsurfaceScattering; } if (receivesSSR) { stencilRefDepth |= (int)StencilUsage.TraceReflectionRay; stencilRefGBuffer |= (int)StencilUsage.TraceReflectionRay; stencilRefMV |= (int)StencilUsage.TraceReflectionRay; } stencilWriteMaskDepth |= (int)StencilUsage.TraceReflectionRay; stencilWriteMaskGBuffer |= (int)StencilUsage.TraceReflectionRay; stencilWriteMaskMV |= (int)StencilUsage.TraceReflectionRay; if (hasRefraction) { stencilRefDepth |= (int)StencilUsage.Refractive; stencilWriteMaskDepth |= (int)StencilUsage.Refractive; } if (!receivesLighting) { stencilRefDepth |= (int)StencilUsage.IsUnlit; stencilWriteMaskDepth |= (int)StencilUsage.IsUnlit; stencilRefMV |= (int)StencilUsage.IsUnlit; } if (excludeFromTUAndAA) { stencilRefDepth |= (int)StencilUsage.ExcludeFromTUAndAA; stencilRef |= (int)StencilUsage.ExcludeFromTUAndAA; stencilWriteMask |= (int)StencilUsage.ExcludeFromTUAndAA; stencilWriteMaskDepth |= (int)StencilUsage.ExcludeFromTUAndAA; } stencilWriteMaskDepth |= (int)StencilUsage.IsUnlit; stencilWriteMaskGBuffer |= (int)StencilUsage.IsUnlit; stencilWriteMaskMV |= (int)StencilUsage.IsUnlit; } 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, material.GetInteger(SurfaceOptions.TRANSPARENT_Z_WRITE)); 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.SetInteger(InternalProperties.CULL_MODE_FORWARD, (int)CullMode.Back); } else { material.SetInteger(InternalProperties.CULL_MODE_FORWARD, (int)(doubleSidedEnable ? CullMode.Off : cullMode)); } // Stencil ComputeStencilProperties(true, true, true, false, false, false, out var stencilRef, out var stencilWriteMask, out var stencilRefDepth, out var stencilWriteMaskDepth, out var stencilRefGBuffer, out var stencilWriteMaskGBuffer, out var stencilRefMV, out var stencilWriteMaskMV); material.SetInteger(InternalProperties.STENCIL_REF, stencilRef); material.SetInteger(InternalProperties.STENCIL_WRITE_MASK, stencilWriteMask); material.SetInteger(InternalProperties.STENCIL_REF_DEPTH, stencilRefDepth); material.SetInteger(InternalProperties.STENCIL_WRITE_MASK_DEPTH, stencilWriteMaskDepth); material.SetInteger(InternalProperties.STENCIL_REF_G_BUFFER, stencilRefGBuffer); material.SetInteger(InternalProperties.STENCIL_WRITE_MASK_G_BUFFER, stencilWriteMaskGBuffer); material.SetInteger(InternalProperties.STENCIL_REF_DISTORTION_VEC, (int)StencilUsage.DistortionVectors); material.SetInteger(InternalProperties.STENCIL_WRITE_MASK_DISTORTION_VEC, (int)StencilUsage.DistortionVectors); material.SetInteger(InternalProperties.STENCIL_REF_MV, stencilRefMV); material.SetInteger(InternalProperties.STENCIL_WRITE_MASK_MV, stencilWriteMaskMV); } } }