2021-09-09 20:42:29 -04:00

353 lines
21 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Rendering.HighDefinition;
using UnityEngine.Rendering;
using UnityEditor.ShaderGraph;
// Include material common properties names
using static UnityEngine.Rendering.HighDefinition.HDMaterialProperties;
namespace UnityEditor.Rendering.HighDefinition
{
// Extension class to setup material keywords on unlit materials
static class BaseUnlitGUI
{
public static void SetupBaseUnlitKeywords(this Material material)
{
// First thing, be sure to have an up to date RenderQueue
material.ResetMaterialCustomRenderQueue();
bool alphaTestEnable = material.HasProperty(kAlphaCutoffEnabled) && material.GetFloat(kAlphaCutoffEnabled) > 0.0f;
CoreUtils.SetKeyword(material, "_ALPHATEST_ON", alphaTestEnable);
// Setup alpha to mask using the _AlphaToMaskInspectorValue that we configure in the material UI
float alphaToMaskEnabled = material.HasProperty("_AlphaToMaskInspectorValue") && material.GetFloat("_AlphaToMaskInspectorValue") > 0.0 ? 1 : 0;
material.SetFloat(kAlphaToMask, alphaTestEnable ? alphaToMaskEnabled : 0);
bool alphaToMaskEnable = alphaTestEnable && material.HasProperty(kAlphaToMask) && material.GetFloat(kAlphaToMask) > 0.0f;
CoreUtils.SetKeyword(material, "_ALPHATOMASK_ON", alphaToMaskEnable);
SurfaceType surfaceType = material.GetSurfaceType();
CoreUtils.SetKeyword(material, "_SURFACE_TYPE_TRANSPARENT", surfaceType == SurfaceType.Transparent);
bool transparentWritesMotionVec = (surfaceType == SurfaceType.Transparent) && material.HasProperty(kTransparentWritingMotionVec) && material.GetInt(kTransparentWritingMotionVec) > 0;
CoreUtils.SetKeyword(material, "_TRANSPARENT_WRITES_MOTION_VEC", transparentWritesMotionVec);
HDRenderQueue.RenderQueueType renderQueueType = HDRenderQueue.GetTypeByRenderQueueValue(material.renderQueue);
bool needOffScreenBlendFactor = renderQueueType == HDRenderQueue.RenderQueueType.AfterPostprocessTransparent || renderQueueType == HDRenderQueue.RenderQueueType.LowTransparent;
// Alpha tested materials always have a prepass where we perform the clip.
// Then during Gbuffer pass we don't perform the clip test, so we need to use depth equal in this case.
if (alphaTestEnable)
{
material.SetInt(kZTestGBuffer, (int)UnityEngine.Rendering.CompareFunction.Equal);
}
else
{
material.SetInt(kZTestGBuffer, (int)UnityEngine.Rendering.CompareFunction.LessEqual);
}
// If the material use the kZTestDepthEqualForOpaque it mean it require depth equal test for opaque but transparent are not affected
if (material.HasProperty(kZTestDepthEqualForOpaque))
{
if (surfaceType == SurfaceType.Opaque)
{
// When the material is after post process, we need to use LEssEqual because there is no depth prepass for unlit opaque
if (HDRenderQueue.k_RenderQueue_AfterPostProcessOpaque.Contains(material.renderQueue))
material.SetInt(kZTestDepthEqualForOpaque, (int)UnityEngine.Rendering.CompareFunction.LessEqual);
else
material.SetInt(kZTestDepthEqualForOpaque, (int)UnityEngine.Rendering.CompareFunction.Equal);
}
else
material.SetInt(kZTestDepthEqualForOpaque, (int)material.GetTransparentZTest());
}
if (surfaceType == SurfaceType.Opaque)
{
material.SetOverrideTag("RenderType", alphaTestEnable ? "TransparentCutout" : "");
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.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.SetInt("_AlphaSrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_AlphaDstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
material.SetInt(kZWrite, 1);
}
else
{
material.SetOverrideTag("RenderType", "Transparent");
material.SetInt(kZWrite, material.GetTransparentZWrite() ? 1 : 0);
if (material.HasProperty(kBlendMode))
{
BlendMode blendMode = material.GetBlendMode();
// When doing off-screen transparency accumulation, we change blend factors as described here: https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch23.html
switch (blendMode)
{
// Alpha
// color: src * src_a + dst * (1 - src_a)
// src * src_a is done in the shader as it allow to reduce precision issue when using _BLENDMODE_PRESERVE_SPECULAR_LIGHTING (See Material.hlsl)
case BlendMode.Alpha:
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
if (needOffScreenBlendFactor)
{
material.SetInt("_AlphaSrcBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
material.SetInt("_AlphaDstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
}
else
{
material.SetInt("_AlphaSrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_AlphaDstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
}
break;
// Additive
// color: src * src_a + dst
// src * src_a is done in the shader
case BlendMode.Additive:
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.One);
if (needOffScreenBlendFactor)
{
material.SetInt("_AlphaSrcBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
material.SetInt("_AlphaDstBlend", (int)UnityEngine.Rendering.BlendMode.One);
}
else
{
material.SetInt("_AlphaSrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_AlphaDstBlend", (int)UnityEngine.Rendering.BlendMode.One);
}
break;
// PremultipliedAlpha
// color: src * src_a + dst * (1 - src_a)
// src is supposed to have been multiplied by alpha in the texture on artists side.
case BlendMode.Premultiply:
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
if (needOffScreenBlendFactor)
{
material.SetInt("_AlphaSrcBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
material.SetInt("_AlphaDstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
}
else
{
material.SetInt("_AlphaSrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_AlphaDstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
}
break;
}
}
}
bool fogEnabled = material.HasProperty(kEnableFogOnTransparent) && material.GetFloat(kEnableFogOnTransparent) > 0.0f && surfaceType == SurfaceType.Transparent;
CoreUtils.SetKeyword(material, "_ENABLE_FOG_ON_TRANSPARENT", fogEnabled);
if (material.HasProperty(kDistortionEnable) && material.HasProperty(kDistortionBlendMode))
{
bool distortionDepthTest = material.GetFloat(kDistortionDepthTest) > 0.0f;
if (material.HasProperty(kZTestModeDistortion))
{
if (distortionDepthTest)
{
material.SetInt(kZTestModeDistortion, (int)UnityEngine.Rendering.CompareFunction.LessEqual);
}
else
{
material.SetInt(kZTestModeDistortion, (int)UnityEngine.Rendering.CompareFunction.Always);
}
}
var distortionBlendMode = material.GetInt(kDistortionBlendMode);
switch (distortionBlendMode)
{
default:
case 0: // Add
material.SetInt("_DistortionSrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_DistortionDstBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_DistortionBlurSrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_DistortionBlurDstBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_DistortionBlurBlendOp", (int)UnityEngine.Rendering.BlendOp.Add);
break;
case 1: // Multiply
material.SetInt("_DistortionSrcBlend", (int)UnityEngine.Rendering.BlendMode.DstColor);
material.SetInt("_DistortionDstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
material.SetInt("_DistortionBlurSrcBlend", (int)UnityEngine.Rendering.BlendMode.DstAlpha);
material.SetInt("_DistortionBlurDstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
material.SetInt("_DistortionBlurBlendOp", (int)UnityEngine.Rendering.BlendOp.Add);
break;
case 2: // Replace
material.SetInt("_DistortionSrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_DistortionDstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
material.SetInt("_DistortionBlurSrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_DistortionBlurDstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
material.SetInt("_DistortionBlurBlendOp", (int)UnityEngine.Rendering.BlendOp.Add);
break;
}
}
CullMode doubleSidedOffMode = (surfaceType == SurfaceType.Transparent) ? material.GetTransparentCullMode() : material.GetOpaqueCullMode();
bool isBackFaceEnable = material.HasProperty(kTransparentBackfaceEnable) && material.GetFloat(kTransparentBackfaceEnable) > 0.0f && surfaceType == SurfaceType.Transparent;
bool doubleSidedEnable = material.HasProperty(kDoubleSidedEnable) && material.GetFloat(kDoubleSidedEnable) > 0.0f;
// Disable culling if double sided
material.SetInt("_CullMode", doubleSidedEnable ? (int)UnityEngine.Rendering.CullMode.Off : (int)doubleSidedOffMode);
// We have a separate cullmode (_CullModeForward) for Forward in case we use backface then frontface rendering, need to configure it
if (isBackFaceEnable)
{
material.SetInt("_CullModeForward", (int)UnityEngine.Rendering.CullMode.Back);
}
else
{
material.SetInt("_CullModeForward", (int)(doubleSidedEnable ? UnityEngine.Rendering.CullMode.Off : doubleSidedOffMode));
}
CoreUtils.SetKeyword(material, "_DOUBLESIDED_ON", doubleSidedEnable);
// A material's GI flag internally keeps track of whether emission is enabled at all, it's enabled but has no effect
// or is enabled and may be modified at runtime. This state depends on the values of the current flag and emissive color.
// The fixup routine makes sure that the material is in the correct state if/when changes are made to the mode or color.
if (material.HasProperty(kEmissionColor))
{
material.SetColor(kEmissionColor, Color.white); // kEmissionColor must always be white to allow our own material to control the GI (this allow to fallback from builtin unity to our system).
// as it happen with old material that it isn't the case, we force it.
MaterialEditor.FixupEmissiveFlag(material);
}
material.SetupMainTexForAlphaTestGI("_UnlitColorMap", "_UnlitColor");
// depth offset for ShaderGraphs (they don't have the displacement mode property)
if (!material.HasProperty(kDisplacementMode) && material.HasProperty(kDepthOffsetEnable))
{
// Depth offset is only enabled if per pixel displacement is
bool depthOffsetEnable = (material.GetFloat(kDepthOffsetEnable) > 0.0f);
CoreUtils.SetKeyword(material, "_DEPTHOFFSET_ON", depthOffsetEnable);
}
// DoubleSidedGI has to be synced with our double sided toggle
var serializedObject = new SerializedObject(material);
var doubleSidedGIppt = serializedObject.FindProperty("m_DoubleSidedGI");
doubleSidedGIppt.boolValue = doubleSidedEnable;
serializedObject.ApplyModifiedProperties();
}
// This is a hack for GI. PVR looks in the shader for a texture named "_MainTex" to extract the opacity of the material for baking. In the same manner, "_Cutoff" and "_Color" are also necessary.
// Since we don't have those parameters in our shaders we need to provide a "fake" useless version of them with the right values for the GI to work.
public static void SetupMainTexForAlphaTestGI(this Material material, string colorMapPropertyName, string colorPropertyName)
{
if (material.HasProperty(colorMapPropertyName))
{
var mainTex = material.GetTexture(colorMapPropertyName);
material.SetTexture("_MainTex", mainTex);
}
if (material.HasProperty(colorPropertyName))
{
var color = material.GetColor(colorPropertyName);
material.SetColor("_Color", color);
}
if (material.HasProperty("_AlphaCutoff")) // Same for all our materials
{
var cutoff = material.GetFloat("_AlphaCutoff");
material.SetFloat("_Cutoff", cutoff);
}
}
static public void SetupBaseUnlitPass(this Material material)
{
if (material.shader.IsShaderGraph())
{
// Shader graph generate distortion pass only if required. So we can safely enable it
// all the time here.
material.SetShaderPassEnabled(HDShaderPassNames.s_DistortionVectorsStr, true);
}
else if (material.HasProperty(kDistortionEnable))
{
bool distortionEnable = material.GetFloat(kDistortionEnable) > 0.0f && ((SurfaceType)material.GetFloat(kSurfaceType) == SurfaceType.Transparent);
bool distortionOnly = false;
if (material.HasProperty(kDistortionOnly))
{
distortionOnly = material.GetFloat(kDistortionOnly) > 0.0f;
}
// If distortion only is enabled, disable all passes (except distortion and debug)
bool enablePass = !(distortionEnable && distortionOnly);
// Disable all passes except distortion
// Distortion is setup in code above
material.SetShaderPassEnabled(HDShaderPassNames.s_ForwardStr, enablePass);
material.SetShaderPassEnabled(HDShaderPassNames.s_DepthOnlyStr, enablePass);
material.SetShaderPassEnabled(HDShaderPassNames.s_DepthForwardOnlyStr, enablePass);
material.SetShaderPassEnabled(HDShaderPassNames.s_ForwardOnlyStr, enablePass);
material.SetShaderPassEnabled(HDShaderPassNames.s_GBufferStr, enablePass);
material.SetShaderPassEnabled(HDShaderPassNames.s_GBufferWithPrepassStr, enablePass);
material.SetShaderPassEnabled(HDShaderPassNames.s_DistortionVectorsStr, distortionEnable); // note: use distortionEnable
material.SetShaderPassEnabled(HDShaderPassNames.s_TransparentDepthPrepassStr, enablePass);
material.SetShaderPassEnabled(HDShaderPassNames.s_TransparentBackfaceStr, enablePass);
material.SetShaderPassEnabled(HDShaderPassNames.s_TransparentDepthPostpassStr, enablePass);
material.SetShaderPassEnabled(HDShaderPassNames.s_RayTracingPrepassStr, enablePass);
material.SetShaderPassEnabled(HDShaderPassNames.s_MetaStr, enablePass);
material.SetShaderPassEnabled(HDShaderPassNames.s_ShadowCasterStr, enablePass);
}
if (material.HasProperty(kTransparentDepthPrepassEnable))
{
bool depthWriteEnable = (material.GetFloat(kTransparentDepthPrepassEnable) > 0.0f) && ((SurfaceType)material.GetFloat(kSurfaceType) == SurfaceType.Transparent);
bool ssrTransparent = material.HasProperty(kReceivesSSRTransparent) ? (material.GetFloat(kReceivesSSRTransparent) > 0.0f) && ((SurfaceType)material.GetFloat(kSurfaceType) == SurfaceType.Transparent) : false;
material.SetShaderPassEnabled(HDShaderPassNames.s_TransparentDepthPrepassStr, depthWriteEnable || ssrTransparent);
}
if (material.HasProperty(kTransparentDepthPostpassEnable))
{
bool depthWriteEnable = (material.GetFloat(kTransparentDepthPostpassEnable) > 0.0f) && ((SurfaceType)material.GetFloat(kSurfaceType) == SurfaceType.Transparent);
material.SetShaderPassEnabled(HDShaderPassNames.s_TransparentDepthPostpassStr, depthWriteEnable);
}
if (material.HasProperty(kTransparentBackfaceEnable))
{
bool backFaceEnable = (material.GetFloat(kTransparentBackfaceEnable) > 0.0f) && ((SurfaceType)material.GetFloat(kSurfaceType) == SurfaceType.Transparent);
material.SetShaderPassEnabled(HDShaderPassNames.s_TransparentBackfaceStr, backFaceEnable);
}
if (material.HasProperty(kRayTracing))
{
bool rayTracingEnable = (material.GetFloat(kRayTracing) > 0.0f);
material.SetShaderPassEnabled(HDShaderPassNames.s_RayTracingPrepassStr, rayTracingEnable);
}
// Shader graphs materials have their own management of motion vector pass in the material inspector
// (see DrawMotionVectorToggle())
if (!material.shader.IsShaderGraph())
{
//In the case of additional velocity data we will enable the motion vector pass.
bool addPrecomputedVelocity = false;
if (material.HasProperty(kAddPrecomputedVelocity))
{
addPrecomputedVelocity = material.GetInt(kAddPrecomputedVelocity) != 0;
}
// We don't have any vertex animation for lit/unlit vector, so we
// setup motion vector pass to false. Remind that in HDRP this
// doesn't disable motion vector, it just mean that the material
// don't do any vertex deformation but we can still have
// skinning / morph target
material.SetShaderPassEnabled(HDShaderPassNames.s_MotionVectorsStr, addPrecomputedVelocity);
}
}
}
}