ADD : 퍼 쉐이더 추가

This commit is contained in:
KINDNICK 2025-12-15 01:52:59 +09:00
parent 52d9e9f548
commit 5a559d888e
20 changed files with 2450 additions and 11 deletions

View File

@ -0,0 +1,298 @@
// 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);
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3ad6027c1c29b71488586df1e8150fa7

View File

@ -0,0 +1,60 @@
// NiloToon Fur Renderer Feature
// Add this to your URP Renderer to enable fur shell rendering
// This allows single-material fur shaders to work properly
//
// Note: Fur mask rendering is now integrated into NiloToonPrepassBufferRTPass
// The fur mask is automatically rendered to G and B channels of _NiloToonPrepassBufferTex
using System;
using UnityEngine;
using UnityEngine.Rendering.Universal;
namespace NiloToon.NiloToonURP
{
[Serializable]
public class NiloToonFurRendererFeatureSettings
{
[Header("Fur Shell Settings")]
public NiloToonFurShellPass.Settings furShellSettings = new NiloToonFurShellPass.Settings();
}
public class NiloToonFurRendererFeature : ScriptableRendererFeature
{
public NiloToonFurRendererFeatureSettings settings = new NiloToonFurRendererFeatureSettings();
NiloToonFurShellPass FurShellPass;
public override void Create()
{
ReInitPassesIfNeeded();
}
private void ReInitPassesIfNeeded()
{
if (FurShellPass == null)
FurShellPass = new NiloToonFurShellPass(settings.furShellSettings);
// Set render pass events
FurShellPass.renderPassEvent = settings.furShellSettings.renderTiming;
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
ReInitPassesIfNeeded();
// Update render timing in case it changed
FurShellPass.renderPassEvent = settings.furShellSettings.renderTiming;
// Enqueue fur shell pass for actual fur rendering
// Note: Fur mask is rendered via NiloToonPrepassBufferRTPass (NiloToonFurShellMask LightMode)
renderer.EnqueuePass(FurShellPass);
}
#if UNITY_2022_2_OR_NEWER
protected override void Dispose(bool disposing)
{
FurShellPass = null;
}
#endif
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 4076e5ecd1634a74eaabed924c9a971c

View File

@ -0,0 +1,220 @@
// NiloToon Fur Mask Pass
// Renders fur shells to a dedicated mask buffer for fur area detection
// Separate from main fur rendering for cleaner architecture
// Based on NiloToonPrepassBufferRTPass pattern
using System;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
using UnityEngine.Rendering.RendererUtils;
#if UNITY_6000_0_OR_NEWER
using UnityEngine.Rendering.RenderGraphModule;
#endif
using UnityEngine.Rendering.Universal;
namespace NiloToon.NiloToonURP
{
public class NiloToonFurMaskPass : ScriptableRenderPass
{
static readonly ShaderTagId furShellMaskLightModeShaderTagId = new ShaderTagId("NiloToonFurShellMask");
static readonly int FurMaskTexId = Shader.PropertyToID("_NiloToonFurMaskTex");
[Serializable]
public class Settings
{
[Tooltip("Create a mask buffer for fur area detection.\n" +
"This creates _NiloToonFurMaskTex which can be used for post-processing.\n\nDefault: ON")]
public bool Enabled = true;
}
Settings settings;
RenderQueueRange renderQueueRange;
#if UNITY_2022_2_OR_NEWER
RTHandle furMaskRTHColor;
RTHandle furMaskRTHDepth;
#else
RenderTargetHandle furMaskRTHandle;
#endif
public NiloToonFurMaskPass(Settings settings)
{
this.settings = settings;
this.renderQueueRange = RenderQueueRange.all;
#if !UNITY_2022_2_OR_NEWER
furMaskRTHandle.Init("_NiloToonFurMaskRT");
#endif
base.profilingSampler = new ProfilingSampler(nameof(NiloToonFurMaskPass));
}
#if UNITY_6000_0_OR_NEWER
[Obsolete]
#endif
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
{
if (!settings.Enabled)
return;
var cameraData = renderingData.cameraData;
#if UNITY_2022_2_OR_NEWER
// [color RTHandle's Descriptor] - same pattern as NiloToonPrepassBufferRTPass
var colorDesc = cameraData.cameraTargetDescriptor;
colorDesc.graphicsFormat = GraphicsFormat.R8G8B8A8_UNorm;
colorDesc.depthBufferBits = 0;
colorDesc.msaaSamples = 1;
// [depth RTHandle's Descriptor] - create own depth buffer like PrepassBufferRTPass
var depthDesc = cameraData.cameraTargetDescriptor;
depthDesc.graphicsFormat = GraphicsFormat.None; // Depth only rendering
depthDesc.depthStencilFormat = cameraData.cameraTargetDescriptor.depthStencilFormat;
depthDesc.msaaSamples = 1;
RenderingUtils.ReAllocateIfNeeded(ref furMaskRTHColor, colorDesc, name: "_NiloToonFurMaskColor");
RenderingUtils.ReAllocateIfNeeded(ref furMaskRTHDepth, depthDesc, name: "_NiloToonFurMaskDepth");
// Set global RT
cmd.SetGlobalTexture(FurMaskTexId, furMaskRTHColor);
// Configure target with own color and depth buffers
ConfigureTarget(furMaskRTHColor, furMaskRTHDepth);
ConfigureClear(ClearFlag.All, Color.black);
#else
var furMaskDesc = cameraData.cameraTargetDescriptor;
furMaskDesc.colorFormat = RenderTextureFormat.ARGB32;
furMaskDesc.depthStencilFormat = cameraData.cameraTargetDescriptor.depthStencilFormat;
furMaskDesc.msaaSamples = 1;
cmd.GetTemporaryRT(furMaskRTHandle.id, furMaskDesc);
cmd.SetGlobalTexture(FurMaskTexId, furMaskRTHandle.Identifier());
ConfigureTarget(new RenderTargetIdentifier(furMaskRTHandle.Identifier(), 0, CubemapFace.Unknown, -1));
ConfigureClear(ClearFlag.All, Color.black);
#endif
}
#if UNITY_6000_0_OR_NEWER
[Obsolete]
#endif
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
if (!settings.Enabled)
return;
// Never draw in Preview
Camera camera = renderingData.cameraData.camera;
if (camera.cameraType == CameraType.Preview)
return;
var cmd = CommandBufferPool.Get();
using (new ProfilingScope(cmd, base.profilingSampler))
{
// Execute command buffer before DrawRenderers (frame debugger requirement)
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
// Draw all fur shells with NiloToonFurShellMask pass
var filterSetting = new FilteringSettings(renderQueueRange);
var sortFlags = SortingCriteria.CommonTransparent;
var drawSettings = CreateDrawingSettings(furShellMaskLightModeShaderTagId, ref renderingData, sortFlags);
drawSettings.perObjectData = PerObjectData.None;
context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref filterSetting);
}
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
CommandBufferPool.Release(cmd);
}
public override void OnCameraCleanup(CommandBuffer cmd)
{
// To Release a RTHandle, do it in ScriptableRendererFeature's Dispose(), don't do it in OnCameraCleanup(...)
#if !UNITY_2022_2_OR_NEWER
if (settings.Enabled)
{
cmd.ReleaseTemporaryRT(furMaskRTHandle.id);
}
#endif
}
#if UNITY_2022_2_OR_NEWER
public void Dispose()
{
furMaskRTHColor?.Release();
furMaskRTHDepth?.Release();
}
#endif
#if UNITY_6000_0_OR_NEWER
private class FurMaskPassData
{
public UniversalCameraData cameraData;
public TextureHandle colorTarget;
public TextureHandle depthTarget;
public RendererListHandle rendererList;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if (!settings.Enabled)
return;
var cameraData = frameData.Get<UniversalCameraData>();
var resourceData = frameData.Get<UniversalResourceData>();
var renderingData = frameData.Get<UniversalRenderingData>();
// Skip Preview cameras - same check as Execute
if (cameraData.camera.cameraType == CameraType.Preview)
return;
using (var builder = renderGraph.AddRasterRenderPass<FurMaskPassData>("NiloToon Fur Mask Buffer", out var passData, base.profilingSampler))
{
passData.cameraData = cameraData;
// Create render textures for RG - EXACT same descriptors as OnCameraSetup
var colorDesc = cameraData.cameraTargetDescriptor;
colorDesc.graphicsFormat = GraphicsFormat.R8G8B8A8_UNorm;
colorDesc.depthBufferBits = 0;
colorDesc.msaaSamples = 1;
var depthDesc = cameraData.cameraTargetDescriptor;
depthDesc.graphicsFormat = GraphicsFormat.None; // Depth only rendering
depthDesc.depthStencilFormat = cameraData.cameraTargetDescriptor.depthStencilFormat;
depthDesc.msaaSamples = 1;
passData.colorTarget = UniversalRenderer.CreateRenderGraphTexture(renderGraph, colorDesc, "_NiloToonFurMaskColor", false);
passData.depthTarget = UniversalRenderer.CreateRenderGraphTexture(renderGraph, depthDesc, "_NiloToonFurMaskDepth", false);
// Create renderer list - EXACT same settings as Execute
var renderListDesc = new RendererListDesc(furShellMaskLightModeShaderTagId, renderingData.cullResults, cameraData.camera)
{
sortingCriteria = SortingCriteria.CommonTransparent,
renderQueueRange = renderQueueRange,
};
passData.rendererList = renderGraph.CreateRendererList(renderListDesc);
// Set up render targets - Match ConfigureTarget behavior
builder.SetRenderAttachment(passData.colorTarget, 0, AccessFlags.Write);
builder.SetRenderAttachmentDepth(passData.depthTarget, AccessFlags.Write);
builder.UseRendererList(passData.rendererList);
// Set global textures - Match cmd.SetGlobalTexture behavior
builder.SetGlobalTextureAfterPass(passData.colorTarget, FurMaskTexId);
builder.SetRenderFunc((FurMaskPassData data, RasterGraphContext context) =>
{
// Match ConfigureClear(ClearFlag.All, Color.black) behavior
context.cmd.ClearRenderTarget(RTClearFlags.All, Color.black, 1.0f, 0);
// Match context.DrawRenderers behavior
context.cmd.DrawRendererList(data.rendererList);
});
}
}
#endif
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d398264d435e9314bae19d3e5532f775

View File

@ -0,0 +1,119 @@
// NiloToon Fur Shell Pass
// Renders fur shell pass for materials with "NiloToonFurShell" LightMode tag
// Only handles fur rendering - mask buffer is handled by NiloToonFurMaskPass
using System;
using UnityEngine;
using UnityEngine.Rendering;
#if UNITY_6000_0_OR_NEWER
using UnityEngine.Rendering.RenderGraphModule;
#endif
using UnityEngine.Rendering.Universal;
namespace NiloToon.NiloToonURP
{
public class NiloToonFurShellPass : ScriptableRenderPass
{
static readonly ShaderTagId furShellLightModeShaderTagId = new ShaderTagId("NiloToonFurShell");
[Serializable]
public class Settings
{
[Header("Fur Shell Settings")]
[Tooltip("Enable to render fur shells.\n\nDefault: ON")]
[OverrideDisplayName("Enable?")]
[Revertible]
public bool ShouldRenderFurShell = true;
[Tooltip("When to render fur shells in the rendering pipeline.\n\nDefault: AfterRenderingOpaques")]
[Revertible]
[OverrideDisplayName("Render Timing")]
public RenderPassEvent renderTiming = RenderPassEvent.AfterRenderingOpaques;
}
Settings settings;
ProfilingSampler m_ProfilingSampler;
RenderQueueRange renderQueueRange;
public NiloToonFurShellPass(Settings settings)
{
this.settings = settings;
m_ProfilingSampler = new ProfilingSampler("NiloToonFurShellPass");
this.renderQueueRange = RenderQueueRange.all;
}
#if UNITY_6000_0_OR_NEWER
[Obsolete]
#endif
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
if (!settings.ShouldRenderFurShell)
return;
CommandBuffer cmd = CommandBufferPool.Get();
using (new ProfilingScope(cmd, m_ProfilingSampler))
{
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
DrawingSettings drawingSettings = CreateDrawingSettings(
furShellLightModeShaderTagId,
ref renderingData,
SortingCriteria.CommonTransparent
);
FilteringSettings filteringSettings = new FilteringSettings(renderQueueRange);
context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref filteringSettings);
}
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
CommandBufferPool.Release(cmd);
}
#if UNITY_6000_0_OR_NEWER
private class PassData
{
public RendererListHandle rendererListHandle;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if (!settings.ShouldRenderFurShell)
return;
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
using (var builder = renderGraph.AddRasterRenderPass<PassData>("NiloToonFurShellPass", out var passData, m_ProfilingSampler))
{
var drawingSettings = RenderingUtils.CreateDrawingSettings(
furShellLightModeShaderTagId,
renderingData,
cameraData,
lightData,
SortingCriteria.CommonTransparent
);
var filteringSettings = new FilteringSettings(renderQueueRange);
var renderListParams = new RendererListParams(renderingData.cullResults, drawingSettings, filteringSettings);
passData.rendererListHandle = renderGraph.CreateRendererList(renderListParams);
builder.UseRendererList(passData.rendererListHandle);
builder.SetRenderAttachment(resourceData.activeColorTexture, 0);
builder.SetRenderAttachmentDepth(resourceData.activeDepthTexture);
builder.SetRenderFunc((PassData data, RasterGraphContext rgContext) =>
{
rgContext.cmd.DrawRendererList(data.rendererListHandle);
});
}
}
#endif
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6edb03bf5b2c68d4dbc8b39fac860935

View File

@ -5,12 +5,14 @@
/*
_NiloToonPrepassBufferRT is storing the following data
-r: face
-g: character visible area (for NiloToon Bloom / NiloToon Tonemapping)
-r: face (legacy, kept for compatibility)
-g: character visible area (for NiloToon Bloom / NiloToon Tonemapping) - includes face, body, and fur
-b: unused
-a: unused
* We may draw IsFace,IsSkin,RimArea into this RT in future versions
* Fur mask is now integrated: renders fur shells to G channel
* All character areas (face, body, fur) use G channel for unified character mask
*/
using System;
using UnityEngine;
@ -38,6 +40,10 @@ namespace NiloToon.NiloToonURP
// Constants
static readonly ShaderTagId shaderTagId = new ShaderTagId("NiloToonPrepassBuffer");
static readonly ShaderTagId furShellMaskShaderTagId = new ShaderTagId("NiloToonFurShellMask");
// Fur mask settings
bool enableFurMask = true;
// Constructor(will not call every frame)
public NiloToonPrepassBufferRTPass(NiloToonRendererFeatureSettings allSettings)
@ -50,6 +56,14 @@ namespace NiloToon.NiloToonURP
base.profilingSampler = new ProfilingSampler(nameof(NiloToonPrepassBufferRTPass));
}
/// <summary>
/// Enable or disable fur mask rendering
/// </summary>
public void SetFurMaskEnabled(bool enabled)
{
enableFurMask = enabled;
}
// This method is called before executing the render pass.
// It can be used to configure render targets and their clear state. Also to create temporary render target textures.
@ -149,16 +163,27 @@ namespace NiloToon.NiloToonURP
cmd.Clear();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// draw all nilotoon character renderer's "NiloToonPrepassBuffer" pass using SRP batching
// 1. Draw all nilotoon character renderer's "NiloToonPrepassBuffer" pass using SRP batching
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var filterSetting = new FilteringSettings(RenderQueueRange.opaque); //TODO: should include transparent also?
var filterSetting = new FilteringSettings(RenderQueueRange.opaque); //TODO: should include transparent also?
var sortFlags = renderingData.cameraData.defaultOpaqueSortFlags;
var drawSettings = CreateDrawingSettings(shaderTagId, ref renderingData, sortFlags);
drawSettings.perObjectData = PerObjectData.None;
context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref filterSetting);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 2. Draw fur shell mask (NiloToonFurShellMask pass) - adds fur area to G and B channels
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (enableFurMask)
{
var furFilterSetting = new FilteringSettings(RenderQueueRange.all);
var furDrawSettings = CreateDrawingSettings(furShellMaskShaderTagId, ref renderingData, SortingCriteria.CommonTransparent);
furDrawSettings.perObjectData = PerObjectData.None;
context.DrawRenderers(renderingData.cullResults, ref furDrawSettings, ref furFilterSetting);
}
}
// must write these line after using{} finished, to ensure profiler and frame debugger display correctness
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
@ -187,13 +212,15 @@ namespace NiloToon.NiloToonURP
/////////////////////////////////////////////////////////////////////
// RG support
/////////////////////////////////////////////////////////////////////
#if UNITY_6000_0_OR_NEWER
#if UNITY_6000_0_OR_NEWER
private class PrepassBufferPassData
{
public UniversalCameraData cameraData;
public TextureHandle colorTarget;
public TextureHandle depthTarget;
public RendererListHandle rendererList;
public RendererListHandle furRendererList;
public bool enableFurMask;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameContext)
@ -209,6 +236,7 @@ namespace NiloToon.NiloToonURP
using (var builder = renderGraph.AddRasterRenderPass<PrepassBufferPassData>("NiloToon Prepass Buffer", out var passData, base.profilingSampler))
{
passData.cameraData = cameraData;
passData.enableFurMask = enableFurMask;
// Create render textures for RG - EXACT same descriptors as OnCameraSetup
var colorDesc = cameraData.cameraTargetDescriptor;
@ -224,19 +252,28 @@ namespace NiloToon.NiloToonURP
passData.colorTarget = UniversalRenderer.CreateRenderGraphTexture(renderGraph, colorDesc, "_NiloToonPrepassBufferColor", false);
passData.depthTarget = UniversalRenderer.CreateRenderGraphTexture(renderGraph, depthDesc, "_NiloToonPrepassBufferDepth", false);
// Create renderer list - EXACT same settings as Execute
// Create renderer list for character prepass - EXACT same settings as Execute
var renderListDesc = new RendererListDesc(shaderTagId, renderingData.cullResults, cameraData.camera)
{
sortingCriteria = cameraData.defaultOpaqueSortFlags, // Match: var sortFlags = renderingData.cameraData.defaultOpaqueSortFlags;
renderQueueRange = RenderQueueRange.opaque, // Match: var filterSetting = new FilteringSettings(RenderQueueRange.opaque);
sortingCriteria = cameraData.defaultOpaqueSortFlags,
renderQueueRange = RenderQueueRange.opaque,
};
passData.rendererList = renderGraph.CreateRendererList(renderListDesc);
// Create renderer list for fur shell mask
var furRenderListDesc = new RendererListDesc(furShellMaskShaderTagId, renderingData.cullResults, cameraData.camera)
{
sortingCriteria = SortingCriteria.CommonTransparent,
renderQueueRange = RenderQueueRange.all,
};
passData.furRendererList = renderGraph.CreateRendererList(furRenderListDesc);
// Set up render targets - Match ConfigureTarget behavior
builder.SetRenderAttachment(passData.colorTarget, 0, AccessFlags.Write);
builder.SetRenderAttachmentDepth(passData.depthTarget, AccessFlags.Write);
builder.UseRendererList(passData.rendererList);
builder.UseRendererList(passData.furRendererList);
// Set global textures - Match cmd.SetGlobalTexture behavior
builder.SetGlobalTextureAfterPass(passData.colorTarget, Shader.PropertyToID("_NiloToonPrepassBufferTex"));
@ -246,9 +283,15 @@ namespace NiloToon.NiloToonURP
{
// Match ConfigureClear(ClearFlag.All, Color.black) behavior
context.cmd.ClearRenderTarget(RTClearFlags.All, Color.black, 1.0f, 0);
// Match context.DrawRenderers behavior
// 1. Draw character prepass
context.cmd.DrawRendererList(data.rendererList);
// 2. Draw fur shell mask (adds fur area to G and B channels)
if (data.enableFurMask)
{
context.cmd.DrawRendererList(data.furRendererList);
}
});
}
}

View File

@ -0,0 +1,566 @@
// NiloToon Character Fur Shader
// Single-material fur shader with Base + Fur Shell rendering
// Requires NiloToonFurRendererFeature to be added to URP Renderer
//
// Usage:
// 1. Add NiloToonFurRendererFeature to your URP Renderer Asset
// 2. Apply this shader to your mesh material (single material slot)
Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur"
{
Properties
{
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Base Color
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[Header(Base Color)]
[Space(5)]
[MainTexture] _BaseMap("Base Map", 2D) = "white" {}
[HDR][MainColor] _BaseColor("Base Color", Color) = (1,1,1,1)
_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Normal Map
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[Header(Normal Map)]
[Space(5)]
[Toggle(_NORMALMAP)] _UseNormalMap("Enable Normal Map", Float) = 0
[Normal] _BumpMap("Normal Map", 2D) = "bump" {}
_BumpScale("Normal Scale", Range(0, 2)) = 1.0
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Fur Settings
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[Header(Fur Shape)]
[Space(5)]
_FurNoiseMask("Fur Noise Mask", 2D) = "white" {}
_FurMask("Fur Mask (where fur appears)", 2D) = "white" {}
_FurLengthMask("Fur Length Mask", 2D) = "white" {}
_FurVector("Fur Direction (XYZ) + Length (W)", Vector) = (0, 0, 1, 0.02)
[Normal] _FurVectorTex("Fur Direction Map (Normal)", 2D) = "bump" {}
_FurVectorScale("Fur Direction Scale", Range(-10, 10)) = 1.0
_FurGravity("Fur Gravity", Range(0, 1)) = 0.25
_FurRandomize("Fur Randomize", Range(0, 1)) = 0.1
_FurAO("Fur Ambient Occlusion", Range(0, 1)) = 0.5
[Header(Shell Layers)]
[Space(5)]
[IntRange] _FurLayerNum("Fur Layer Count", Range(1, 3)) = 2
_FurRootOffset("Fur Root Offset", Range(-1, 0)) = 0
[Header(Fur Rim Light)]
[Space(5)]
[HDR] _FurRimColor("Fur Rim Color", Color) = (1, 1, 1, 1)
_FurRimFresnelPower("Fur Rim Fresnel Power", Range(0.01, 50)) = 3.0
_FurRimAntiLight("Fur Rim Anti-Light", Range(0, 1)) = 0.5
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Toon Shading (NiloToon Style)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[Header(Cel Shading)]
[Space(5)]
_CelShadeMidPoint("Cel Shade Mid Point", Range(-1, 1)) = 0
_CelShadeSoftness("Cel Shade Softness", Range(0, 1)) = 0.1
[Header(Shadow Color)]
[Space(5)]
[Toggle(_SHADOW_COLOR)] _EnableShadowColor("Enable Shadow Color", Float) = 0
[HDR] _ShadowColor("Shadow Tint Color", Color) = (1, 1, 1, 1)
_ShadowBrightness("Shadow Brightness", Range(0, 2)) = 1.0
[Header(HSV Adjustment in Shadow)]
[Space(5)]
_ShadowHueShift("Shadow Hue Shift", Range(-0.5, 0.5)) = 0
_ShadowSaturationBoost("Shadow Saturation Boost", Range(0, 2)) = 1.0
_ShadowValueMultiplier("Shadow Value Multiplier", Range(0, 2)) = 1.0
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MatCap
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[Header(MatCap Additive)]
[Space(5)]
[Toggle(_MATCAP_ADD)] _UseMatCapAdd("Enable MatCap (Add)", Float) = 0
_MatCapAddMap("MatCap Add Map", 2D) = "black" {}
[HDR] _MatCapAddColor("MatCap Add Color", Color) = (1, 1, 1, 1)
_MatCapAddIntensity("MatCap Add Intensity", Range(0, 5)) = 1.0
_MatCapAddMask("MatCap Add Mask", 2D) = "white" {}
[Header(MatCap Multiply)]
[Space(5)]
[Toggle(_MATCAP_MUL)] _UseMatCapMul("Enable MatCap (Multiply)", Float) = 0
_MatCapMulMap("MatCap Multiply Map", 2D) = "white" {}
_MatCapMulIntensity("MatCap Multiply Intensity", Range(0, 2)) = 1.0
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Rim Light (General)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[Header(Rim Light)]
[Space(5)]
[Toggle(_RIMLIGHT)] _UseRimLight("Enable Rim Light", Float) = 0
[HDR] _RimLightColor("Rim Light Color", Color) = (1, 1, 1, 1)
_RimLightPower("Rim Light Power", Range(0.01, 20)) = 5.0
_RimLightIntensity("Rim Light Intensity", Range(0, 5)) = 1.0
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Emission
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[Header(Emission)]
[Space(5)]
[Toggle(_EMISSION)] _UseEmission("Enable Emission", Float) = 0
_EmissionMap("Emission Map", 2D) = "white" {}
[HDR] _EmissionColor("Emission Color", Color) = (0, 0, 0, 1)
_EmissionIntensity("Emission Intensity", Range(0, 10)) = 1.0
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Occlusion
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[Header(Occlusion)]
[Space(5)]
[Toggle(_OCCLUSIONMAP)] _UseOcclusion("Enable Occlusion Map", Float) = 0
_OcclusionMap("Occlusion Map", 2D) = "white" {}
_OcclusionStrength("Occlusion Strength", Range(0, 1)) = 1.0
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Outline
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[Header(Outline)]
[Space(5)]
_OutlineWidth("Outline Width", Range(0, 10)) = 1.0
[HDR] _OutlineColor("Outline Color", Color) = (0, 0, 0, 1)
_OutlineWidthMask("Outline Width Mask", 2D) = "white" {}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Rendering Options
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[Header(Rendering Options)]
[Space(5)]
[Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull Mode", Float) = 2
[Enum(UnityEngine.Rendering.CullMode)] _FurCull("Fur Cull Mode", Float) = 0
[Enum(Off, 0, On, 1)] _ZWrite("ZWrite", Float) = 1
_FurZWrite("Fur ZWrite", Float) = 0
[Enum(UnityEngine.Rendering.CompareFunction)] _ZTest("ZTest", Float) = 4
// Hidden
[HideInInspector] _Surface("__surface", Float) = 0.0
[HideInInspector] _Blend("__blend", Float) = 0.0
[HideInInspector] _QueueOffset("Queue offset", Float) = 0.0
}
SubShader
{
Tags
{
"RenderType" = "Opaque"
"RenderPipeline" = "UniversalPipeline"
"UniversalMaterialType" = "Lit"
"IgnoreProjector" = "True"
"Queue" = "Geometry"
}
LOD 300
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pass 0: ForwardLit - Base Surface (Full NiloToon Style)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Pass
{
Name "ForwardLit"
Tags { "LightMode" = "UniversalForward" }
Cull [_Cull]
ZWrite On
ZTest LEqual
Blend One Zero
HLSLPROGRAM
#pragma target 4.5
// Shader Features
#pragma shader_feature_local _NORMALMAP
#pragma shader_feature_local _SHADOW_COLOR
#pragma shader_feature_local _MATCAP_ADD
#pragma shader_feature_local _MATCAP_MUL
#pragma shader_feature_local _RIMLIGHT
#pragma shader_feature_local _EMISSION
#pragma shader_feature_local _OCCLUSIONMAP
// URP Keywords
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile_fragment _ _SHADOWS_SOFT
#pragma multi_compile_fog
// GPU Instancing
#pragma multi_compile_instancing
#pragma instancing_options renderinglayer
#pragma vertex vert
#pragma fragment frag
#define NILOTOON_FUR_BASE_PASS
#include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl"
#include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Fragment.hlsl"
ENDHLSL
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pass 1: Fur Shell Pass (Geometry Shader) - Standard single target
// Rendered by NiloToonFurRendererFeature via "NiloToonFurShell" LightMode
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Pass
{
Name "FurShell"
Tags { "LightMode" = "NiloToonFurShell" }
Cull [_FurCull]
ZWrite [_FurZWrite]
ZTest LEqual
Blend SrcAlpha OneMinusSrcAlpha, One OneMinusSrcAlpha
HLSLPROGRAM
#pragma target 4.5
#pragma require geometry
// Shader Features
#pragma shader_feature_local _NORMALMAP
#pragma shader_feature_local _SHADOW_COLOR
#pragma shader_feature_local _MATCAP_ADD
#pragma shader_feature_local _MATCAP_MUL
#pragma shader_feature_local _RIMLIGHT
#pragma shader_feature_local _EMISSION
#pragma shader_feature_local _OCCLUSIONMAP
// URP Keywords
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile_fragment _ _SHADOWS_SOFT
#pragma multi_compile_fog
// GPU Instancing
#pragma multi_compile_instancing
#pragma instancing_options renderinglayer
#pragma vertex vert_fur
#pragma geometry geom_fur
#pragma fragment frag_fur
#define NILOTOON_FUR_SHELL_PASS
#include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl"
#include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Geometry.hlsl"
#include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Fragment.hlsl"
ENDHLSL
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pass 1.5: Fur Shell Mask Pass (Geometry Shader)
// Renders fur shells to mask buffer only (white where fur is rendered)
// Used for fur area detection in post-processing
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Pass
{
Name "FurShellMask"
Tags { "LightMode" = "NiloToonFurShellMask" }
Cull [_FurCull]
ZWrite Off
ZTest Always // Always pass - we want to mark ALL fur pixels regardless of depth
Blend One Zero // Just overwrite
HLSLPROGRAM
#pragma target 4.5
#pragma require geometry
// GPU Instancing
#pragma multi_compile_instancing
#pragma instancing_options renderinglayer
#pragma vertex vert_fur
#pragma geometry geom_fur
#pragma fragment frag_fur_mask
#define NILOTOON_FUR_SHELL_PASS
#define NILOTOON_FUR_MASK_PASS
#include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl"
#include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Geometry.hlsl"
#include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Fragment.hlsl"
ENDHLSL
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pass 2: Outline (NiloToon Style)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Pass
{
Name "Outline"
Tags { "LightMode" = "NiloToonOutline" }
Cull Front
ZWrite [_ZWrite]
ZTest [_ZTest]
HLSLPROGRAM
#pragma target 4.5
#pragma multi_compile_instancing
#pragma instancing_options renderinglayer
#pragma multi_compile_fog
#pragma vertex vert_outline
#pragma fragment frag_outline
#define NILOTOON_FUR_OUTLINE_PASS
#include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl"
Varyings vert_outline(Attributes input)
{
Varyings output = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
float outlineWidth = _OutlineWidth * 0.001;
float outlineMask = SAMPLE_TEXTURE2D_LOD(_OutlineWidthMask, sampler_OutlineWidthMask, input.uv, 0).r;
outlineWidth *= outlineMask;
// FOV-aware outline width
float fovFactor = unity_OrthoParams.w > 0.5 ? 1.0 : (2.0 * abs(UNITY_MATRIX_P[1][1]));
outlineWidth *= fovFactor;
float3 posOS = input.positionOS.xyz + input.normalOS * outlineWidth;
output.positionCS = TransformObjectToHClip(posOS);
output.uv = input.uv;
output.fogFactor = ComputeFogFactor(output.positionCS.z);
return output;
}
half4 frag_outline(Varyings input) : SV_Target
{
half4 color = _OutlineColor;
color.rgb = MixFog(color.rgb, input.fogFactor);
return color;
}
ENDHLSL
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pass 3: ShadowCaster
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Pass
{
Name "ShadowCaster"
Tags { "LightMode" = "ShadowCaster" }
ZWrite On
ZTest LEqual
ColorMask 0
Cull [_Cull]
HLSLPROGRAM
#pragma target 4.5
#pragma multi_compile_instancing
#pragma instancing_options renderinglayer
#pragma vertex ShadowPassVertex
#pragma fragment ShadowPassFragment
#define NILOTOON_FUR_SHADOW_PASS
#include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl"
float3 _LightDirection;
float3 _LightPosition;
float4 GetShadowPositionHClip(Attributes input)
{
float3 positionWS = TransformObjectToWorld(input.positionOS.xyz);
float3 normalWS = TransformObjectToWorldNormal(input.normalOS);
#if _CASTING_PUNCTUAL_LIGHT_SHADOW
float3 lightDirectionWS = normalize(_LightPosition - positionWS);
#else
float3 lightDirectionWS = _LightDirection;
#endif
float4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, lightDirectionWS));
#if UNITY_REVERSED_Z
positionCS.z = min(positionCS.z, UNITY_NEAR_CLIP_VALUE);
#else
positionCS.z = max(positionCS.z, UNITY_NEAR_CLIP_VALUE);
#endif
return positionCS;
}
Varyings ShadowPassVertex(Attributes input)
{
Varyings output = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
output.uv = input.uv;
output.positionCS = GetShadowPositionHClip(input);
return output;
}
half4 ShadowPassFragment(Varyings input) : SV_TARGET
{
half alpha = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv).a * _BaseColor.a;
clip(alpha - _Cutoff);
return 0;
}
ENDHLSL
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pass 4: DepthOnly
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Pass
{
Name "DepthOnly"
Tags { "LightMode" = "DepthOnly" }
ZWrite On
ColorMask R
Cull [_Cull]
HLSLPROGRAM
#pragma target 4.5
#pragma multi_compile_instancing
#pragma instancing_options renderinglayer
#pragma vertex DepthOnlyVertex
#pragma fragment DepthOnlyFragment
#define NILOTOON_FUR_DEPTH_PASS
#include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl"
Varyings DepthOnlyVertex(Attributes input)
{
Varyings output = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
output.uv = input.uv;
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
return output;
}
half DepthOnlyFragment(Varyings input) : SV_TARGET
{
half alpha = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv).a * _BaseColor.a;
clip(alpha - _Cutoff);
return input.positionCS.z;
}
ENDHLSL
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pass 5: DepthNormals
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Pass
{
Name "DepthNormals"
Tags { "LightMode" = "DepthNormals" }
ZWrite On
Cull [_Cull]
HLSLPROGRAM
#pragma target 4.5
#pragma multi_compile_instancing
#pragma instancing_options renderinglayer
#pragma vertex DepthNormalsVertex
#pragma fragment DepthNormalsFragment
#define NILOTOON_FUR_DEPTHNORMALS_PASS
#include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl"
Varyings DepthNormalsVertex(Attributes input)
{
Varyings output = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
output.uv = input.uv;
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
output.normalWS = TransformObjectToWorldNormal(input.normalOS);
return output;
}
float4 DepthNormalsFragment(Varyings input) : SV_TARGET
{
half alpha = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv).a * _BaseColor.a;
clip(alpha - _Cutoff);
return float4(normalize(input.normalWS) * 0.5 + 0.5, 0);
}
ENDHLSL
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pass 6: NiloToonPrepassBuffer (Base mesh only)
// For NiloToon Bloom / Tonemapping character area detection - base mesh
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Pass
{
Name "NiloToonPrepassBuffer"
Tags { "LightMode" = "NiloToonPrepassBuffer" }
ZWrite On
ZTest LEqual
Cull [_Cull]
HLSLPROGRAM
#pragma target 4.5
#pragma multi_compile_instancing
#pragma instancing_options renderinglayer
#pragma vertex PrepassBufferVertexBase
#pragma fragment PrepassBufferFragmentBase
#define NILOTOON_FUR_PREPASSBUFFER_PASS
#include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl"
Varyings PrepassBufferVertexBase(Attributes input)
{
Varyings output = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
output.uv = input.uv;
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
output.furLayer = -1; // Base mesh marker
return output;
}
float4 PrepassBufferFragmentBase(Varyings input) : SV_TARGET
{
UNITY_SETUP_INSTANCE_ID(input);
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
// Base mesh alpha clip
half alpha = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv).a * _BaseColor.a;
clip(alpha - _Cutoff);
// Output: character visible area = 1
return float4(0, 1, 0, 1);
}
ENDHLSL
}
// Note: Fur shell prepass is handled via MRT in the FurShellMRT pass
// When WriteToPrepassBuffer is enabled, FurShellMRT outputs to both:
// - SV_Target0: Main color buffer
// - SV_Target1: PrepassBuffer (character mask with jagged fur silhouette)
}
FallBack "Universal Render Pipeline/Lit"
CustomEditor "NiloToonURP.ShaderGUI.NiloToonCharacterFurShaderGUI"
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: d65d4a470e01cd947a07483a9bf34206
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 721b8d7aa96ce3243ad6e988775df5ee
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,333 @@
// NiloToon Character Fur - Fragment Shader
// Full-featured NiloToon style cel shading + Fur specific effects
#ifndef NILOTOON_CHARACTER_FUR_FRAGMENT_INCLUDED
#define NILOTOON_CHARACTER_FUR_FRAGMENT_INCLUDED
//------------------------------------------------------------------------------------------------------------------------------
// Get Normal from Normal Map
//------------------------------------------------------------------------------------------------------------------------------
half3 GetNormalFromMap(float2 uv, float3 normalWS, float4 tangentWS)
{
#if defined(_NORMALMAP)
half4 normalMap = SAMPLE_TEXTURE2D(_BumpMap, sampler_BumpMap, uv);
half3 normalTS = UnpackNormalWithScale(normalMap, _BumpScale);
float3 bitangent = cross(normalWS, tangentWS.xyz) * tangentWS.w;
float3x3 TBN = float3x3(tangentWS.xyz, bitangent, normalWS);
return normalize(mul(normalTS, TBN));
#else
return normalize(normalWS);
#endif
}
//------------------------------------------------------------------------------------------------------------------------------
// Apply All Effects (MatCap, Rim, Emission)
//------------------------------------------------------------------------------------------------------------------------------
half3 ApplyEffects(
half3 color,
float2 uv,
float3 normalWS,
float3 viewDirWS,
half3 lightColor,
half furLayer
)
{
// MatCap UV
float2 matCapUV = GetMatCapUV(normalWS, viewDirWS);
// MatCap Additive
#if defined(_MATCAP_ADD)
half matCapAddMask = SAMPLE_TEXTURE2D(_MatCapAddMask, sampler_MatCapAddMask, uv).r;
half3 matCapAdd = SAMPLE_TEXTURE2D(_MatCapAddMap, sampler_MatCapAddMap, matCapUV).rgb;
matCapAdd *= _MatCapAddColor.rgb * _MatCapAddIntensity * matCapAddMask;
color += matCapAdd;
#endif
// MatCap Multiply
#if defined(_MATCAP_MUL)
half3 matCapMul = SAMPLE_TEXTURE2D(_MatCapMulMap, sampler_MatCapMulMap, matCapUV).rgb;
color *= lerp(half3(1, 1, 1), matCapMul, _MatCapMulIntensity);
#endif
// Rim Light (General) - not applied to fur shells to avoid double rim
#if defined(_RIMLIGHT)
if (furLayer < 0) // Only for base pass
{
half NdotV = saturate(dot(normalWS, viewDirWS));
half rim = pow(1.0 - NdotV, _RimLightPower);
color += rim * _RimLightColor.rgb * _RimLightIntensity * lightColor;
}
#endif
// Emission
#if defined(_EMISSION)
half3 emission = SAMPLE_TEXTURE2D(_EmissionMap, sampler_EmissionMap, uv).rgb;
emission *= _EmissionColor.rgb * _EmissionIntensity;
color += emission;
#endif
return color;
}
//------------------------------------------------------------------------------------------------------------------------------
// Base Pass Fragment Shader
//------------------------------------------------------------------------------------------------------------------------------
#if defined(NILOTOON_FUR_BASE_PASS)
half4 frag(Varyings input) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(input);
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
// Sample base texture
half4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv);
half4 color = baseMap * _BaseColor;
// Alpha cutoff
clip(color.a - _Cutoff);
// Get normal (with normal map if enabled)
float3 normalWS = GetNormalFromMap(input.uv, input.normalWS, input.tangentWS);
// Get view direction
float3 viewDirWS = GetWorldSpaceViewDirSafe(input.positionWS);
// Get main light with NiloToon override support
Light mainLight = GetMainLightWithNiloToonOverride();
// Try to get shadow attenuation if shadow coord is valid
half shadowAttenuation = 1.0;
#if defined(_MAIN_LIGHT_SHADOWS) || defined(_MAIN_LIGHT_SHADOWS_CASCADE) || defined(_MAIN_LIGHT_SHADOWS_SCREEN)
shadowAttenuation = MainLightRealtimeShadow(input.shadowCoord);
#endif
mainLight.shadowAttenuation = shadowAttenuation;
// Occlusion
half occlusion = 1.0;
#if defined(_OCCLUSIONMAP)
occlusion = lerp(1.0, SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, input.uv).r, _OcclusionStrength);
#endif
// Apply NiloToon cel shading
color.rgb = ApplyNiloToonCelShading(
color.rgb,
mainLight.color,
mainLight.direction,
normalWS,
viewDirWS,
shadowAttenuation,
occlusion
);
// Apply additional effects
color.rgb = ApplyEffects(color.rgb, input.uv, normalWS, viewDirWS, mainLight.color, input.furLayer);
// Apply fog
color.rgb = MixFog(color.rgb, input.fogFactor);
return color;
}
#endif
//------------------------------------------------------------------------------------------------------------------------------
// Fur Shell Pass Fragment Shader
//------------------------------------------------------------------------------------------------------------------------------
#if defined(NILOTOON_FUR_SHELL_PASS)
// MRT output structure for simultaneous color + prepass buffer rendering
struct FurMRTOutput
{
half4 color : SV_Target0; // Main color buffer
half4 prepass : SV_Target1; // PrepassBuffer (character mask)
};
// Internal function to compute fur color (shared between standard and MRT versions)
half4 ComputeFurColor(Varyings input, out half furAlpha)
{
UNITY_SETUP_INSTANCE_ID(input);
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
// Sample base texture
half4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv);
half4 color = baseMap * _BaseColor;
// Get main light with NiloToon override support
Light mainLight = GetMainLightWithNiloToonOverride();
half3 lightColor = mainLight.color;
half3 lightDir = mainLight.direction;
// Sample fur noise mask for alpha (default white = full fur)
float2 furNoiseUV = input.uv * _FurNoiseMask_ST.xy + _FurNoiseMask_ST.zw;
half furNoise = SAMPLE_TEXTURE2D(_FurNoiseMask, sampler_FurNoiseMask, furNoiseUV).r;
// Sample fur mask (where fur appears, default white = everywhere)
float2 furMaskUV = input.uv * _FurMask_ST.xy + _FurMask_ST.zw;
half furMask = SAMPLE_TEXTURE2D(_FurMask, sampler_FurMask, furMaskUV).r;
// furLayer: 0 = root/base, 1 = tip
half furLayer = saturate(input.furLayer);
// Calculate fur alpha using lilToon-style non-linear curve
// This creates natural-looking fur tips instead of obvious hair cards
// furLayerShift with root offset adjustment (lilToon style)
// _FurRootOffset range is -1 to 0: -1 = hide roots completely, 0 = show all
half furLayerShift = furLayer - furLayer * _FurRootOffset + _FurRootOffset;
half furLayerAbs = abs(furLayerShift);
// Non-linear alpha curve: creates sharp tip cutoff
// Using cubic falloff for natural-looking fur tips
furAlpha = saturate(furNoise - furLayerShift * furLayerAbs * furLayerAbs * furLayerAbs + 0.25);
// Apply fur mask
furAlpha *= furMask;
// Minimum alpha threshold
clip(furAlpha - 0.05);
// Get normal (with normal map if enabled)
float3 normalWS = GetNormalFromMap(input.uv, input.normalWS, input.tangentWS);
// Get view direction
float3 viewDirWS = GetWorldSpaceViewDirSafe(input.positionWS);
// Occlusion
half occlusion = 1.0;
#if defined(_OCCLUSIONMAP)
occlusion = lerp(1.0, SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, input.uv).r, _OcclusionStrength);
#endif
// Cel shading
half NdotL = dot(normalWS, lightDir);
half halfLambert = NdotL * 0.5 + 0.5;
half celShadeResult = smoothstep(
_CelShadeMidPoint + 0.5 - _CelShadeSoftness,
_CelShadeMidPoint + 0.5 + _CelShadeSoftness,
halfLambert
);
celShadeResult *= occlusion;
#if defined(_SHADOW_COLOR)
// Apply HSV adjustment to shadow
half3 shadowAlbedo = ApplyHSVChange(
color.rgb,
_ShadowHueShift,
_ShadowSaturationBoost,
_ShadowValueMultiplier
);
shadowAlbedo *= _ShadowColor.rgb * _ShadowBrightness;
color.rgb = lerp(shadowAlbedo, color.rgb, celShadeResult);
#else
color.rgb = lerp(color.rgb * 0.5, color.rgb, celShadeResult);
#endif
// Apply light color
color.rgb *= lightColor;
// Apply fur ambient occlusion (lilToon style)
// Uses fwidth to reduce aliasing at layer boundaries
half furAOFactor = _FurAO * saturate(1.0 - fwidth(input.furLayer));
color.rgb *= furLayer * furAOFactor * 2.0 + 1.0 - furAOFactor;
// Apply fur rim lighting
half NdotV = abs(dot(normalWS, viewDirWS));
half rimFresnel = pow(saturate(1.0 - NdotV), _FurRimFresnelPower);
half antiLightFactor = lerp(1.0, 1.0 - Grayscale(lightColor), _FurRimAntiLight);
half3 rimColor = furLayer * rimFresnel * antiLightFactor * _FurRimColor.rgb;
color.rgb += rimColor;
// Apply additional effects (MatCap, Emission)
// MatCap UV
float2 matCapUV = GetMatCapUV(normalWS, viewDirWS);
#if defined(_MATCAP_ADD)
half matCapAddMask = SAMPLE_TEXTURE2D(_MatCapAddMask, sampler_MatCapAddMask, input.uv).r;
half3 matCapAdd = SAMPLE_TEXTURE2D(_MatCapAddMap, sampler_MatCapAddMap, matCapUV).rgb;
matCapAdd *= _MatCapAddColor.rgb * _MatCapAddIntensity * matCapAddMask * (1.0 - furLayer * 0.5);
color.rgb += matCapAdd;
#endif
#if defined(_MATCAP_MUL)
half3 matCapMul = SAMPLE_TEXTURE2D(_MatCapMulMap, sampler_MatCapMulMap, matCapUV).rgb;
color.rgb *= lerp(half3(1, 1, 1), matCapMul, _MatCapMulIntensity * (1.0 - furLayer * 0.5));
#endif
#if defined(_EMISSION)
half3 emission = SAMPLE_TEXTURE2D(_EmissionMap, sampler_EmissionMap, input.uv).rgb;
emission *= _EmissionColor.rgb * _EmissionIntensity * (1.0 - furLayer * 0.5);
color.rgb += emission;
#endif
// Apply fog
color.rgb = MixFog(color.rgb, input.fogFactor);
// Final alpha
color.a = furAlpha;
return color;
}
// Standard single render target version
half4 frag_fur(Varyings input) : SV_Target
{
half furAlpha;
return ComputeFurColor(input, furAlpha);
}
// MRT version: outputs to both color buffer and Fur Mask buffer simultaneously
// This creates the jagged mask effect where only actual rendered fur pixels are marked
FurMRTOutput frag_fur_mrt(Varyings input)
{
FurMRTOutput output;
half furAlpha;
output.color = ComputeFurColor(input, furAlpha);
// Write to Fur Mask Buffer (_NiloToonFurMaskTex):
// Write white (1,1,1,1) where fur pixels are rendered
// This creates a mask that shows exactly where fur is visible
output.prepass = half4(1, 1, 1, 1);
return output;
}
// Mask-only version: outputs to PrepassBuffer format
// R: face (not used by fur), G: character area (fur adds here), B: fur area only
// Used for two-pass approach (more compatible than MRT)
half4 frag_fur_mask(Varyings input) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(input);
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
// Sample fur noise mask for alpha (default white = full fur)
float2 furNoiseUV = input.uv * _FurNoiseMask_ST.xy + _FurNoiseMask_ST.zw;
half furNoise = SAMPLE_TEXTURE2D(_FurNoiseMask, sampler_FurNoiseMask, furNoiseUV).r;
// Sample fur mask (where fur appears, default white = everywhere)
float2 furMaskUV = input.uv * _FurMask_ST.xy + _FurMask_ST.zw;
half furMask = SAMPLE_TEXTURE2D(_FurMask, sampler_FurMask, furMaskUV).r;
// furLayer: 0 = root/base, 1 = tip
half furLayer = saturate(input.furLayer);
// Calculate fur alpha using lilToon-style non-linear curve
half furLayerShift = furLayer - furLayer * _FurRootOffset + _FurRootOffset;
half furLayerAbs = abs(furLayerShift);
half furAlpha = saturate(furNoise - furLayerShift * furLayerAbs * furLayerAbs * furLayerAbs + 0.25);
// Apply fur mask
furAlpha *= furMask;
// Clip pixels that don't pass threshold (same as main fur rendering)
clip(furAlpha - 0.05);
// Output to PrepassBuffer format:
// G channel = character visible area (unified mask for face, body, and fur)
// All character areas use G channel for consistent masking
return half4(0, 1, 0, 0);
}
#endif
#endif // NILOTOON_CHARACTER_FUR_FRAGMENT_INCLUDED

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 734309d3fd8d31246bde14f3278d4ee3
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,149 @@
// NiloToon Character Fur - Geometry Shader
// Shell-based fur generation (based on lilToon implementation)
#ifndef NILOTOON_CHARACTER_FUR_GEOMETRY_INCLUDED
#define NILOTOON_CHARACTER_FUR_GEOMETRY_INCLUDED
//------------------------------------------------------------------------------------------------------------------------------
// Barycentric Interpolation Helper
//------------------------------------------------------------------------------------------------------------------------------
float LerpBary(float a, float b, float c, float3 factor) { return a * factor.x + b * factor.y + c * factor.z; }
float2 LerpBary(float2 a, float2 b, float2 c, float3 factor) { return a * factor.x + b * factor.y + c * factor.z; }
float3 LerpBary(float3 a, float3 b, float3 c, float3 factor) { return a * factor.x + b * factor.y + c * factor.z; }
float4 LerpBary(float4 a, float4 b, float4 c, float3 factor) { return a * factor.x + b * factor.y + c * factor.z; }
//------------------------------------------------------------------------------------------------------------------------------
// Append Single Fur Shell (Base + Tip)
//------------------------------------------------------------------------------------------------------------------------------
void AppendFurShell(
inout TriangleStream<Varyings> outStream,
V2G input[3],
float3 furVectors[3],
float3 factor,
float layerProgress // 0 = base, 1 = outer
)
{
Varyings output = (Varyings)0;
UNITY_TRANSFER_INSTANCE_ID(input[0], output);
UNITY_TRANSFER_VERTEX_OUTPUT_STEREO(input[0], output);
// Interpolate base attributes
output.uv = LerpBary(input[0].uv, input[1].uv, input[2].uv, factor);
output.normalWS = normalize(LerpBary(input[0].normalWS, input[1].normalWS, input[2].normalWS, factor));
output.tangentWS = LerpBary(input[0].tangentWS, input[1].tangentWS, input[2].tangentWS, factor);
output.tangentWS.xyz = normalize(output.tangentWS.xyz);
output.fogFactor = LerpBary(input[0].fogFactor, input[1].fogFactor, input[2].fogFactor, factor);
// Base position (furLayer = 0)
float3 basePositionWS = LerpBary(input[0].positionWS, input[1].positionWS, input[2].positionWS, factor);
output.positionWS = basePositionWS;
output.positionCS = TransformWorldToHClip(basePositionWS);
output.furLayer = 0;
// Clipping canceller for near plane
#if defined(UNITY_REVERSED_Z)
if(output.positionCS.w < _ProjectionParams.y * 1.01 && output.positionCS.w > 0)
{
output.positionCS.z = output.positionCS.z * 0.0001 + output.positionCS.w * 0.999;
}
#else
if(output.positionCS.w < _ProjectionParams.y * 1.01 && output.positionCS.w > 0)
{
output.positionCS.z = output.positionCS.z * 0.0001 - output.positionCS.w * 0.999;
}
#endif
outStream.Append(output);
// Tip position (furLayer = 1)
float3 mixedFurVector = LerpBary(furVectors[0], furVectors[1], furVectors[2], factor);
float3 tipPositionWS = basePositionWS + mixedFurVector;
output.positionWS = tipPositionWS;
output.positionCS = TransformWorldToHClip(tipPositionWS);
output.furLayer = layerProgress;
// Clipping canceller for near plane
#if defined(UNITY_REVERSED_Z)
if(output.positionCS.w < _ProjectionParams.y * 1.01 && output.positionCS.w > 0)
{
output.positionCS.z = output.positionCS.z * 0.0001 + output.positionCS.w * 0.999;
}
#else
if(output.positionCS.w < _ProjectionParams.y * 1.01 && output.positionCS.w > 0)
{
output.positionCS.z = output.positionCS.z * 0.0001 - output.positionCS.w * 0.999;
}
#endif
outStream.Append(output);
}
//------------------------------------------------------------------------------------------------------------------------------
// Geometry Shader - Shell Generation
// Based on lilToon's fur geometry shader
//------------------------------------------------------------------------------------------------------------------------------
[maxvertexcount(40)]
void geom_fur(triangle V2G input[3], inout TriangleStream<Varyings> outStream)
{
UNITY_SETUP_INSTANCE_ID(input[0]);
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input[0]);
// Get fur vectors for each vertex
float3 furVectors[3];
furVectors[0] = input[0].furVector;
furVectors[1] = input[1].furVector;
furVectors[2] = input[2].furVector;
// Apply procedural randomization using vertex IDs (from lilToon)
uint3 n0 = (input[0].vertexID * 3 + input[1].vertexID * 1 + input[2].vertexID * 1) * uint3(1597334677U, 3812015801U, 2912667907U);
uint3 n1 = (input[0].vertexID * 1 + input[1].vertexID * 3 + input[2].vertexID * 1) * uint3(1597334677U, 3812015801U, 2912667907U);
uint3 n2 = (input[0].vertexID * 1 + input[1].vertexID * 1 + input[2].vertexID * 3) * uint3(1597334677U, 3812015801U, 2912667907U);
float3 noise0 = normalize(float3(n0) * (2.0 / float(0xffffffffU)) - 1.0);
float3 noise1 = normalize(float3(n1) * (2.0 / float(0xffffffffU)) - 1.0);
float3 noise2 = normalize(float3(n2) * (2.0 / float(0xffffffffU)) - 1.0);
furVectors[0] += noise0 * _FurVector.w * _FurRandomize;
furVectors[1] += noise1 * _FurVector.w * _FurRandomize;
furVectors[2] += noise2 * _FurVector.w * _FurRandomize;
// Generate shells based on layer count
// Layer pattern from lilToon: spreads shells across triangle vertices
if (_FurLayerNum >= 1)
{
// 3 shells at each vertex
AppendFurShell(outStream, input, furVectors, float3(1.0, 0.0, 0.0), 1.0);
AppendFurShell(outStream, input, furVectors, float3(0.0, 1.0, 0.0), 1.0);
AppendFurShell(outStream, input, furVectors, float3(0.0, 0.0, 1.0), 1.0);
}
if (_FurLayerNum >= 2)
{
// 3 more shells at edge midpoints
AppendFurShell(outStream, input, furVectors, float3(0.0, 0.5, 0.5), 1.0);
AppendFurShell(outStream, input, furVectors, float3(0.5, 0.0, 0.5), 1.0);
AppendFurShell(outStream, input, furVectors, float3(0.5, 0.5, 0.0), 1.0);
}
if (_FurLayerNum >= 3)
{
// 6 more shells for dense fur
AppendFurShell(outStream, input, furVectors, float3(1.0/6.0, 4.0/6.0, 1.0/6.0), 1.0);
AppendFurShell(outStream, input, furVectors, float3(0.0, 0.5, 0.5), 0.8);
AppendFurShell(outStream, input, furVectors, float3(1.0/6.0, 1.0/6.0, 4.0/6.0), 1.0);
AppendFurShell(outStream, input, furVectors, float3(0.5, 0.0, 0.5), 0.8);
AppendFurShell(outStream, input, furVectors, float3(4.0/6.0, 1.0/6.0, 1.0/6.0), 1.0);
AppendFurShell(outStream, input, furVectors, float3(0.5, 0.5, 0.0), 0.8);
}
// Final shell at first vertex to close the strip
AppendFurShell(outStream, input, furVectors, float3(1.0, 0.0, 0.0), 1.0);
outStream.RestartStrip();
}
#endif // NILOTOON_CHARACTER_FUR_GEOMETRY_INCLUDED

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 130ac8fd23814be49854157d7be9194e
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,440 @@
// NiloToon Character Fur - Shared Definitions
// Full-featured structs, CBUFFER, and common functions
#ifndef NILOTOON_CHARACTER_FUR_SHARED_INCLUDED
#define NILOTOON_CHARACTER_FUR_SHARED_INCLUDED
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
//------------------------------------------------------------------------------------------------------------------------------
// NiloToon Global Light Override Variables (from NiloToonCharacterMainLightOverrider)
//------------------------------------------------------------------------------------------------------------------------------
half4 _GlobalUserOverriddenFinalMainLightDirWSParam; // xyz: direction, w: 1 if enabled
half4 _GlobalUserOverriddenFinalMainLightColorParam; // rgb: color, w: 1 if enabled
//------------------------------------------------------------------------------------------------------------------------------
// Helper function for view direction (compatible with all URP versions)
//------------------------------------------------------------------------------------------------------------------------------
float3 GetWorldSpaceViewDirSafe(float3 positionWS)
{
return normalize(_WorldSpaceCameraPos.xyz - positionWS);
}
//------------------------------------------------------------------------------------------------------------------------------
// Helper function for shadow coord (compatible with all URP versions)
//------------------------------------------------------------------------------------------------------------------------------
float4 GetShadowCoordSafe(VertexPositionInputs vertexInput)
{
#if defined(_MAIN_LIGHT_SHADOWS_SCREEN) && !defined(_SURFACE_TYPE_TRANSPARENT)
return ComputeScreenPos(vertexInput.positionCS);
#else
return TransformWorldToShadowCoord(vertexInput.positionWS);
#endif
}
//------------------------------------------------------------------------------------------------------------------------------
// Get Main Light with NiloToon Override Support
//------------------------------------------------------------------------------------------------------------------------------
Light GetMainLightWithNiloToonOverride()
{
Light mainLight = GetMainLight();
// Apply NiloToon MainLightOverrider if enabled
if (_GlobalUserOverriddenFinalMainLightDirWSParam.w > 0.5)
{
mainLight.direction = _GlobalUserOverriddenFinalMainLightDirWSParam.xyz;
}
if (_GlobalUserOverriddenFinalMainLightColorParam.w > 0.5)
{
mainLight.color = _GlobalUserOverriddenFinalMainLightColorParam.rgb;
}
return mainLight;
}
//------------------------------------------------------------------------------------------------------------------------------
// CBUFFER for SRP Batcher
//------------------------------------------------------------------------------------------------------------------------------
CBUFFER_START(UnityPerMaterial)
// Base
float4 _BaseMap_ST;
half4 _BaseColor;
half _Cutoff;
// Normal Map
float4 _BumpMap_ST;
half _BumpScale;
// Fur Shape
float4 _FurNoiseMask_ST;
float4 _FurMask_ST;
float4 _FurLengthMask_ST;
float4 _FurVector;
float4 _FurVectorTex_ST;
half _FurVectorScale;
half _FurGravity;
half _FurRandomize;
half _FurAO;
half _FurLayerNum;
half _FurRootOffset;
// Fur Rim
half4 _FurRimColor;
half _FurRimFresnelPower;
half _FurRimAntiLight;
// Cel Shading
half _CelShadeMidPoint;
half _CelShadeSoftness;
// Shadow Color
half4 _ShadowColor;
half _ShadowBrightness;
half _ShadowHueShift;
half _ShadowSaturationBoost;
half _ShadowValueMultiplier;
// MatCap Add
float4 _MatCapAddMap_ST;
half4 _MatCapAddColor;
half _MatCapAddIntensity;
float4 _MatCapAddMask_ST;
// MatCap Multiply
float4 _MatCapMulMap_ST;
half _MatCapMulIntensity;
// Rim Light
half4 _RimLightColor;
half _RimLightPower;
half _RimLightIntensity;
// Emission
float4 _EmissionMap_ST;
half4 _EmissionColor;
half _EmissionIntensity;
// Occlusion
float4 _OcclusionMap_ST;
half _OcclusionStrength;
// Outline
half _OutlineWidth;
half4 _OutlineColor;
float4 _OutlineWidthMask_ST;
// Rendering
half _Cull;
half _FurCull;
CBUFFER_END
//------------------------------------------------------------------------------------------------------------------------------
// Texture Declarations (all textures declared unconditionally to avoid compilation issues)
//------------------------------------------------------------------------------------------------------------------------------
TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap);
TEXTURE2D(_BumpMap); SAMPLER(sampler_BumpMap);
// Fur textures
TEXTURE2D(_FurNoiseMask); SAMPLER(sampler_FurNoiseMask);
TEXTURE2D(_FurMask); SAMPLER(sampler_FurMask);
TEXTURE2D(_FurLengthMask); SAMPLER(sampler_FurLengthMask);
TEXTURE2D(_FurVectorTex); SAMPLER(sampler_FurVectorTex);
// MatCap
TEXTURE2D(_MatCapAddMap); SAMPLER(sampler_MatCapAddMap);
TEXTURE2D(_MatCapAddMask); SAMPLER(sampler_MatCapAddMask);
TEXTURE2D(_MatCapMulMap); SAMPLER(sampler_MatCapMulMap);
// Emission
TEXTURE2D(_EmissionMap); SAMPLER(sampler_EmissionMap);
// Occlusion
TEXTURE2D(_OcclusionMap); SAMPLER(sampler_OcclusionMap);
// Outline
TEXTURE2D(_OutlineWidthMask); SAMPLER(sampler_OutlineWidthMask);
//------------------------------------------------------------------------------------------------------------------------------
// Vertex Input Structure
//------------------------------------------------------------------------------------------------------------------------------
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float4 tangentOS : TANGENT;
float2 uv : TEXCOORD0;
float2 uv2 : TEXCOORD1;
float4 color : COLOR;
uint vertexID : SV_VertexID;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
//------------------------------------------------------------------------------------------------------------------------------
// Vertex to Geometry Structure (for Fur Shell Pass)
//------------------------------------------------------------------------------------------------------------------------------
struct V2G
{
float2 uv : TEXCOORD0;
float3 positionWS : TEXCOORD1;
float3 normalWS : TEXCOORD2;
float4 tangentWS : TEXCOORD3;
float3 furVector : TEXCOORD4;
float fogFactor : TEXCOORD5;
uint vertexID : TEXCOORD6;
float4 color : COLOR;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
//------------------------------------------------------------------------------------------------------------------------------
// Fragment Input Structure
//------------------------------------------------------------------------------------------------------------------------------
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
float3 positionWS : TEXCOORD1;
float3 normalWS : TEXCOORD2;
float4 tangentWS : TEXCOORD3;
float fogFactor : TEXCOORD4;
float furLayer : TEXCOORD5; // 0 = base, 1 = outer shell
float4 shadowCoord : TEXCOORD6;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
//------------------------------------------------------------------------------------------------------------------------------
// Color Space Conversion (RGB <-> HSV)
//------------------------------------------------------------------------------------------------------------------------------
float3 RGBToHSV(float3 rgb)
{
float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
float4 p = lerp(float4(rgb.bg, K.wz), float4(rgb.gb, K.xy), step(rgb.b, rgb.g));
float4 q = lerp(float4(p.xyw, rgb.r), float4(rgb.r, p.yzx), step(p.x, rgb.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
float3 HSVToRGB(float3 hsv)
{
float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
float3 p = abs(frac(hsv.xxx + K.xyz) * 6.0 - K.www);
return hsv.z * lerp(K.xxx, saturate(p - K.xxx), hsv.y);
}
//------------------------------------------------------------------------------------------------------------------------------
// Utility: Grayscale
//------------------------------------------------------------------------------------------------------------------------------
half Grayscale(half3 color)
{
return dot(color, half3(0.299, 0.587, 0.114));
}
//------------------------------------------------------------------------------------------------------------------------------
// Normal Map Unpacking
//------------------------------------------------------------------------------------------------------------------------------
half3 UnpackNormalWithScale(half4 packedNormal, half scale)
{
#if defined(UNITY_NO_DXT5nm)
half3 normal = packedNormal.xyz * 2.0 - 1.0;
#else
half3 normal;
normal.xy = packedNormal.ag * 2.0 - 1.0;
normal.z = sqrt(1.0 - saturate(dot(normal.xy, normal.xy)));
#endif
normal.xy *= scale;
return normalize(normal);
}
//------------------------------------------------------------------------------------------------------------------------------
// MatCap UV Calculation
//------------------------------------------------------------------------------------------------------------------------------
float2 GetMatCapUV(float3 normalWS, float3 viewDirWS)
{
float3 viewNormal = mul((float3x3)UNITY_MATRIX_V, normalWS);
return viewNormal.xy * 0.5 + 0.5;
}
//------------------------------------------------------------------------------------------------------------------------------
// Apply HSV Change to Color
//------------------------------------------------------------------------------------------------------------------------------
half3 ApplyHSVChange(half3 color, half hueOffset, half saturationBoost, half valueMul)
{
float3 hsv = RGBToHSV(color);
hsv.x = frac(hsv.x + hueOffset);
hsv.y = saturate(hsv.y * saturationBoost);
hsv.z = hsv.z * valueMul;
return HSVToRGB(hsv);
}
//------------------------------------------------------------------------------------------------------------------------------
// NiloToon Style Cel Shading (Full Version)
//------------------------------------------------------------------------------------------------------------------------------
half3 ApplyNiloToonCelShading(
half3 baseColor,
half3 lightColor,
half3 lightDir,
half3 normalWS,
half3 viewDir,
half shadowAttenuation,
half occlusion
)
{
// Calculate NdotL
half NdotL = dot(normalWS, lightDir);
// Cel shading with configurable mid point and softness
half halfLambert = NdotL * 0.5 + 0.5;
half celShadeResult = smoothstep(
_CelShadeMidPoint + 0.5 - _CelShadeSoftness,
_CelShadeMidPoint + 0.5 + _CelShadeSoftness,
halfLambert
);
// Apply shadow map attenuation
celShadeResult *= shadowAttenuation;
// Apply occlusion
celShadeResult *= occlusion;
half3 finalColor = baseColor;
#if defined(_SHADOW_COLOR)
// Apply HSV adjustment to shadow
half3 shadowAlbedo = ApplyHSVChange(
baseColor,
_ShadowHueShift,
_ShadowSaturationBoost,
_ShadowValueMultiplier
);
// Apply shadow tint and brightness
shadowAlbedo *= _ShadowColor.rgb * _ShadowBrightness;
// Blend between shadow and lit based on cel shade result
finalColor = lerp(shadowAlbedo, baseColor, celShadeResult);
#else
// Simple brightness reduction in shadow
finalColor = lerp(baseColor * 0.5, baseColor, celShadeResult);
#endif
// Apply light color
finalColor *= lightColor;
return finalColor;
}
//------------------------------------------------------------------------------------------------------------------------------
// Fur Vector Calculation (from lilToon)
//------------------------------------------------------------------------------------------------------------------------------
float3 CalculateFurVector(float3 normalOS, float4 tangentOS, float2 uv, float4 vertexColor, bool useVertexColor)
{
// Build TBN matrix
float3 bitangentOS = normalize(cross(normalOS, tangentOS.xyz)) * (tangentOS.w * length(normalOS));
float3x3 tbnOS = float3x3(tangentOS.xyz, bitangentOS, normalOS);
// Start with base fur vector
float3 furVector = _FurVector.xyz + float3(0, 0, 0.001);
// Optional: blend with vertex color
if (useVertexColor)
{
float3 vertexNormal = vertexColor.xyz * 2.0 - 1.0;
furVector = normalize(furVector + vertexNormal);
}
// Transform to tangent space, then apply fur direction texture
#if defined(NILOTOON_FUR_SHELL_PASS)
float4 furDirTex = SAMPLE_TEXTURE2D_LOD(_FurVectorTex, sampler_FurVectorTex, uv * _FurVectorTex_ST.xy + _FurVectorTex_ST.zw, 0);
float3 furDirFromTex = UnpackNormalWithScale(furDirTex, _FurVectorScale);
furVector = normalize(furVector + furDirFromTex);
#endif
// Transform to object space direction
furVector = mul(normalize(furVector), tbnOS);
furVector *= _FurVector.w;
// Apply gravity
float furLength = length(furVector);
furVector.y -= _FurGravity * furLength;
return furVector;
}
//------------------------------------------------------------------------------------------------------------------------------
// Base Pass Vertex Shader
//------------------------------------------------------------------------------------------------------------------------------
#if defined(NILOTOON_FUR_BASE_PASS)
Varyings vert(Attributes input)
{
Varyings output = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
// Transform
VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS);
output.positionCS = vertexInput.positionCS;
output.positionWS = vertexInput.positionWS;
output.normalWS = normalInput.normalWS;
output.tangentWS = float4(normalInput.tangentWS, input.tangentOS.w);
output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
// Shadow coord
output.shadowCoord = GetShadowCoordSafe(vertexInput);
// Fog
output.fogFactor = ComputeFogFactor(vertexInput.positionCS.z);
// Base pass has no fur layer
output.furLayer = -1.0;
return output;
}
#endif
//------------------------------------------------------------------------------------------------------------------------------
// Fur Pass Vertex Shader (outputs to Geometry Shader)
//------------------------------------------------------------------------------------------------------------------------------
#if defined(NILOTOON_FUR_SHELL_PASS)
V2G vert_fur(Attributes input)
{
V2G output = (V2G)0;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
// Transform position and normal to world space
VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS);
output.positionWS = TransformObjectToWorld(input.positionOS.xyz);
output.normalWS = normalInput.normalWS;
output.tangentWS = float4(normalInput.tangentWS, input.tangentOS.w);
output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
output.color = input.color;
output.vertexID = input.vertexID;
// Calculate fur vector in world space
float3 furVectorOS = CalculateFurVector(input.normalOS, input.tangentOS, input.uv, input.color, false);
output.furVector = TransformObjectToWorldDir(furVectorOS, false);
// Apply fur length mask
float furLengthMask = SAMPLE_TEXTURE2D_LOD(_FurLengthMask, sampler_FurLengthMask, input.uv * _FurLengthMask_ST.xy + _FurLengthMask_ST.zw, 0).r;
output.furVector *= furLengthMask;
// Fog
float4 posCS = TransformWorldToHClip(output.positionWS);
output.fogFactor = ComputeFogFactor(posCS.z);
return output;
}
#endif
#endif // NILOTOON_CHARACTER_FUR_SHARED_INCLUDED

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 433eaffdfbbdaff41b9794f5adc9878a
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,156 @@
// NiloToon Character Fur Shell Only Shader
// Use this shader on a second material slot to render fur shells
// The base mesh should use NiloToonCharacter or NiloToonCharacterFur shader
Shader "Universal Render Pipeline/NiloToon/NiloToon_Character_Fur_ShellOnly"
{
Properties
{
[Header(Base Color)]
[Space(5)]
[MainTexture] _BaseMap("Base Map", 2D) = "white" {}
[HDR][MainColor] _BaseColor("Base Color", Color) = (1,1,1,1)
_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
[Header(Normal Map)]
[Space(5)]
[Toggle(_NORMALMAP)] _UseNormalMap("Enable Normal Map", Float) = 0
[Normal] _BumpMap("Normal Map", 2D) = "bump" {}
_BumpScale("Normal Scale", Range(0, 2)) = 1.0
[Header(Fur Shape)]
[Space(5)]
_FurNoiseMask("Fur Noise Mask", 2D) = "white" {}
_FurMask("Fur Mask (where fur appears)", 2D) = "white" {}
_FurLengthMask("Fur Length Mask", 2D) = "white" {}
_FurVector("Fur Direction (XYZ) + Length (W)", Vector) = (0, 0, 1, 0.02)
[Normal] _FurVectorTex("Fur Direction Map (Normal)", 2D) = "bump" {}
_FurVectorScale("Fur Direction Scale", Range(-10, 10)) = 1.0
_FurGravity("Fur Gravity", Range(0, 1)) = 0.25
_FurRandomize("Fur Randomize", Range(0, 1)) = 0.1
_FurAO("Fur Ambient Occlusion", Range(0, 1)) = 0.5
[Header(Shell Layers)]
[Space(5)]
[IntRange] _FurLayerNum("Fur Layer Count", Range(1, 3)) = 2
_FurRootOffset("Fur Root Offset", Range(-1, 0)) = 0
[Header(Fur Rim Light)]
[Space(5)]
[HDR] _FurRimColor("Fur Rim Color", Color) = (1, 1, 1, 1)
_FurRimFresnelPower("Fur Rim Fresnel Power", Range(0.01, 50)) = 3.0
_FurRimAntiLight("Fur Rim Anti-Light", Range(0, 1)) = 0.5
[Header(Cel Shading)]
[Space(5)]
_CelShadeMidPoint("Cel Shade Mid Point", Range(-1, 1)) = 0
_CelShadeSoftness("Cel Shade Softness", Range(0, 1)) = 0.1
[Header(Shadow Color)]
[Space(5)]
[Toggle(_SHADOW_COLOR)] _EnableShadowColor("Enable Shadow Color", Float) = 0
[HDR] _ShadowColor("Shadow Tint Color", Color) = (1, 1, 1, 1)
_ShadowBrightness("Shadow Brightness", Range(0, 2)) = 1.0
_ShadowHueShift("Shadow Hue Shift", Range(-0.5, 0.5)) = 0
_ShadowSaturationBoost("Shadow Saturation Boost", Range(0, 2)) = 1.0
_ShadowValueMultiplier("Shadow Value Multiplier", Range(0, 2)) = 1.0
[Header(MatCap Additive)]
[Space(5)]
[Toggle(_MATCAP_ADD)] _UseMatCapAdd("Enable MatCap (Add)", Float) = 0
_MatCapAddMap("MatCap Add Map", 2D) = "black" {}
[HDR] _MatCapAddColor("MatCap Add Color", Color) = (1, 1, 1, 1)
_MatCapAddIntensity("MatCap Add Intensity", Range(0, 5)) = 1.0
_MatCapAddMask("MatCap Add Mask", 2D) = "white" {}
[Header(MatCap Multiply)]
[Space(5)]
[Toggle(_MATCAP_MUL)] _UseMatCapMul("Enable MatCap (Multiply)", Float) = 0
_MatCapMulMap("MatCap Multiply Map", 2D) = "white" {}
_MatCapMulIntensity("MatCap Multiply Intensity", Range(0, 2)) = 1.0
[Header(Emission)]
[Space(5)]
[Toggle(_EMISSION)] _UseEmission("Enable Emission", Float) = 0
_EmissionMap("Emission Map", 2D) = "white" {}
[HDR] _EmissionColor("Emission Color", Color) = (0, 0, 0, 1)
_EmissionIntensity("Emission Intensity", Range(0, 10)) = 1.0
[Header(Occlusion)]
[Space(5)]
[Toggle(_OCCLUSIONMAP)] _UseOcclusion("Enable Occlusion Map", Float) = 0
_OcclusionMap("Occlusion Map", 2D) = "white" {}
_OcclusionStrength("Occlusion Strength", Range(0, 1)) = 1.0
[Header(Outline)]
[Space(5)]
_OutlineWidth("Outline Width", Range(0, 10)) = 0
[HDR] _OutlineColor("Outline Color", Color) = (0, 0, 0, 1)
_OutlineWidthMask("Outline Width Mask", 2D) = "white" {}
[Header(Rendering Options)]
[Space(5)]
[Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull Mode", Float) = 2
[Enum(UnityEngine.Rendering.CullMode)] _FurCull("Fur Cull Mode", Float) = 0
[Enum(Off, 0, On, 1)] _ZWrite("ZWrite", Float) = 0
_FurZWrite("Fur ZWrite", Float) = 0
[Enum(UnityEngine.Rendering.CompareFunction)] _ZTest("ZTest", Float) = 4
}
SubShader
{
Tags
{
"RenderType" = "Transparent"
"RenderPipeline" = "UniversalPipeline"
"IgnoreProjector" = "True"
"Queue" = "Transparent-50"
}
LOD 300
// Fur Shell Pass
Pass
{
Name "FurShell"
Tags { "LightMode" = "UniversalForward" }
Cull [_FurCull]
ZWrite [_FurZWrite]
ZTest LEqual
Blend SrcAlpha OneMinusSrcAlpha, One OneMinusSrcAlpha
HLSLPROGRAM
#pragma target 4.5
#pragma require geometry
#pragma shader_feature_local _NORMALMAP
#pragma shader_feature_local _SHADOW_COLOR
#pragma shader_feature_local _MATCAP_ADD
#pragma shader_feature_local _MATCAP_MUL
#pragma shader_feature_local _RIMLIGHT
#pragma shader_feature_local _EMISSION
#pragma shader_feature_local _OCCLUSIONMAP
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile_fragment _ _SHADOWS_SOFT
#pragma multi_compile_fog
#pragma multi_compile_instancing
#pragma instancing_options renderinglayer
#pragma vertex vert_fur
#pragma geometry geom_fur
#pragma fragment frag_fur
#define NILOTOON_FUR_SHELL_PASS
#include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Shared.hlsl"
#include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Geometry.hlsl"
#include "NiloToonCharacterFur_HLSL/NiloToonCharacterFur_Fragment.hlsl"
ENDHLSL
}
}
FallBack Off
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 9b3349963a9dea04da1b88df137b8d82
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant: