Files
2025-11-30 08:35:03 +02:00

282 lines
14 KiB
C#

// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
using System;
using UnityEngine;
namespace UnityEditor
{
internal class PbrShaderGUI : ShaderGUI
{
private enum WorkflowMode
{
SpecularGlossiness,
MetallicRoughness,
Unlit
}
public enum BlendMode
{
Opaque,
Mask,
Blend
}
private static class Styles
{
public static GUIContent albedoText = new GUIContent("Base Color", "Albedo (RGB) and Transparency (A)");
public static GUIContent vertexColorText = new GUIContent("Apply Vertex Colors", "Process vertex colors");
public static GUIContent alphaCutoffText = new GUIContent("Alpha Cutoff", "Threshold for alpha cutoff");
public static GUIContent specularMapText = new GUIContent("Spec Gloss", "Specular (RGB) and Glossiness (A)");
public static GUIContent metallicMapText = new GUIContent("Metal Rough", "Metallic (B) and Roughness (G)");
public static GUIContent metallicText = new GUIContent("Metallic", "Metallic value");
public static GUIContent metallicScaleText = new GUIContent("Metallic", "Metallic scale factor");
public static GUIContent roughnessText = new GUIContent("Roughness", "Roughness value");
public static GUIContent roughnessScaleText = new GUIContent("Roughness", "Roughness scale factor");
public static GUIContent glossinessText = new GUIContent("Glossiness", "Glossiness value");
public static GUIContent glossinessScaleText = new GUIContent("Glossiness", "Glossiness scale factor");
public static GUIContent normalMapText = new GUIContent("Normal Map", "Normal Map");
public static GUIContent occlusionText = new GUIContent("Occlusion", "Occlusion (R)");
public static GUIContent emissionText = new GUIContent("Emissive", "Emissive (RGB)");
public static string primaryMapsText = "Main Maps";
public static string renderingMode = "Rendering Mode";
public static GUIContent emissiveWarning = new GUIContent("Emissive value is animated but the material has not been configured to support emissive. Please make sure the material itself has some amount of emissive.");
public static readonly string[] blendNames = Enum.GetNames(typeof(BlendMode));
}
MaterialProperty blendMode = null;
MaterialProperty albedoMap = null;
MaterialProperty albedoColor = null;
MaterialProperty vertexColors = null;
MaterialProperty alphaCutoff = null;
MaterialProperty specularMap = null;
MaterialProperty specularColor = null;
MaterialProperty metallicMap = null;
MaterialProperty metallic = null;
MaterialProperty smoothness = null;
MaterialProperty bumpScale = null;
MaterialProperty bumpMap = null;
MaterialProperty occlusionStrength = null;
MaterialProperty occlusionMap = null;
MaterialProperty emissionColor = null;
MaterialProperty emissionMap = null;
MaterialEditor m_MaterialEditor;
WorkflowMode m_WorkflowMode = WorkflowMode.MetallicRoughness;
bool m_FirstTimeApply = true;
public void FindProperties(MaterialProperty[] props)
{
blendMode = FindProperty("_Mode", props);
albedoMap = FindProperty("_MainTex", props);
albedoColor = FindProperty("_Color", props);
vertexColors = FindProperty("_VertexColors", props);
alphaCutoff = FindProperty("_Cutoff", props);
specularMap = FindProperty("_SpecGlossMap", props, false);
specularColor = FindProperty("_SpecColor", props, false);
metallicMap = FindProperty("_MetallicGlossMap", props, false);
metallic = FindProperty("_Metallic", props, false);
if (specularMap != null && specularColor != null)
m_WorkflowMode = WorkflowMode.SpecularGlossiness;
else if (metallicMap != null && metallic != null)
m_WorkflowMode = WorkflowMode.MetallicRoughness;
else
m_WorkflowMode = WorkflowMode.Unlit;
smoothness = FindProperty("_Glossiness", props);
bumpScale = FindProperty("_BumpScale", props);
bumpMap = FindProperty("_BumpMap", props);
occlusionStrength = FindProperty("_OcclusionStrength", props);
occlusionMap = FindProperty("_OcclusionMap", props);
emissionColor = FindProperty("_EmissionColor", props);
emissionMap = FindProperty("_EmissionMap", props);
}
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props)
{
FindProperties(props); // MaterialProperties can be animated so we do not cache them but fetch them every event to ensure animated values are updated correctly
m_MaterialEditor = materialEditor;
Material material = materialEditor.target as Material;
// Make sure that needed setup (ie keywords/renderqueue) are set up if we're switching some existing
// material to a standard shader.
// Do this before any GUI code has been issued to prevent layout issues in subsequent GUILayout statements (case 780071)
if (m_FirstTimeApply)
{
MaterialChanged(material, m_WorkflowMode);
m_FirstTimeApply = false;
}
ShaderPropertiesGUI(material);
}
public void ShaderPropertiesGUI(Material material)
{
// Use default labelWidth
EditorGUIUtility.labelWidth = 0f;
// Detect any changes to the material
EditorGUI.BeginChangeCheck();
{
BlendModePopup();
// Primary properties
GUILayout.Label(Styles.primaryMapsText, EditorStyles.boldLabel);
DoAlbedoArea(material);
DoSpecularMetallicArea();
m_MaterialEditor.TexturePropertySingleLine(Styles.normalMapText, bumpMap, bumpMap.textureValue != null ? bumpScale : null);
m_MaterialEditor.TexturePropertySingleLine(Styles.occlusionText, occlusionMap, occlusionMap.textureValue != null ? occlusionStrength : null);
DoEmissionArea(material);
EditorGUILayout.Space();
}
if (EditorGUI.EndChangeCheck())
{
foreach (var obj in blendMode.targets)
MaterialChanged((Material)obj, m_WorkflowMode);
}
}
internal void DetermineWorkflow(MaterialProperty[] props)
{
if (FindProperty("_SpecGlossMap", props, false) != null && FindProperty("_SpecColor", props, false) != null)
m_WorkflowMode = WorkflowMode.SpecularGlossiness;
else if (FindProperty("_MetallicGlossMap", props, false) != null && FindProperty("_Metallic", props, false) != null)
m_WorkflowMode = WorkflowMode.MetallicRoughness;
else
m_WorkflowMode = WorkflowMode.Unlit;
}
void BlendModePopup()
{
EditorGUI.showMixedValue = blendMode.hasMixedValue;
var mode = (BlendMode)blendMode.floatValue;
EditorGUI.BeginChangeCheck();
mode = (BlendMode)EditorGUILayout.Popup(Styles.renderingMode, (int)mode, Styles.blendNames);
if (EditorGUI.EndChangeCheck())
{
m_MaterialEditor.RegisterPropertyChangeUndo("Rendering Mode");
blendMode.floatValue = (float)mode;
}
EditorGUI.showMixedValue = false;
}
void DoAlbedoArea(Material material)
{
m_MaterialEditor.TexturePropertySingleLine(Styles.albedoText, albedoMap, albedoColor);
m_MaterialEditor.ShaderProperty(vertexColors, Styles.vertexColorText, MaterialEditor.kMiniTextureFieldLabelIndentLevel + 1);
if (((BlendMode)material.GetFloat("_Mode") == BlendMode.Mask))
{
m_MaterialEditor.ShaderProperty(alphaCutoff, Styles.alphaCutoffText.text, MaterialEditor.kMiniTextureFieldLabelIndentLevel + 1);
}
}
void DoEmissionArea(Material material)
{
bool hadEmissionTexture = emissionMap.textureValue != null;
// Texture and HDR color controls
m_MaterialEditor.TexturePropertySingleLine(Styles.emissionText, emissionMap, emissionColor);
// If texture was assigned and color was black set color to white
float brightness = emissionColor.colorValue.maxColorComponent;
if (emissionMap.textureValue != null && !hadEmissionTexture && brightness <= 0f)
emissionColor.colorValue = Color.white;
}
void DoSpecularMetallicArea()
{
bool hasGlossMap = false;
if (m_WorkflowMode == WorkflowMode.SpecularGlossiness)
{
hasGlossMap = specularMap.textureValue != null;
m_MaterialEditor.TexturePropertySingleLine(Styles.specularMapText, specularMap, hasGlossMap ? null : specularColor);
m_MaterialEditor.ShaderProperty(smoothness, hasGlossMap ? Styles.glossinessScaleText : Styles.glossinessText, 2);
}
else if (m_WorkflowMode == WorkflowMode.MetallicRoughness)
{
hasGlossMap = metallicMap.textureValue != null;
m_MaterialEditor.TexturePropertySingleLine(Styles.metallicMapText, metallicMap);
m_MaterialEditor.ShaderProperty(metallic, hasGlossMap ? Styles.metallicScaleText : Styles.metallicText, 2);
m_MaterialEditor.ShaderProperty(smoothness, hasGlossMap ? Styles.roughnessScaleText : Styles.roughnessText, 2);
}
}
public static void SetupMaterialWithBlendMode(Material material, BlendMode blendMode, float alphaCutoff)
{
switch (blendMode)
{
case BlendMode.Opaque:
material.SetOverrideTag("RenderType", "Opaque");
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
material.SetInt("_ZWrite", 1);
material.DisableKeyword("_ALPHATEST_ON");
material.DisableKeyword("_ALPHABLEND_ON");
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Geometry;
break;
case BlendMode.Mask:
material.SetOverrideTag("RenderType", "TransparentCutout");
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
material.SetInt("_ZWrite", 1);
material.EnableKeyword("_ALPHATEST_ON");
material.DisableKeyword("_ALPHABLEND_ON");
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest;
material.SetFloat("_Cutoff", alphaCutoff);
material.SetFloat("_AlphaToMask", 1f);
break;
case BlendMode.Blend:
material.SetOverrideTag("RenderType", "Transparent");
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
material.SetInt("_ZWrite", 0);
material.DisableKeyword("_ALPHATEST_ON");
material.EnableKeyword("_ALPHABLEND_ON");
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
material.SetFloat("_BlendModePreserveSpecular", 0);
material.SetFloat("_AlphaToMask", 0);
material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
break;
}
material.SetFloat("_Mode", (float)blendMode);
}
static void SetMaterialKeywords(Material material, WorkflowMode workflowMode)
{
// Note: keywords must be based on Material value not on MaterialProperty due to multi-edit & material animation
// (MaterialProperty value might come from renderer material property block)
SetKeyword(material, "_NORMALMAP", material.GetTexture("_BumpMap"));
if (workflowMode == WorkflowMode.SpecularGlossiness)
SetKeyword(material, "_SPECGLOSSMAP", material.GetTexture("_SpecGlossMap"));
else if (workflowMode == WorkflowMode.MetallicRoughness)
SetKeyword(material, "_METALLICGLOSSMAP", material.GetTexture("_MetallicGlossMap"));
bool shouldEmissionBeEnabled = material.GetColor("_EmissionColor") != Color.black;
SetKeyword(material, "_EMISSION", shouldEmissionBeEnabled);
}
static void MaterialChanged(Material material, WorkflowMode workflowMode)
{
SetupMaterialWithBlendMode(material, (BlendMode)material.GetFloat("_Mode"), material.GetFloat("_Cutoff"));
SetMaterialKeywords(material, workflowMode);
}
static void SetKeyword(Material m, string keyword, bool state)
{
if (state)
m.EnableKeyword(keyword);
else
m.DisableKeyword(keyword);
}
}
} // namespace UnityEditor