https://roystan.net/articles/toon-shader
Shader "URP/Unlit/URP_ToonShader" { Properties { [MainTexture] _BaseMap("Texture", 2D) = "white" {} [MainColor] _BaseColor("Color", Color) = (1, 1, 1, 1) [NormalMap] _NormalMap("Normal Map", 2D) = "bump" {} [HDR] _AmbientColor("Ambient Color", Color) = (0.4, 0.4, 0.4, 1) [HDR] _SpecularColor("Specular Color", Color) = (0.9, 0.9, 0.9, 1) _Glossiness("Glossiness", Float) = 32 [HDR] _RimColor("Rim Color", Color) = (1, 1, 1, 1) _RimAmount("Rim Amount", Range(0, 1)) = 0.5 _RimThreshold("Rim Threshold", Range(0, 1)) = 0.1 [OcclusionMap] _OcclusionMap("Occlusion Map", 2D) = "white" {} _OcclusionStrength("Occlusion Strength", Range(0, 1)) = 1.0 [Toggle] _EnableEmission("Enable Emission", Float) = 0 [MainTexture] _EmissionMap("Emission Map", 2D) = "black" {} [HDR] _EmissionColor("Emission Color", Color) = (1, 1, 1, 1) _EmissionIntensity("Emission Intensity", Float) = 1.0 } SubShader { Tags { "RenderType" = "Oqueue" "Queue" = "Geometry" "RenderPipeline" = "UniversalPipeline" } Pass { Name "URP_ToonShader" Tags { "LightMode" = "UniversalForward" } HLSLPROGRAM #pragma target 4.5 #pragma vertex vert #pragma fragment frag #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" CBUFFER_START(UnityPerMaterial) float4 _BaseMap_ST; float4 _BaseColor; float4 _AmbientColor; float4 _SpecularColor; float _Glossiness; float4 _RimColor; float _RimAmount; float _RimThreshold; float4 _OcclusionMap_ST; float _OcclusionStrength; float _EnableEmission; float4 _EmissionColor; float _EmissionIntensity; CBUFFER_END TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap); TEXTURE2D(_NormalMap); SAMPLER(sampler_NormalMap); TEXTURE2D(_OcclusionMap); SAMPLER(sampler_OcclusionMap); TEXTURE2D(_EmissionMap); SAMPLER(sampler_EmissionMap); struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; float2 uv : TEXCOORD0; float4 tangentOS : TANGENT; }; struct Varyings { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; float3 normalWS : TEXCOORD1; float3 tangentWS : TEXCOORD2; float3 bitangentWS : TEXCOORD3; float3 lightDir : TEXCOORD4; float3 viewDir : TEXCOORD5; // 그림자 관련 변수 float4 shadowCoord : TEXCOORD6; }; Varyings vert(Attributes IN) { Varyings OUT = (Varyings)0; OUT.positionCS = TransformObjectToHClip(IN.positionOS); OUT.normalWS = TransformObjectToWorldNormal(IN.normalOS); OUT.tangentWS = TransformObjectToWorldNormal(IN.tangentOS.xyz); OUT.bitangentWS = cross(OUT.normalWS, OUT.tangentWS) * IN.tangentOS.w; OUT.lightDir = normalize(_MainLightPosition.xyz); float3 positionWS = TransformObjectToWorld(IN.positionOS.xyz); OUT.viewDir = normalize(_WorldSpaceCameraPos.xyz - positionWS); OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap); VertexPositionInputs positions = GetVertexPositionInputs(IN.positionOS.xyz); OUT.shadowCoord = GetShadowCoord(positions); return OUT; } float3 GetNormal(Varyings IN) { float3 normalTS = UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, IN.uv)); float3x3 TBN = float3x3(IN.tangentWS, IN.bitangentWS, IN.normalWS); return normalize(mul(normalTS, TBN)); } float4 frag(Varyings IN) : SV_Target { float4 col = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv) * _BaseColor; // 오클루전 맵 샘플링 float occlusion = SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, IN.uv).r; occlusion = lerp(1.0, occlusion, _OcclusionStrength); float3 normal = GetNormal(IN); float3 viewDir = normalize(IN.viewDir); // 그림자 계산 float shadow = MainLightRealtimeShadow(IN.shadowCoord); float NdotL = saturate(dot(normal, IN.lightDir)); float lightIntensity = smoothstep(0.0, 0.01, NdotL) * shadow; // 그림자 적용 float4 light = lightIntensity * _MainLightColor * occlusion; float3 halfVector = normalize(IN.lightDir + viewDir); float NdotH = saturate(dot(normal, halfVector)); float specularIntensity = pow(NdotH, _Glossiness * _Glossiness); float specularIntensitySmooth = smoothstep(0.005, 0.01, specularIntensity); float4 specular = specularIntensitySmooth * _SpecularColor * occlusion; float rimDot = 1.0 - saturate(dot(viewDir, normal)); float rimIntensity = rimDot * pow(NdotL, _RimThreshold); rimIntensity = smoothstep(_RimAmount - 0.01, _RimAmount + 0.01, rimIntensity); float4 rim = rimIntensity * _RimColor * occlusion; // Emission Map 적용 float4 emission = float4(0, 0, 0, 0); if (_EnableEmission > 0.5) { float4 emissionMapValue = SAMPLE_TEXTURE2D(_EmissionMap, sampler_EmissionMap, IN.uv); emission = emissionMapValue * _EmissionColor * _EmissionIntensity; } col.xyz *= (light.xyz + _AmbientColor.xyz + specular.xyz + rim.xyz); col.xyz += emission.xyz; // Emission 추가 return col; } ENDHLSL } UsePass "Universal Render Pipeline/Lit/ShadowCaster" } }