// NiloToon Character Fur Shader - Custom ShaderGUI // Clean, organized material inspector with foldable sections using UnityEngine; using UnityEditor; using System; namespace NiloToonURP.ShaderGUI { public class NiloToonCharacterFurShaderGUI : UnityEditor.ShaderGUI { // Foldout states private static bool showBaseColor = true; private static bool showNormalMap = false; private static bool showFurShape = true; private static bool showFurRim = false; private static bool showCelShading = true; private static bool showShadowColor = false; private static bool showMatCap = false; private static bool showRimLight = false; private static bool showEmission = false; private static bool showOcclusion = false; private static bool showOutline = false; private static bool showRendering = false; // Styles private static GUIStyle headerStyle; private static GUIStyle foldoutStyle; private static bool stylesInitialized = false; private static void InitStyles() { if (stylesInitialized) return; headerStyle = new GUIStyle(EditorStyles.boldLabel) { fontSize = 12, margin = new RectOffset(0, 0, 10, 5) }; foldoutStyle = new GUIStyle(EditorStyles.foldout) { fontStyle = FontStyle.Bold, fontSize = 11 }; stylesInitialized = true; } public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties) { InitStyles(); Material material = materialEditor.target as Material; EditorGUILayout.Space(5); // Header EditorGUILayout.LabelField("NiloToon Character Fur Shader", EditorStyles.boldLabel); EditorGUILayout.HelpBox( "Single-material fur shader with Base + Fur Shell rendering.\n\n" + "Setup:\n" + "1. Add 'NiloToonFurRendererFeature' to your URP Renderer Asset\n" + "2. Apply this shader to your mesh material (single material slot)\n\n" + "The shader will render base mesh first, then fur shells on top.", MessageType.Info); DrawSeparator(); // Base Color Section showBaseColor = DrawSection("Base Color", showBaseColor, () => { DrawProperty(materialEditor, properties, "_BaseMap", "Base Map"); DrawProperty(materialEditor, properties, "_BaseColor", "Base Color"); DrawProperty(materialEditor, properties, "_Cutoff", "Alpha Cutoff"); }); // Normal Map Section showNormalMap = DrawSection("Normal Map", showNormalMap, () => { DrawToggleProperty(materialEditor, properties, "_UseNormalMap", "Enable Normal Map", "_NORMALMAP", material); if (material.IsKeywordEnabled("_NORMALMAP")) { EditorGUI.indentLevel++; DrawProperty(materialEditor, properties, "_BumpMap", "Normal Map"); DrawProperty(materialEditor, properties, "_BumpScale", "Normal Scale"); EditorGUI.indentLevel--; } }); // Fur Shape Section showFurShape = DrawSection("Fur Shape", showFurShape, () => { DrawProperty(materialEditor, properties, "_FurNoiseMask", "Fur Noise Mask"); DrawProperty(materialEditor, properties, "_FurMask", "Fur Mask"); DrawProperty(materialEditor, properties, "_FurLengthMask", "Fur Length Mask"); EditorGUILayout.Space(5); DrawProperty(materialEditor, properties, "_FurVector", "Fur Direction (XYZ) + Length (W)"); DrawProperty(materialEditor, properties, "_FurVectorTex", "Fur Direction Map"); DrawProperty(materialEditor, properties, "_FurVectorScale", "Fur Direction Scale"); EditorGUILayout.Space(5); DrawProperty(materialEditor, properties, "_FurGravity", "Fur Gravity"); DrawProperty(materialEditor, properties, "_FurRandomize", "Fur Randomize"); DrawProperty(materialEditor, properties, "_FurAO", "Fur Ambient Occlusion"); EditorGUILayout.Space(5); DrawProperty(materialEditor, properties, "_FurLayerNum", "Fur Layer Count"); DrawProperty(materialEditor, properties, "_FurRootOffset", "Fur Root Offset"); }); // Fur Rim Section showFurRim = DrawSection("Fur Rim Light", showFurRim, () => { DrawProperty(materialEditor, properties, "_FurRimColor", "Fur Rim Color"); DrawProperty(materialEditor, properties, "_FurRimFresnelPower", "Fur Rim Fresnel Power"); DrawProperty(materialEditor, properties, "_FurRimAntiLight", "Fur Rim Anti-Light"); }); // Cel Shading Section showCelShading = DrawSection("Cel Shading", showCelShading, () => { DrawProperty(materialEditor, properties, "_CelShadeMidPoint", "Cel Shade Mid Point"); DrawProperty(materialEditor, properties, "_CelShadeSoftness", "Cel Shade Softness"); }); // Shadow Color Section showShadowColor = DrawSection("Shadow Color", showShadowColor, () => { DrawToggleProperty(materialEditor, properties, "_EnableShadowColor", "Enable Shadow Color", "_SHADOW_COLOR", material); if (material.IsKeywordEnabled("_SHADOW_COLOR")) { EditorGUI.indentLevel++; DrawProperty(materialEditor, properties, "_ShadowColor", "Shadow Tint Color"); DrawProperty(materialEditor, properties, "_ShadowBrightness", "Shadow Brightness"); EditorGUILayout.Space(5); EditorGUILayout.LabelField("HSV Adjustment", EditorStyles.miniLabel); DrawProperty(materialEditor, properties, "_ShadowHueShift", "Hue Shift"); DrawProperty(materialEditor, properties, "_ShadowSaturationBoost", "Saturation Boost"); DrawProperty(materialEditor, properties, "_ShadowValueMultiplier", "Value Multiplier"); EditorGUI.indentLevel--; } }); // MatCap Section showMatCap = DrawSection("MatCap", showMatCap, () => { EditorGUILayout.LabelField("MatCap Additive", EditorStyles.miniLabel); DrawToggleProperty(materialEditor, properties, "_UseMatCapAdd", "Enable MatCap (Add)", "_MATCAP_ADD", material); if (material.IsKeywordEnabled("_MATCAP_ADD")) { EditorGUI.indentLevel++; DrawProperty(materialEditor, properties, "_MatCapAddMap", "MatCap Add Map"); DrawProperty(materialEditor, properties, "_MatCapAddColor", "MatCap Add Color"); DrawProperty(materialEditor, properties, "_MatCapAddIntensity", "MatCap Add Intensity"); DrawProperty(materialEditor, properties, "_MatCapAddMask", "MatCap Add Mask"); EditorGUI.indentLevel--; } EditorGUILayout.Space(5); EditorGUILayout.LabelField("MatCap Multiply", EditorStyles.miniLabel); DrawToggleProperty(materialEditor, properties, "_UseMatCapMul", "Enable MatCap (Multiply)", "_MATCAP_MUL", material); if (material.IsKeywordEnabled("_MATCAP_MUL")) { EditorGUI.indentLevel++; DrawProperty(materialEditor, properties, "_MatCapMulMap", "MatCap Multiply Map"); DrawProperty(materialEditor, properties, "_MatCapMulIntensity", "MatCap Multiply Intensity"); EditorGUI.indentLevel--; } }); // Rim Light Section showRimLight = DrawSection("Rim Light (General)", showRimLight, () => { DrawToggleProperty(materialEditor, properties, "_UseRimLight", "Enable Rim Light", "_RIMLIGHT", material); if (material.IsKeywordEnabled("_RIMLIGHT")) { EditorGUI.indentLevel++; DrawProperty(materialEditor, properties, "_RimLightColor", "Rim Light Color"); DrawProperty(materialEditor, properties, "_RimLightPower", "Rim Light Power"); DrawProperty(materialEditor, properties, "_RimLightIntensity", "Rim Light Intensity"); EditorGUI.indentLevel--; } }); // Emission Section showEmission = DrawSection("Emission", showEmission, () => { DrawToggleProperty(materialEditor, properties, "_UseEmission", "Enable Emission", "_EMISSION", material); if (material.IsKeywordEnabled("_EMISSION")) { EditorGUI.indentLevel++; DrawProperty(materialEditor, properties, "_EmissionMap", "Emission Map"); DrawProperty(materialEditor, properties, "_EmissionColor", "Emission Color"); DrawProperty(materialEditor, properties, "_EmissionIntensity", "Emission Intensity"); EditorGUI.indentLevel--; } }); // Occlusion Section showOcclusion = DrawSection("Occlusion", showOcclusion, () => { DrawToggleProperty(materialEditor, properties, "_UseOcclusion", "Enable Occlusion Map", "_OCCLUSIONMAP", material); if (material.IsKeywordEnabled("_OCCLUSIONMAP")) { EditorGUI.indentLevel++; DrawProperty(materialEditor, properties, "_OcclusionMap", "Occlusion Map"); DrawProperty(materialEditor, properties, "_OcclusionStrength", "Occlusion Strength"); EditorGUI.indentLevel--; } }); // Outline Section showOutline = DrawSection("Outline", showOutline, () => { DrawProperty(materialEditor, properties, "_OutlineWidth", "Outline Width"); DrawProperty(materialEditor, properties, "_OutlineColor", "Outline Color"); DrawProperty(materialEditor, properties, "_OutlineWidthMask", "Outline Width Mask"); }); // Rendering Options Section showRendering = DrawSection("Rendering Options", showRendering, () => { DrawProperty(materialEditor, properties, "_Cull", "Cull Mode"); DrawProperty(materialEditor, properties, "_FurCull", "Fur Cull Mode"); DrawProperty(materialEditor, properties, "_ZWrite", "ZWrite"); DrawProperty(materialEditor, properties, "_FurZWrite", "Fur ZWrite"); DrawProperty(materialEditor, properties, "_ZTest", "ZTest"); }); EditorGUILayout.Space(10); // Render Queue materialEditor.RenderQueueField(); materialEditor.EnableInstancingField(); materialEditor.DoubleSidedGIField(); } private bool DrawSection(string title, bool foldout, Action drawContent) { EditorGUILayout.Space(2); // Section header with foldout Rect headerRect = EditorGUILayout.GetControlRect(false, 20); headerRect.x -= 2; headerRect.width += 4; // Background EditorGUI.DrawRect(headerRect, new Color(0.22f, 0.22f, 0.22f, 1f)); // Foldout headerRect.x += 4; foldout = EditorGUI.Foldout(headerRect, foldout, title, true, foldoutStyle); if (foldout) { EditorGUILayout.BeginVertical(EditorStyles.helpBox); EditorGUI.indentLevel++; drawContent?.Invoke(); EditorGUI.indentLevel--; EditorGUILayout.EndVertical(); } return foldout; } private void DrawProperty(MaterialEditor editor, MaterialProperty[] properties, string propertyName, string label) { MaterialProperty prop = FindProperty(propertyName, properties, false); if (prop != null) { editor.ShaderProperty(prop, label); } } private void DrawToggleProperty(MaterialEditor editor, MaterialProperty[] properties, string propertyName, string label, string keyword, Material material) { MaterialProperty prop = FindProperty(propertyName, properties, false); if (prop != null) { EditorGUI.BeginChangeCheck(); editor.ShaderProperty(prop, label); if (EditorGUI.EndChangeCheck()) { if (prop.floatValue > 0.5f) material.EnableKeyword(keyword); else material.DisableKeyword(keyword); } } } private void DrawSeparator() { EditorGUILayout.Space(5); Rect rect = EditorGUILayout.GetControlRect(false, 1); EditorGUI.DrawRect(rect, new Color(0.5f, 0.5f, 0.5f, 0.5f)); EditorGUILayout.Space(5); } } }