ADD : 퍼 쉐이더 추가
This commit is contained in:
parent
52d9e9f548
commit
5a559d888e
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ad6027c1c29b71488586df1e8150fa7
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4076e5ecd1634a74eaabed924c9a971c
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d398264d435e9314bae19d3e5532f775
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6edb03bf5b2c68d4dbc8b39fac860935
|
||||
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
566
Assets/NiloToonURP/Shaders/NiloToonCharacterFur.shader
Normal file
566
Assets/NiloToonURP/Shaders/NiloToonCharacterFur.shader
Normal 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"
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d65d4a470e01cd947a07483a9bf34206
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 721b8d7aa96ce3243ad6e988775df5ee
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 734309d3fd8d31246bde14f3278d4ee3
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 130ac8fd23814be49854157d7be9194e
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 433eaffdfbbdaff41b9794f5adc9878a
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
156
Assets/NiloToonURP/Shaders/NiloToonCharacterFur_ShellOnly.shader
Normal file
156
Assets/NiloToonURP/Shaders/NiloToonCharacterFur_ShellOnly.shader
Normal 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
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b3349963a9dea04da1b88df137b8d82
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Loading…
x
Reference in New Issue
Block a user