Add toon shader with outline for pose icon stage

Cel-shaded white body with tinted shadow band plus inverted-hull
clip-space outline (constant screen width, works in deferred via
SRPDefaultUnlit). Key light added to the Pose Icon scene, material
wired to the Paintman.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Fable 5 committed 2026-07-12 18:59:15 +02:00
1 parent ed1d171ed3
commit 2de153d8fe
5 files changed
+340 -14

No files matched your search

+40
View File
@@ -0,0 +1,40 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Pose Icon Toon
m_Shader: {fileID: 4800000, guid: 4feab3af8d45cb545a6f4f9b7bdb6f2d, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _OutlineWidth: 6
- _ShadeSoftness: 0.05
- _ShadeThreshold: 0.5
m_Colors:
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _OutlineColor: {r: 0.09999997, g: 0.079999976, b: 0.19999996, a: 1}
- _ShadowTint: {r: 0.54999995, g: 0.5, b: 0.65, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 662451554d7a52d4baa06298116a9b6a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
+142 -14
View File
@@ -156,7 +156,7 @@ Camera:
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 2
m_BackGroundColor: {r: 0.40251565, g: 0.10759059, b: 0.32042146, a: 0}
m_BackGroundColor: {r: 1, g: 0, b: 1, a: 0}
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
@@ -278,7 +278,7 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: c2044ff1352a6bb4586d6d04b59012b7, type: 3}
propertyPath: m_LocalRotation.w
value: 1
value: 0.9647873
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: c2044ff1352a6bb4586d6d04b59012b7, type: 3}
propertyPath: m_LocalRotation.x
@@ -286,7 +286,7 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: c2044ff1352a6bb4586d6d04b59012b7, type: 3}
propertyPath: m_LocalRotation.y
value: 0
value: 0.2630312
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: c2044ff1352a6bb4586d6d04b59012b7, type: 3}
propertyPath: m_LocalRotation.z
@@ -827,7 +827,7 @@ PrefabInstance:
- target: {fileID: 5804137883901333613, guid: c2044ff1352a6bb4586d6d04b59012b7, type: 3}
propertyPath: 'm_Materials.Array.data[0]'
value:
objectReference: {fileID: 2100000, guid: 9d915c00589d8b544a529437f76867fd, type: 2}
objectReference: {fileID: 2100000, guid: 662451554d7a52d4baa06298116a9b6a, type: 2}
- target: {fileID: 5866666021909216657, guid: c2044ff1352a6bb4586d6d04b59012b7, type: 3}
propertyPath: m_Avatar
value:
@@ -982,26 +982,154 @@ MonoBehaviour:
entries:
- clip: {fileID: 7400000, guid: 18925b0fe0029224aa6d9f4d9ca9ab72, type: 2}
position: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 30.5, z: 0}
- clip: {fileID: 7400000, guid: b4b88b2d2f749a94fbcdc7c0447cd73c, type: 2}
position: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 30.5, z: 0}
- clip: {fileID: 7400000, guid: 8080490af5235b043abfca704be7ff8d, type: 2}
position: {x: 0, y: 0.48, z: 0}
rotation: {x: 30.93, y: -21.5, z: -12.3}
position: {x: 0.06, y: 0.33, z: 0}
rotation: {x: 20.1, y: -153.5, z: 9.9}
- clip: {fileID: 7400000, guid: 7686660013aff7f478d9a6fd61196957, type: 2}
position: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 0, z: 0}
position: {x: 0, y: 0.46, z: 0}
rotation: {x: -11.5, y: 0, z: 0}
- clip: {fileID: 7400000, guid: f8de430ad75135743b87456111e7abac, type: 2}
position: {x: 0, y: 0.4, z: 0}
rotation: {x: 26.42, y: -36.62, z: 0}
position: {x: 0, y: 0.5, z: 0}
rotation: {x: 21.2, y: -133.3, z: 11.56}
- clip: {fileID: 7400000, guid: 933a7fdefa4f6974b9ed61bd940da6ed, type: 2}
position: {x: 0.08, y: 0.7, z: 0}
rotation: {x: 43.1, y: -24.67, z: 0}
position: {x: 0.09, y: 0.7, z: 0}
rotation: {x: 24.4, y: -144.8, z: 0}
currentIndex: 0
--- !u!1 &1708736866
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1708736868}
- component: {fileID: 1708736867}
- component: {fileID: 1708736869}
m_Layer: 0
m_Name: Directional Light
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!108 &1708736867
Light:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1708736866}
m_Enabled: 1
serializedVersion: 12
m_Type: 1
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_Intensity: 1.1
m_Range: 10
m_SpotAngle: 30
m_InnerSpotAngle: 21.80208
m_CookieSize2D: {x: 0.5, y: 0.5}
m_Shadows:
m_Type: 0
m_Resolution: -1
m_CustomResolution: -1
m_Strength: 1
m_Bias: 0.05
m_NormalBias: 0.4
m_NearPlane: 0.2
m_CullingMatrixOverride:
e00: 1
e01: 0
e02: 0
e03: 0
e10: 0
e11: 1
e12: 0
e13: 0
e20: 0
e21: 0
e22: 1
e23: 0
e30: 0
e31: 0
e32: 0
e33: 1
m_UseCullingMatrixOverride: 0
m_Cookie: {fileID: 0}
m_DrawHalo: 0
m_Flare: {fileID: 0}
m_RenderMode: 0
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingLayerMask: 1
m_Lightmapping: 4
m_LightShadowCasterMode: 0
m_AreaSize: {x: 1, y: 1}
m_BounceIntensity: 1
m_ColorTemperature: 6570
m_UseColorTemperature: 0
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
m_UseBoundingSphereOverride: 0
m_UseViewFrustumForShadowCasterCull: 1
m_ForceVisible: 0
m_ShadowRadius: 0
m_ShadowAngle: 0
m_LightUnit: 1
m_LuxAtDistance: 1
m_EnableSpotReflector: 1
--- !u!4 &1708736868
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1708736866}
serializedVersion: 2
m_LocalRotation: {x: 0.3339129, y: -0.20338672, z: 0.07402671, w: 0.9174182}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1708736869
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1708736866}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalAdditionalLightData
m_UsePipelineSettings: 1
m_AdditionalLightsShadowResolutionTier: 2
m_CustomShadowLayers: 0
m_LightCookieSize: {x: 1, y: 1}
m_LightCookieOffset: {x: 0, y: 0}
m_SoftShadowQuality: 0
m_RenderingLayersMask:
serializedVersion: 0
m_Bits: 1
m_ShadowRenderingLayersMask:
serializedVersion: 0
m_Bits: 1
m_Version: 4
m_LightLayerMask: 1
m_ShadowLayerMask: 1
m_RenderingLayers: 1
m_ShadowRenderingLayers: 1
--- !u!1660057539 &9223372036854775807
SceneRoots:
m_ObjectHideFlags: 0
m_Roots:
- {fileID: 843504939}
- {fileID: 1331925571}
- {fileID: 1708736868}
+141
View File
@@ -0,0 +1,141 @@
// Minimal cel/toon shader with an inverted-hull outline for the Pose Icon stage
// (icon screenshots of the staged Paintman). Deliberately NOT a URP/Lit clone:
// the stage is a controlled studio setup, so main-light cel banding + ambient is
// all the lighting it needs. Outline runs as a second pass (SRPDefaultUnlit, so
// URP renders it without a Render Feature); width is screen-space (pixel-ish)
// and distance-independent for consistent icon thickness.
Shader "Custom/PoseIconToon"
{
Properties
{
[MainTexture] _BaseMap("Albedo", 2D) = "white" {}
[MainColor] _BaseColor("Color", Color) = (1,1,1,1)
[Header(Toon Shading)]
_ShadeThreshold("Shade Threshold", Range(0.0, 1.0)) = 0.5
_ShadeSoftness("Shade Softness", Range(0.001, 0.5)) = 0.05
_ShadowTint("Shadow Tint", Color) = (0.55, 0.5, 0.65, 1)
[Header(Outline)]
_OutlineColor("Outline Color", Color) = (0.1, 0.08, 0.2, 1)
_OutlineWidth("Outline Width (px at 1080p)", Range(0.0, 20.0)) = 6.0
}
SubShader
{
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" "Queue" = "Geometry" }
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
half4 _BaseColor;
half _ShadeThreshold;
half _ShadeSoftness;
half4 _ShadowTint;
half4 _OutlineColor;
half _OutlineWidth;
CBUFFER_END
ENDHLSL
Pass
{
Name "ToonForward"
// UniversalForwardOnly (not UniversalForward): drawn by BOTH the Forward and the
// Deferred renderer (the deferred path only picks up ForwardOnly/SRPDefaultUnlit
// passes for materials without a GBuffer pass - plain UniversalForward is skipped).
Tags { "LightMode" = "UniversalForwardOnly" }
HLSLPROGRAM
#pragma vertex ToonVertex
#pragma fragment ToonFragment
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float2 uv : TEXCOORD0;
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
float3 normalWS : TEXCOORD1;
};
Varyings ToonVertex(Attributes input)
{
Varyings output;
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
output.normalWS = TransformObjectToWorldNormal(input.normalOS);
output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
return output;
}
half4 ToonFragment(Varyings input) : SV_Target
{
half4 albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv) * _BaseColor;
Light mainLight = GetMainLight();
half ndl = dot(normalize(input.normalWS), mainLight.direction) * 0.5h + 0.5h;
half lit = smoothstep(_ShadeThreshold - _ShadeSoftness, _ShadeThreshold + _ShadeSoftness, ndl);
half3 litColor = albedo.rgb * mainLight.color;
half3 shadeColor = albedo.rgb * _ShadowTint.rgb;
half3 ambient = albedo.rgb * SampleSH(input.normalWS) * 0.25h;
return half4(lerp(shadeColor, litColor, lit) + ambient, albedo.a);
}
ENDHLSL
}
Pass
{
Name "Outline"
Tags { "LightMode" = "SRPDefaultUnlit" }
Cull Front
HLSLPROGRAM
#pragma vertex OutlineVertex
#pragma fragment OutlineFragment
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
};
struct Varyings
{
float4 positionCS : SV_POSITION;
};
Varyings OutlineVertex(Attributes input)
{
Varyings output;
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
// Extrude in clip space: constant on-screen width regardless of distance.
float3 normalWS = TransformObjectToWorldNormal(input.normalOS);
float3 normalCS = normalize(mul((float3x3)UNITY_MATRIX_VP, normalWS).xyz);
float2 offset = normalCS.xy * _OutlineWidth * 2.0 / 1080.0 * output.positionCS.w;
offset.x *= _ScreenParams.y / _ScreenParams.x; // keep width uniform on non-square aspect
output.positionCS.xy += offset;
return output;
}
half4 OutlineFragment(Varyings input) : SV_Target
{
return _OutlineColor;
}
ENDHLSL
}
}
}
+9
View File
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 4feab3af8d45cb545a6f4f9b7bdb6f2d
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant: