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); 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); if (material.HasProperty(kAddPrecomputedVelocity)) { CoreUtils.SetKeyword(material, "_ADD_PRECOMPUTED_VELOCITY", material.GetInt(kAddPrecomputedVelocity) != 0); } 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; DoubleSidedGIMode doubleSidedGIMode = DoubleSidedGIMode.Auto; if (material.HasProperty(kDoubleSidedGIMode)) { doubleSidedGIMode = (DoubleSidedGIMode)material.GetFloat(kDoubleSidedGIMode); } // 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. #if UNITY_EDITOR MaterialEditor.FixupEmissiveFlag(material); #endif } 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); // conservative depth offset for ShaderGraphs if (material.HasProperty(kConservativeDepthOffsetEnable)) { // Depth offset is only enabled if per pixel displacement is bool conservativeDepthOffset = (material.GetFloat(kConservativeDepthOffsetEnable) > 0.0f); CoreUtils.SetKeyword(material, "_CONSERVATIVE_DEPTH_OFFSET", conservativeDepthOffset); } } if (material.HasProperty(kTessellationMode)) { TessellationMode tessMode = (TessellationMode)material.GetFloat(kTessellationMode); CoreUtils.SetKeyword(material, "_TESSELLATION_PHONG", tessMode == TessellationMode.Phong); } // DoubleSidedGI has to be synced with our double sided toggle if (doubleSidedGIMode == DoubleSidedGIMode.Auto) { material.doubleSidedGI = doubleSidedEnable; } else if (doubleSidedGIMode == DoubleSidedGIMode.On) { material.doubleSidedGI = true; } else if (doubleSidedGIMode == DoubleSidedGIMode.Off) { material.doubleSidedGI = false; } }
public static void AddDoubleSidedProperty(PropertyCollector collector, DoubleSidedMode mode = DoubleSidedMode.Enabled, DoubleSidedGIMode giMode = DoubleSidedGIMode.Auto) { var normalMode = ConvertDoubleSidedModeToDoubleSidedNormalMode(mode); collector.AddToggleProperty("_DoubleSidedEnable", mode != DoubleSidedMode.Disabled); collector.AddShaderProperty(new Vector1ShaderProperty { enumNames = { "Flip", "Mirror", "None" }, // values will be 0, 1 and 2 floatType = FloatType.Enum, overrideReferenceName = "_DoubleSidedNormalMode", hidden = true, overrideHLSLDeclaration = true, hlslDeclarationOverride = HLSLDeclaration.DoNotDeclare, value = (int)normalMode }); collector.AddShaderProperty(new Vector4ShaderProperty { overrideReferenceName = "_DoubleSidedConstants", hidden = true, overrideHLSLDeclaration = true, hlslDeclarationOverride = HLSLDeclaration.UnityPerMaterial, value = new Vector4(1, 1, -1, 0) }); collector.AddShaderProperty(new Vector1ShaderProperty { enumNames = { "Auto", "On", "Off" }, // values will be 0, 1 and 2 floatType = FloatType.Enum, overrideReferenceName = "_DoubleSidedGIMode", hidden = true, overrideHLSLDeclaration = true, hlslDeclarationOverride = HLSLDeclaration.DoNotDeclare, value = (int)giMode }); }