// SPDX-FileCopyrightText: 2023 Unity Technologies and the glTFast authors // SPDX-License-Identifier: Apache-2.0 #if ! ( USING_URP || USING_HDRP || (UNITY_SHADER_GRAPH_12_OR_NEWER && GLTFAST_BUILTIN_SHADER_GRAPH) ) #define GLTFAST_BUILTIN_RP #endif #if GLTFAST_BUILTIN_RP || UNITY_EDITOR using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif using Material = UnityEngine.Material; namespace GLTFast.Materials { using Logging; using AlphaMode = Schema.MaterialBase.AlphaMode; /// /// Converts glTF materials to Unity materials for the Built-in Render Pipeline /// public class BuiltInMaterialGenerator : MaterialGenerator { // Built-in Render Pipeline const string k_AlphaBlendOnKeyword = "_ALPHABLEND_ON"; const string k_AlphaPremultiplyOnKeyword = "_ALPHAPREMULTIPLY_ON"; const string k_EmissionKeyword = "_EMISSION"; const string k_MetallicRoughnessMapKeyword = "_METALLICGLOSSMAP"; const string k_OcclusionKeyword = "_OCCLUSION"; const string k_SpecGlossMapKeyword = "_SPECGLOSSMAP"; #if UNITY_EDITOR const string k_ShaderPathPrefix = "Packages/" + GltfGlobals.GltfPackageName + "/Runtime/Shader/Built-In/"; const string k_PbrMetallicRoughnessShaderPath = "glTFPbrMetallicRoughness.shader"; const string k_PbrSpecularGlossinessShaderPath = "glTFPbrSpecularGlossiness.shader"; const string k_UnlitShaderPath = "glTFUnlit.shader"; #else const string k_PbrMetallicRoughnessShaderName = "glTF/PbrMetallicRoughness"; const string k_PbrSpecularGlossinessShaderName = "glTF/PbrSpecularGlossiness"; const string k_UnlitShaderName = "glTF/Unlit"; #endif Shader m_PbrMetallicRoughnessShader; Shader m_PbrSpecularGlossinessShader; Shader m_UnlitShader; static bool s_DefaultMaterialGenerated; static Material s_DefaultMaterial; /// protected override Material GenerateDefaultMaterial(bool pointsSupport = false) { if (pointsSupport) { Logger?.Warning(LogCode.TopologyPointsMaterialUnsupported); } if (!s_DefaultMaterialGenerated) { s_DefaultMaterial = GetPbrMetallicRoughnessMaterial(); if (s_DefaultMaterial != null) { s_DefaultMaterial.name = DefaultMaterialName; } s_DefaultMaterialGenerated = true; // Material works on lines as well // TODO: Create dedicated point cloud material } return s_DefaultMaterial; } /// /// Finds the shader required for metallic/roughness based materials. /// /// Metallic/Roughness shader // Needs to be non-static outside of the Editor. // ReSharper disable once MemberCanBeMadeStatic.Local Shader FinderShaderMetallicRoughness() { #if UNITY_EDITOR return AssetDatabase.LoadAssetAtPath($"{k_ShaderPathPrefix}{k_PbrMetallicRoughnessShaderPath}"); #else return FindShader(k_PbrMetallicRoughnessShaderName, Logger); #endif } /// /// Finds the shader required for specular/glossiness based materials. /// /// Specular/Glossiness shader // Needs to be non-static outside of the Editor. // ReSharper disable once MemberCanBeMadeStatic.Local Shader FinderShaderSpecularGlossiness() { #if UNITY_EDITOR return AssetDatabase.LoadAssetAtPath($"{k_ShaderPathPrefix}{k_PbrSpecularGlossinessShaderPath}"); #else return FindShader(k_PbrSpecularGlossinessShaderName, Logger); #endif } /// /// Finds the shader required for unlit materials. /// /// Unlit shader // Needs to be non-static outside of the Editor. // ReSharper disable once MemberCanBeMadeStatic.Local Shader FinderShaderUnlit() { #if UNITY_EDITOR return AssetDatabase.LoadAssetAtPath($"{k_ShaderPathPrefix}{k_UnlitShaderPath}"); #else return FindShader(k_UnlitShaderName, Logger); #endif } Material GetPbrMetallicRoughnessMaterial(bool doubleSided = false) { if (m_PbrMetallicRoughnessShader == null) { m_PbrMetallicRoughnessShader = FinderShaderMetallicRoughness(); } if (m_PbrMetallicRoughnessShader == null) { return null; } var mat = new Material(m_PbrMetallicRoughnessShader); if (doubleSided) { // Turn off back-face culling mat.SetFloat(MaterialProperty.CullMode, 0); #if UNITY_EDITOR mat.doubleSidedGI = true; #endif } return mat; } Material GetPbrSpecularGlossinessMaterial(bool doubleSided = false) { if (m_PbrSpecularGlossinessShader == null) { m_PbrSpecularGlossinessShader = FinderShaderSpecularGlossiness(); } if (m_PbrSpecularGlossinessShader == null) { return null; } var mat = new Material(m_PbrSpecularGlossinessShader); if (doubleSided) { // Turn off back-face culling mat.SetFloat(MaterialProperty.CullMode, 0); #if UNITY_EDITOR mat.doubleSidedGI = true; #endif } return mat; } Material GetUnlitMaterial(bool doubleSided = false) { if (m_UnlitShader == null) { m_UnlitShader = FinderShaderUnlit(); } if (m_UnlitShader == null) { return null; } var mat = new Material(m_UnlitShader); if (doubleSided) { // Turn off back-face culling mat.SetFloat(MaterialProperty.CullMode, 0); #if UNITY_EDITOR mat.doubleSidedGI = true; #endif } return mat; } /// public override Material GenerateMaterial( Schema.MaterialBase gltfMaterial, IGltfReadable gltf, bool pointsSupport = false ) { Material material; var isUnlit = gltfMaterial.Extensions?.KHR_materials_unlit != null; if (gltfMaterial.Extensions?.KHR_materials_pbrSpecularGlossiness != null) { material = GetPbrSpecularGlossinessMaterial(gltfMaterial.doubleSided); } else if (isUnlit) { material = GetUnlitMaterial(gltfMaterial.doubleSided); } else { material = GetPbrMetallicRoughnessMaterial(gltfMaterial.doubleSided); } if (material == null) return null; if (!isUnlit && pointsSupport) { Logger?.Warning(LogCode.TopologyPointsMaterialUnsupported); } material.name = gltfMaterial.name; StandardShaderMode shaderMode = StandardShaderMode.Opaque; Color baseColorLinear = Color.white; if (gltfMaterial.GetAlphaMode() == AlphaMode.Mask) { material.SetFloat(MaterialProperty.AlphaCutoff, gltfMaterial.alphaCutoff); shaderMode = StandardShaderMode.Cutout; } else if (gltfMaterial.GetAlphaMode() == AlphaMode.Blend) { SetAlphaModeBlend(material); shaderMode = StandardShaderMode.Fade; } if (gltfMaterial.Extensions != null) { // Specular glossiness Schema.PbrSpecularGlossiness specGloss = gltfMaterial.Extensions.KHR_materials_pbrSpecularGlossiness; if (specGloss != null) { baseColorLinear = specGloss.DiffuseColor; material.SetVector(MaterialProperty.SpecularFactor, specGloss.SpecularColor); material.SetFloat(MaterialProperty.GlossinessFactor, specGloss.glossinessFactor); TrySetTexture( specGloss.diffuseTexture, material, gltf, MaterialProperty.BaseColorTexture, MaterialProperty.BaseColorTextureScaleTransform, MaterialProperty.BaseColorTextureRotation, MaterialProperty.BaseColorTextureTexCoord ); if (TrySetTexture( specGloss.specularGlossinessTexture, material, gltf, MaterialProperty.SpecularGlossinessTexture, MaterialProperty.SpecularGlossinessTextureScaleTransform, MaterialProperty.SpecularGlossinessTextureRotation, MaterialProperty.SpecularGlossinessTextureTexCoord )) { material.EnableKeyword(k_SpecGlossMapKeyword); } } } if (gltfMaterial.PbrMetallicRoughness != null // If there's a specular-glossiness extension, ignore metallic-roughness // (according to extension specification) && gltfMaterial.Extensions?.KHR_materials_pbrSpecularGlossiness == null) { baseColorLinear = gltfMaterial.PbrMetallicRoughness.BaseColor; material.SetFloat(MaterialProperty.Metallic, gltfMaterial.PbrMetallicRoughness.metallicFactor); material.SetFloat(MaterialProperty.RoughnessFactor, gltfMaterial.PbrMetallicRoughness.roughnessFactor); TrySetTexture( gltfMaterial.PbrMetallicRoughness.BaseColorTexture, material, gltf, MaterialProperty.BaseColorTexture, MaterialProperty.BaseColorTextureScaleTransform, MaterialProperty.BaseColorTextureRotation, MaterialProperty.BaseColorTextureTexCoord ); if (TrySetTexture( gltfMaterial.PbrMetallicRoughness.MetallicRoughnessTexture, material, gltf, MaterialProperty.MetallicRoughnessMap, MaterialProperty.MetallicRoughnessMapScaleTransform, MaterialProperty.MetallicRoughnessMapRotation, MaterialProperty.MetallicRoughnessMapTexCoord )) { material.EnableKeyword(k_MetallicRoughnessMapKeyword); } } if (TrySetTexture( gltfMaterial.NormalTexture, material, gltf, MaterialProperty.NormalTexture, MaterialProperty.NormalTextureScaleTransform, MaterialProperty.NormalTextureRotation, MaterialProperty.NormalTextureTexCoord )) { material.EnableKeyword(Constants.NormalMapKeyword); material.SetFloat(MaterialProperty.NormalTextureScale, gltfMaterial.NormalTexture.scale); } if (TrySetTexture( gltfMaterial.OcclusionTexture, material, gltf, MaterialProperty.OcclusionTexture, MaterialProperty.OcclusionTextureScaleTransform, MaterialProperty.OcclusionTextureRotation, MaterialProperty.OcclusionTextureTexCoord )) { material.EnableKeyword(k_OcclusionKeyword); material.SetFloat(MaterialProperty.OcclusionTextureStrength, gltfMaterial.OcclusionTexture.strength); } if (TrySetTexture( gltfMaterial.EmissiveTexture, material, gltf, MaterialProperty.EmissiveTexture, MaterialProperty.EmissiveTextureScaleTransform, MaterialProperty.EmissiveTextureRotation, MaterialProperty.EmissiveTextureTexCoord )) { material.EnableKeyword(k_EmissionKeyword); } if (gltfMaterial.Extensions != null) { // Transmission - Approximation var transmission = gltfMaterial.Extensions.KHR_materials_transmission; if (transmission != null) { #if UNITY_EDITOR Logger?.Warning(LogCode.MaterialTransmissionApprox); #endif // Correct transmission is not supported in Built-In renderer // This is an approximation for some corner cases if (transmission.transmissionFactor > 0f && (transmission.transmissionTexture == null || transmission.transmissionTexture.index < 0) ) { var premultiply = TransmissionWorkaroundShaderMode(transmission, ref baseColorLinear); shaderMode = premultiply ? StandardShaderMode.Transparent : StandardShaderMode.Fade; } } } switch (shaderMode) { case StandardShaderMode.Cutout: SetAlphaModeMask(material, gltfMaterial); break; case StandardShaderMode.Fade: SetAlphaModeBlend(material); break; case StandardShaderMode.Transparent: SetAlphaModeTransparent(material); break; default: SetOpaqueMode(material); break; } material.SetVector(MaterialProperty.BaseColor, baseColorLinear.gamma); if (gltfMaterial.Emissive != Color.black) { material.SetColor(MaterialProperty.EmissiveFactor, gltfMaterial.Emissive.gamma); material.EnableKeyword(k_EmissionKeyword); } return material; } /// /// Configures material for alpha masking. /// /// Target material /// Threshold value for alpha masking public static void SetAlphaModeMask(Material material, float alphaCutoff) { material.EnableKeyword(AlphaTestOnKeyword); material.SetInt(MaterialProperty.ZWrite, 1); material.DisableKeyword(k_AlphaPremultiplyOnKeyword); material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest; //2450 material.SetFloat(MaterialProperty.AlphaCutoff, alphaCutoff); material.SetFloat(MaterialProperty.Mode, (int)StandardShaderMode.Cutout); material.SetOverrideTag(RenderTypeTag, TransparentCutoutRenderType); material.SetInt(MaterialProperty.SrcBlend, (int)UnityEngine.Rendering.BlendMode.One); material.SetInt(MaterialProperty.DstBlend, (int)UnityEngine.Rendering.BlendMode.Zero); material.DisableKeyword(k_AlphaBlendOnKeyword); } /// /// Configures material for alpha masking. /// /// Target material /// Source material static void SetAlphaModeMask(Material material, Schema.MaterialBase gltfMaterial) { SetAlphaModeMask(material, gltfMaterial.alphaCutoff); } /// /// Configures material for alpha blending. /// /// Target material public static void SetAlphaModeBlend(Material material) { material.SetFloat(MaterialProperty.Mode, (int)StandardShaderMode.Fade); material.SetOverrideTag(RenderTypeTag, FadeRenderType); material.EnableKeyword(k_AlphaBlendOnKeyword); material.SetInt(MaterialProperty.SrcBlend, (int)UnityEngine.Rendering.BlendMode.SrcAlpha);//5 material.SetInt(MaterialProperty.DstBlend, (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);//10 material.SetInt(MaterialProperty.ZWrite, 0); material.DisableKeyword(k_AlphaPremultiplyOnKeyword); material.DisableKeyword(AlphaTestOnKeyword); material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent; //3000 } /// /// Configures material for transparency. /// /// Target material public static void SetAlphaModeTransparent(Material material) { material.SetFloat(MaterialProperty.Mode, (int)StandardShaderMode.Fade); material.SetOverrideTag(RenderTypeTag, TransparentRenderType); material.EnableKeyword(k_AlphaPremultiplyOnKeyword); material.SetInt(MaterialProperty.SrcBlend, (int)UnityEngine.Rendering.BlendMode.One);//1 material.SetInt(MaterialProperty.DstBlend, (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);//10 material.SetInt(MaterialProperty.ZWrite, 0); material.DisableKeyword(k_AlphaBlendOnKeyword); material.DisableKeyword(AlphaTestOnKeyword); material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent; //3000 } /// /// Configures material to be opaque. /// /// Target material public static void SetOpaqueMode(Material material) { material.SetOverrideTag(RenderTypeTag, OpaqueRenderType); material.DisableKeyword(k_AlphaBlendOnKeyword); material.renderQueue = -1; material.SetInt(MaterialProperty.SrcBlend, (int)UnityEngine.Rendering.BlendMode.One); material.SetInt(MaterialProperty.DstBlend, (int)UnityEngine.Rendering.BlendMode.Zero); material.SetInt(MaterialProperty.ZWrite, 1); material.DisableKeyword(AlphaTestOnKeyword); material.DisableKeyword(k_AlphaPremultiplyOnKeyword); } } } #endif // GLTFAST_BUILTIN_RP || UNITY_EDITOR