using UnityEngine; using UnityEngine.Rendering; using UnityEditor.Rendering.HighDefinition; // Include material common properties names using static UnityEngine.Rendering.HighDefinition.HDMaterialProperties; namespace UnityEngine.Rendering.HighDefinition { abstract class BaseLitAPI { // Wind protected const string kWindEnabled = "_EnableWind"; public static DisplacementMode GetFilteredDisplacementMode(Material material) { return material.GetFilteredDisplacementMode((DisplacementMode)material.GetFloat(kDisplacementMode)); } // All Setup Keyword functions must be static. It allow to create script to automatically update the shaders with a script if code change static public void SetupBaseLitKeywords(Material material) { material.SetupBaseUnlitKeywords(); bool doubleSidedEnable = material.HasProperty(kDoubleSidedEnable) ? material.GetFloat(kDoubleSidedEnable) > 0.0f : false; if (doubleSidedEnable) { DoubleSidedNormalMode doubleSidedNormalMode = (DoubleSidedNormalMode)material.GetFloat(kDoubleSidedNormalMode); switch (doubleSidedNormalMode) { case DoubleSidedNormalMode.Mirror: // Mirror mode (in tangent space) material.SetVector("_DoubleSidedConstants", new Vector4(1.0f, 1.0f, -1.0f, 0.0f)); break; case DoubleSidedNormalMode.Flip: // Flip mode (in tangent space) material.SetVector("_DoubleSidedConstants", new Vector4(-1.0f, -1.0f, -1.0f, 0.0f)); break; case DoubleSidedNormalMode.None: // None mode (in tangent space) material.SetVector("_DoubleSidedConstants", new Vector4(1.0f, 1.0f, 1.0f, 0.0f)); break; } } // Displacement Mapping { bool enableVertexDisplacement = false; bool enablePixelDisplacement = false; bool enableTessellationDisplacement = false; bool displacementLockObjectScale = false; bool displacementLockTilingScale = false; bool enableDepthOffset = false; bool conservativeDepthOffset = false; bool enableDisplacement = material.HasProperty(kDisplacementMode) && (GetFilteredDisplacementMode(material) != DisplacementMode.None); if (enableDisplacement) { var displacementMode = GetFilteredDisplacementMode(material); enableVertexDisplacement = displacementMode == DisplacementMode.Vertex; enablePixelDisplacement = displacementMode == DisplacementMode.Pixel; enableTessellationDisplacement = displacementMode == DisplacementMode.Tessellation; displacementLockObjectScale = material.GetFloat(kDisplacementLockObjectScale) > 0.0f; displacementLockTilingScale = material.GetFloat(kDisplacementLockTilingScale) > 0.0f; } // Depth Offset may be used without Displacement Mapping as well (a Shader Graph feature). if (enablePixelDisplacement || (!material.HasProperty(kDisplacementMode) && material.HasProperty(kDepthOffsetEnable))) { enableDepthOffset = material.GetFloat(kDepthOffsetEnable) > 0.0f; } if (enableDepthOffset && material.HasProperty(kConservativeDepthOffsetEnable)) { conservativeDepthOffset = material.GetFloat(kConservativeDepthOffsetEnable) > 0.0f; } CoreUtils.SetKeyword(material, "_VERTEX_DISPLACEMENT", enableVertexDisplacement); CoreUtils.SetKeyword(material, "_PIXEL_DISPLACEMENT", enablePixelDisplacement); CoreUtils.SetKeyword(material, "_TESSELLATION_DISPLACEMENT", enableTessellationDisplacement); // Tessellation reuse vertex flag. CoreUtils.SetKeyword(material, "_VERTEX_DISPLACEMENT_LOCK_OBJECT_SCALE", displacementLockObjectScale && (enableVertexDisplacement || enableTessellationDisplacement)); CoreUtils.SetKeyword(material, "_PIXEL_DISPLACEMENT_LOCK_OBJECT_SCALE", displacementLockObjectScale && enablePixelDisplacement); CoreUtils.SetKeyword(material, "_DISPLACEMENT_LOCK_TILING_SCALE", displacementLockTilingScale && enableDisplacement); CoreUtils.SetKeyword(material, "_DEPTHOFFSET_ON", enableDepthOffset); CoreUtils.SetKeyword(material, "_CONSERVATIVE_DEPTH_OFFSET", conservativeDepthOffset); } CoreUtils.SetKeyword(material, "_VERTEX_WIND", false); material.SetupMainTexForAlphaTestGI("_BaseColorMap", "_BaseColor"); // Use negation so we don't create keyword by default CoreUtils.SetKeyword(material, "_DISABLE_DECALS", material.HasProperty(kSupportDecals) && material.GetFloat(kSupportDecals) == 0.0f); CoreUtils.SetKeyword(material, "_DISABLE_SSR", material.HasProperty(kReceivesSSR) && material.GetFloat(kReceivesSSR) == 0.0f); CoreUtils.SetKeyword(material, "_DISABLE_SSR_TRANSPARENT", material.HasProperty(kReceivesSSRTransparent) && material.GetFloat(kReceivesSSRTransparent) == 0.0f); CoreUtils.SetKeyword(material, "_ENABLE_GEOMETRIC_SPECULAR_AA", material.HasProperty(kEnableGeometricSpecularAA) && material.GetFloat(kEnableGeometricSpecularAA) == 1.0f); if (material.HasProperty(kRefractionModel)) { var refractionModel = material.GetRefractionModel(); CoreUtils.SetKeyword(material, "_REFRACTION_PLANE", refractionModel == ScreenSpaceRefraction.RefractionModel.Planar); CoreUtils.SetKeyword(material, "_REFRACTION_SPHERE", refractionModel == ScreenSpaceRefraction.RefractionModel.Sphere); CoreUtils.SetKeyword(material, "_REFRACTION_THIN", refractionModel == ScreenSpaceRefraction.RefractionModel.Thin); } } static public bool CompatibleWithExcludeFromTUAndAA(SurfaceType surfaceType, int renderQueue) { return surfaceType == SurfaceType.Transparent && HDRenderQueue.k_RenderQueue_Transparent.Contains(renderQueue); } static public bool CompatibleWithExcludeFromTUAndAA(Material material) { return CompatibleWithExcludeFromTUAndAA(material.GetSurfaceType(), material.renderQueue) && material.HasProperty(kExcludeFromTUAndAA); } // WW1MOD: added composition mask on stencil user bit 0 static public void SetupStencil(Material material, bool receivesLighting, bool receivesSSR, bool useSplitLighting, bool excludeFromTUAndAA = false, bool compositionMask = false) { // To determine if the shader is forward only, we can't rely on the presence of GBuffer pass because that depends on the active subshader, which // depends on the active render pipeline, giving an inconsistent result. The properties of a shader are always the same so it's ok to check them bool forwardOnly = material.shader.FindPropertyIndex(kZTestGBuffer) == -1; bool hasRefraction = material.GetRefractionModel() != ScreenSpaceRefraction.RefractionModel.None; ComputeStencilProperties( receivesLighting, forwardOnly, receivesSSR, useSplitLighting, hasRefraction, excludeFromTUAndAA, compositionMask, // WW1MOD out int stencilRef, out int stencilWriteMask, out int stencilRefDepth, out int stencilWriteMaskDepth, out int stencilRefGBuffer, out int stencilWriteMaskGBuffer, out int stencilRefMV, out int stencilWriteMaskMV); // As we tag both during motion vector pass and Gbuffer pass we need a separate state and we need to use the write mask if (material.HasProperty(kStencilRef)) { material.SetInt(kStencilRef, stencilRef); material.SetInt(kStencilWriteMask, stencilWriteMask); } if (material.HasProperty(kStencilRefDepth)) { material.SetInt(kStencilRefDepth, stencilRefDepth); material.SetInt(kStencilWriteMaskDepth, stencilWriteMaskDepth); } if (material.HasProperty(kStencilRefGBuffer)) { material.SetInt(kStencilRefGBuffer, stencilRefGBuffer); material.SetInt(kStencilWriteMaskGBuffer, stencilWriteMaskGBuffer); } if (material.HasProperty(kStencilRefDistortionVec)) { material.SetInt(kStencilRefDistortionVec, (int)StencilUsage.DistortionVectors); material.SetInt(kStencilWriteMaskDistortionVec, (int)StencilUsage.DistortionVectors); } if (material.HasProperty(kStencilRefMV)) { material.SetInt(kStencilRefMV, stencilRefMV); material.SetInt(kStencilWriteMaskMV, stencilWriteMaskMV); } } static public void ComputeStencilProperties( bool receivesLighting, bool forwardOnly, bool receivesSSR, bool useSplitLighting, bool hasRefraction, bool excludeFromTUAndAA, bool compositionMask, // WW1MOD 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; } // WW1MOD: added composition mask on stencil user bit 0 if (compositionMask) { stencilRefDepth |= (int)StencilUsage.UserBit0; stencilRef |= (int)StencilUsage.UserBit0; stencilWriteMask |= (int)StencilUsage.UserBit0; stencilWriteMaskDepth |= (int)StencilUsage.UserBit0; } stencilWriteMaskDepth |= (int)StencilUsage.IsUnlit; stencilWriteMaskGBuffer |= (int)StencilUsage.IsUnlit; stencilWriteMaskMV |= (int)StencilUsage.IsUnlit; } static public void SetupBaseLitMaterialPass(Material material) { material.SetupBaseUnlitPass(); } static public void SetupDisplacement(Material material, int layerCount = 1) { DisplacementMode displacementMode = GetFilteredDisplacementMode(material); for (int i = 0; i < layerCount; i++) { var heightAmplitude = layerCount > 1 ? kHeightAmplitude + i : kHeightAmplitude; var heightCenter = layerCount > 1 ? kHeightCenter + i : kHeightCenter; if (material.HasProperty(heightAmplitude) && material.HasProperty(heightCenter)) { var heightPoMAmplitude = layerCount > 1 ? kHeightPoMAmplitude + i : kHeightPoMAmplitude; var heightParametrization = layerCount > 1 ? kHeightParametrization + i : kHeightParametrization; var heightTessAmplitude = layerCount > 1 ? kHeightTessAmplitude + i : kHeightTessAmplitude; var heightTessCenter = layerCount > 1 ? kHeightTessCenter + i : kHeightTessCenter; var heightOffset = layerCount > 1 ? kHeightOffset + i : kHeightOffset; var heightMin = layerCount > 1 ? kHeightMin + i : kHeightMin; var heightMax = layerCount > 1 ? kHeightMax + i : kHeightMax; if (displacementMode == DisplacementMode.Pixel) { material.SetFloat(heightAmplitude, material.GetFloat(heightPoMAmplitude) * 0.01f); // Convert centimeters to meters. material.SetFloat(heightCenter, 1.0f); // PoM is always inward so base (0 height) is mapped to 1 in the texture } else { var parametrization = (HeightmapParametrization)material.GetFloat(heightParametrization); if (parametrization == HeightmapParametrization.MinMax) { float offset = material.GetFloat(heightOffset); float minHeight = material.GetFloat(heightMin); float amplitude = material.GetFloat(heightMax) - minHeight; material.SetFloat(heightAmplitude, amplitude * 0.01f); // Convert centimeters to meters. material.SetFloat(heightCenter, -(minHeight + offset) / Mathf.Max(1e-6f, amplitude)); } else { float offset = material.GetFloat(heightOffset); float center = material.GetFloat(heightTessCenter); float amplitude = material.GetFloat(heightTessAmplitude); material.SetFloat(heightAmplitude, amplitude * 0.01f); // Convert centimeters to meters. material.SetFloat(heightCenter, -offset / Mathf.Max(1e-6f, amplitude) + center); } } } } } } } // namespace UnityEditor