Added transparent depth post pass; Added transparent depth write; Added transparent back then front render; Added transparent motion vector write;
270 lines
15 KiB
C#
270 lines
15 KiB
C#
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);
|
|
}
|
|
}
|
|
} |