Shader
- Outline 필요함.
- 플레이어를 마우스로 가리킬 때 빨간색 Outline이 생겨야 함.
- 맵에 가려질 때 플레이어가 파란색으로 투영되어야 함. (X-Ray Shader)
X-Ray Shader

SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry" "RenderPipeline"="UniversalPipeline" }
LOD 200
// Pass 1: 가려진 부분
Pass {
Name "OccludedPass"
Tags { "LightMode" = "SRPDefaultUnlit" }
ZWrite Off
ZTest Greater
Blend SrcAlpha OneMinusSrcAlpha
Cull Back
Stencil {
Ref [_StencilRef]
Comp NotEqual
Pass Keep
ReadMask [_StencilReadMask]
}
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct Attributes {
float4 positionOS : POSITION;
};
struct Varyings {
float4 positionCS : SV_POSITION;
};
CBUFFER_START(UnityPerMaterial)
half4 _OccludedColor;
CBUFFER_END
Varyings vert(Attributes input) {
Varyings output;
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
return output;
}
half4 frag(Varyings input) : SV_Target {
return _OccludedColor;
}
ENDHLSL
}
// Pass 2: 보이는 부분
Pass {
Name "ForwardLit"
Tags { "LightMode" = "UniversalForward" }
ZWrite On
ZTest LEqual
Cull Back
Stencil {
Ref [_StencilRef]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
struct Attributes {
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float2 uv : TEXCOORD0;
};
struct Varyings {
float2 uv : TEXCOORD0;
float3 normalWS : TEXCOORD1;
float3 positionWS : TEXCOORD2;
float4 positionCS : SV_POSITION;
};
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
TEXTURE2D(_1st_ShadeMap);
SAMPLER(sampler_1st_ShadeMap);
TEXTURE2D(_Emissive_Tex);
SAMPLER(sampler_Emissive_Tex);
CBUFFER_START(UnityPerMaterial)
float4 _MainTex_ST;
half4 _BaseColor;
half4 _1st_ShadeColor;
half4 _2nd_ShadeColor;
half4 _Emissive_Color;
half4 _OverColor;
half _AddValOffset;
half _CurrPos;
half _BaseColor_Step;
half _BaseShade_Feather;
half _ShadeColor_Step;
half _1st2nd_Shades_Feather;
half _Use_BaseAs1st;
half _Unlit_Intensity;
half _GI_Intensity;
CBUFFER_END
Varyings vert(Attributes input) {
Varyings output;
VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS);
output.positionCS = vertexInput.positionCS;
output.positionWS = vertexInput.positionWS;
output.normalWS = normalInput.normalWS;
output.uv = TRANSFORM_TEX(input.uv, _MainTex);
return output;
}
half4 frag(Varyings input) : SV_Target {
half4 baseColor = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv) * _BaseColor;
Light mainLight = GetMainLight();
half3 normalWS = normalize(input.normalWS);
half NdotL = dot(normalWS, mainLight.direction);
// 툰 셰이딩
half lightIntensity = NdotL * 0.5 + 0.5;
half toonStep = step(_BaseColor_Step, lightIntensity);
if (_BaseShade_Feather > 0.0001)
{
toonStep = smoothstep(_BaseColor_Step - _BaseShade_Feather * 0.5,
_BaseColor_Step + _BaseShade_Feather * 0.5,
lightIntensity);
}
half4 shadeMap = _Use_BaseAs1st > 0.5 ? baseColor : SAMPLE_TEXTURE2D(_1st_ShadeMap, sampler_1st_ShadeMap, input.uv);
half4 firstShade = shadeMap * _1st_ShadeColor;
half shade2Step = step(_ShadeColor_Step, lightIntensity);
if (_1st2nd_Shades_Feather > 0.0001)
{
shade2Step = smoothstep(_ShadeColor_Step - _1st2nd_Shades_Feather * 0.5,
_ShadeColor_Step + _1st2nd_Shades_Feather * 0.5,
lightIntensity);
}
half4 secondShade = shadeMap * _2nd_ShadeColor;
// 셰이드 믹스
half4 shadedColor = lerp(secondShade, firstShade, shade2Step);
half4 finalColor = lerp(shadedColor, baseColor, toonStep);
// 조명 적용
finalColor.rgb *= _Unlit_Intensity;
half3 ambient = half3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w) * _GI_Intensity * 0.3;
finalColor.rgb += ambient * baseColor.rgb;
// Emissive
half4 emissive = SAMPLE_TEXTURE2D(_Emissive_Tex, sampler_Emissive_Tex, input.uv) * _Emissive_Color;
finalColor.rgb += emissive.rgb;
// Overlay Color
half maskValue = saturate((_CurrPos - _AddValOffset) * 10.0);
finalColor.rgb = lerp(finalColor.rgb, _OverColor.rgb, _OverColor.a * maskValue);
return finalColor;
}
ENDHLSL
}
}
Fallback "Hidden/Universal Render Pipeline/FallbackError"
}
문제점 1

- 맵이나 특정 오브젝트에 대해서만 X-Ray가 적용되어야 하는데 무기에 가려져 함께 X-Ray 영향을 받아버린다.
- 스탠실 버퍼를 배운 적이 있는데, x-ray에 영향을 받지 않는 플레이어, 무기의 경우 오브젝트에 대해서 스탠실 번호를 100으로 지정한 후 제외하고자 한다.
[Header("X-Ray Settings")]
[SerializeField] private int xRayIgnoreStencilID = 100;
#region Shader
void InitializeXRay()
{
SetupPlayerWeaponXRay();
}
void SetupPlayerWeaponXRay()
{
SetXRayGroup(gameObject, xRayIgnoreStencilID);
if (_eqipWeapon != null)
SetXRayGroup(_eqipWeapon, xRayIgnoreStencilID);
}
public void SetxRayFromPlayer(GameObject player)
{
SetXRayGroup(player, xRayIgnoreStencilID);
}
void SetXRayGroup(GameObject root, int stencilID)
{
Renderer[] renderers = root.GetComponentsInChildren<Renderer>(true);
foreach (Renderer renderer in renderers)
{
foreach (Material mat in renderer.materials)
{
if (mat.shader.name.Contains("Toon_DoubleShadeWithFeather"))
{
if (mat.HasProperty("_StencilRef"))
{
mat.SetInt("_StencilRef", stencilID);
mat.SetInt("_StencilComp", (int)UnityEngine.Rendering.CompareFunction.Always);
mat.SetInt("_StencilOp", (int)UnityEngine.Rendering.StencilOp.Replace);
}
}
}
}
}
문제점 2


- 기본 Scene에서는 X-Ray의 영향을 안 받는데, 다른 Scene에서는 Black Outline이 X-Ray의 영향을 받는다.
- 살펴보니까 모델 내에 Black Outline만 영향을 받는듯 하다
- 그래서 Black Outline Shader 내에서 스탠실 버퍼를 지정해줬다.
Shader "Custom/Outliner"
{
Properties
{
_Color ("Outline Color", Color) = (1,0,0,1)
_Width ("Outline Width", Range(0.0, 0.1)) = 0.03
[Header(Stencil)]
_StencilRef ("Stencil Reference", Float) = 0
[Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp ("Stencil Comparison", Float) = 8
}
SubShader
{
Tags { "RenderType"="Opaque" "Queue"="Geometry+1" "RenderPipeline"="UniversalPipeline" }
Pass
{
Name "Outline"
Tags { "LightMode" = "SRPDefaultUnlit" }
Cull Front
ZWrite On
ZTest LEqual
Stencil
{
Ref [_StencilRef]
Comp [_StencilComp]
Pass Keep
}
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
half4 _Color;
float _Width;
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
};
struct Varyings
{
float4 positionCS : SV_POSITION;
};
Varyings vert(Attributes input)
{
Varyings output;
float3 normalOS = normalize(input.normalOS);
float3 expandedPos = input.positionOS.xyz + normalOS * _Width;
output.positionCS = TransformObjectToHClip(expandedPos);
return output;
}
half4 frag(Varyings input) : SV_Target
{
return _Color;
}
ENDHLSL
}
}
}
- Comp = Always: XRay와 관계없이 항상 표시
결과물


✅상대 씬에서도 X-Ray가 잘 구성된다.
✅Black Outline에 영향을 받지 않는다.
✅X-Ray 영향 받지 않는 오브젝트는 스탠실로 구분했다.
+추가


- 겸사 겸사 Smooth/Emissive를 Shader에 추가해 조금 더 예쁘게 만들어 주었음.
'이론 > 엔진' 카테고리의 다른 글
| [Unity 포트폴리오] 이터널리턴 코발트 프로토콜 모작 완성! (0) | 2025.12.14 |
|---|---|
| [Unity 오류] InvalidOperationException: Collection was modifie / 반복자 오류 (0) | 2025.12.06 |