// ───────────────────────────────────────────────────────────────────────────── // WLRimAdd.shader — 캐릭터 윤곽의 밝은 테(림라이트) (WL-814t · #814 · 요소 Ⓓ) // // ■ 왜 별도 패스인가 // 원본 `Toon.shadergraph` 에는 Fresnel 입력이 없고, 원본(`Assets/3DPixelArtEnvironment/**`) // 수정은 금지다. 그래프 **복사본**을 만들어 Fresnel 을 더하는 대신, 같은 메시를 한 번 더 // 가산(Blend One One)으로 덧그리는 패스를 택했다 — 머티리얼/그래프를 하나도 안 건드리고 // 런타임에 켜고 끌 수 있고, 세기를 슬라이더 하나로 준다. // // ■ 무엇을 하나 // rim = pow(1 - saturate(dot(N, V)), _RimPower) — 시선과 수직인 가장자리에서 1. // `_RimUpBias` 로 위쪽(레퍼런스의 무대조명) 윤곽만 더 밝게 줄 수 있다. // // ■ 주의 // Transparent 큐 · ZWrite Off · ZTest LEqual + Offset 으로 원본 면 위에 얹는다. // ───────────────────────────────────────────────────────────────────────────── Shader "WL/RimAdd" { Properties { [HDR] _RimColor ("Rim Color", Color) = (1, 0.85, 0.55, 1) _RimPower ("Rim Power", Float) = 3.0 _RimStrength ("Rim Strength", Float) = 1.0 _RimUpBias ("Rim Up Bias (0=균일, 1=위쪽만)", Range(0, 1)) = 0.35 } SubShader { Tags { "RenderPipeline" = "UniversalPipeline" "RenderType" = "Transparent" "Queue" = "Transparent" } Pass { Name "WLRimAdd" Tags { "LightMode" = "SRPDefaultUnlit" } Cull Back ZWrite Off ZTest LEqual Offset -1, -1 Blend One One HLSLPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_instancing #pragma target 3.0 #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" CBUFFER_START(UnityPerMaterial) float4 _RimColor; float _RimPower; float _RimStrength; float _RimUpBias; CBUFFER_END struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct Varyings { float4 positionCS : SV_POSITION; float3 normalWS : TEXCOORD0; float3 positionWS : TEXCOORD1; UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_OUTPUT_STEREO }; Varyings vert(Attributes input) { Varyings o = (Varyings)0; UNITY_SETUP_INSTANCE_ID(input); UNITY_TRANSFER_INSTANCE_ID(input, o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); o.positionWS = TransformObjectToWorld(input.positionOS.xyz); o.positionCS = TransformWorldToHClip(o.positionWS); o.normalWS = TransformObjectToWorldNormal(input.normalOS); return o; } half4 frag(Varyings input) : SV_Target { UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); float3 n = normalize(input.normalWS); float3 v = normalize(GetWorldSpaceViewDir(input.positionWS)); float rim = 1.0 - saturate(dot(n, v)); rim = pow(saturate(rim), max(_RimPower, 0.01)); // 위쪽 윤곽 가중(무대 조명 느낌) float up = saturate(n.y * 0.5 + 0.5); rim *= lerp(1.0, up, saturate(_RimUpBias)); return half4(_RimColor.rgb * (rim * _RimStrength), 0.0); } ENDHLSL } } Fallback Off }