Shader
01
커스텀 글래스

코드보기
목적
투명 유리 표현: 방향광 하이라이트 + 내·외곽 림 + 스팟을 합성하여 반짝임과 두께감을 부여.
필수 입력
메인광: _LightDirection, _LightColor, _LightCutoff, _LightSmoothness, _LightGradient
림: _InnerRimColor, _InnerRimPower, _InnerRimCutoff, _OuterRimColor, _OuterPower, _OuterCutoff
스팟: _SpotDirection, _SpotColor, _SpotSize, _SpotOpacity
정점 단계(필수 전달)
OUT.normalWS = GetVertexNormalInputs(IN.normalOS).normalWS;
OUT.viewDirWS = GetWorldSpaceViewDir(vertexWS);
OUT.normalOS = IN.normalOS; // 일부 항목은 OS 노멀 사용
프래그먼트 핵심 수식
// 1) 메인 라이트(방향광)
float ndotlOS = dot(normalize(IN.normalOS), normalize(_LightDirection.xyz));
float lightA = smoothstep(_LightCutoff, _LightCutoff + _LightSmoothness, _LightGradient + ndotlOS);
// 2) 림(뷰-노멀 기반)
float vndot = dot(normalize(IN.normalWS), normalize(IN.viewDirWS));
float rim = pow(1.0 - saturate(vndot), /*Power*/);
float outerA = smoothstep(0.5 - _OuterCutoff, 0.5, pow(1.0 - saturate(vndot), _OuterPower));
float innerA = smoothstep(0.5 - _InnerRimCutoff, 0.5, pow(1.0 - saturate(vndot), _InnerRimPower));
// 3) 스팟 하이라이트(정점 OS 노멀·스팟 방향)
float spotA = pow(saturate(dot(normalize(IN.normalOS), normalize(_SpotDirection))), _SpotSize) * _SpotOpacity;
// 4) 합성(RGB) 및 투명도
float3 col = _LightColor.rgb * lightA
+ _OuterRimColor.rgb * outerA
+ _InnerRimColor.rgb * innerA
+ _SpotColor.rgb * spotA;
float alpha = saturate(lightA + outerA + innerA + spotA);
블렌딩/깊이
Blend One OneMinusSrcAlpha // 유리의 가산성 반짝임
ZWrite Off // 투명 재질
요약: **뷰 각도 림(rim)**으로 윤곽, 방향광으로 면 밝기, 스팟으로 예리한 하이라이트를 더해 투명·광택 유리를 간결한 Unlit 합성으로 구현합니다.
02
해안가

코드보기
목적
텍스처 흐름/왜곡을 기반으로 프레넬 밝음 + 파도 결 + 미세 반짝임 + 해안선 발광을 순차 합성하는 Stylized 수면.
정점(필수 전달)
uv(메인 UV), positionWS(디더용 스크린좌표 계산).
핵심 수식(프래그먼트)
1) 바닥 흐름/굴절(UnderGround)
float2 flow = float2(_FlowSpeed_X, _FlowSpeed_Y);
float2 subUV = ( _UnderGround_Transform.xy * uv * _UnderGround_Transform.z) + (-flow)*_Time.y;
float4 sub = SAMPLE(_UnderGround_Texture, subUV);
float2 undUV = ( _UnderGround_Transform.xy * uv * _UnderGround_Transform.z) + flow*_Time.y
+ _Flowness_Intensity * sub.xy;
float4 under = SAMPLE(_UnderGround_Texture, undUV);
2) 프레넬(베이스)
float f = saturate(_Fresnel_Range * (1 - uv.y));
float a = saturate(pow(f, _Fresnel_Falloff));
float3 base = lerp(_WaterColor.rgb*under.rgb, _WaterColor.rgb*under.rgb + _Fresnel_Color.rgb, a);
3) 파도 결(회전/타일/속도)
float2 ruv = rotate(uv, radians(_Wave_Transform.w));
float2 wuv = ruv * _Wave_Transform.xy * _Wave_Transform.z + float2(_Wave_Speed,0)*_Time.y;
float w = SAMPLE(_Wave_Texture, wuv).r;
float wF = pow(saturate(_Wave_Range * (1 - uv.y)), _Wave_Falloff) * w;
base = lerp(base, _Wave_Color.rgb, wF);
4) 미세 반짝임(Flicker) + 왜곡
float4 fd = SAMPLE(_UnderGround_Texture, uv + float2(0, _FlickerDistortion_Speed)*_Time.y/100);
float fx = SAMPLE(_Flicker_Texture, _Flicker_Scale * (uv + fd.r * _FlickerDistortion_Scale)).r;
float fF = saturate(pow(saturate(fx * _Flicker_Intensity), _Flicker_Contrast));
base = lerp(base, _Flicker_Color.rgb, fF);
5) 해안선 발광(shore glow)
float shoreM = SAMPLE(_UnderGround_Texture, _ShoreEdge_Transform.xy * uv * _ShoreEdge_Transform.z).r;
float sF = pow(saturate((1 - uv.y) * _ShoreEdge_Range), _ShoreEdge_Falloff)
* (_ShoreEdge_Intensity * shoreM);
base = lerp(base, _ShoreEdge_Color.rgb, sF * _GlowDelay);
6) 디더 컷아웃(옵션)
float d = Bayer4x4(screenPos) ; // 0..1 임계
clip(1 - d * _Dither);
03
글리치 쉐이더

코드보기
목적: 로고/문양 위에 스캔라인 + 글리치 + 브라운관 림을 얹는 Old TV 효과.
필수 입력:
_Texture2D(R=심볼 마스크), _Stripe_Color, _Rim_Color, _Rim_Power/_Rim_Intensity, _Transform(xyzw), _Distortion_*, _Line_Width, _Glitching, _Alpha, _ShapeMask.
정점(필수 전달):
output.normalWS = GetVertexNormalInputs(input.normalOS).normalWS;
output.viewDirWS = GetWorldSpaceViewDir(GetVertexPositionInputs(input.positionOS).positionWS);
프래그먼트 핵심 로직:
// 1) 글리치(수평 쉬프트, 줄 단위)
float rand = frac(sin(dot(round(uv.y + floor(_Time.y*1.5)), float2(12.9898,78.233)))*43758.5453);
float glitch = (_Glitching>0) ? 0.05 * rand : 0.0;
// 2) 수직 왜곡(Gradient Noise 스크롤)
float n; GradientNoise(uv.y*_Distortion_Scale + _Time.y*_Distortion_Scroll_Speed, 10, n);
float2 tuv = ((float2(uv.x + glitch, uv.y + _Distortion_Strength*(n-0.5)*0.1) - _Transform.xy)
* _Transform.zz) /*scale*/;
tuv = rotate(tuv, _Transform.w);
// 3) 심볼/배경 합성 + 스캔라인
float mask = SAMPLE(_Texture2D, tuv).r;
float3 base = lerp(_Background_Color.rgb, _Symbol_Color.rgb, mask);
float stripes = saturate(sin(((_Time.y*0.15)+uv.y)*_Line_Width))
+ saturate(pow(frac(((_Time.y*0.5)+uv.y)*2),15));
base = lerp(base, base + _Stripe_Color.rgb, saturate(stripes));
// 4) 림(뷰-노멀 프레넬)
float fres = pow(1 - saturate(dot(normalize(normalWS), normalize(viewDirWS))), _Rim_Power);
base = lerp(base, base + _Rim_Color.rgb, fres * _Rim_Intensity);
// 5) 최종 알파
float a = lerp(_Alpha, mask, _ShapeMask);
블렌딩: Blend SrcAlpha OneMinusSrcAlpha(투명 합성, ZWrite Off).
04
월드 좌표기반 잔디 쉐이더

코드보기
목적: Opaque Unlit 베이스에 옵션형 바람 변형(_WIND) 을 정점 단계에서만 적용.
핵심 입력
_WIND(키워드), _WindIntensity, _WindSpeed, _WindScale, _BaseMap(+ _BaseMap_ST), _BaseAlphaMap(r, 필요 시 마스크).
정점(바람 변형 핵심)
// 월드 XY + 시간 기반 노이즈
float n = Unity_SimpleNoise(vp.positionWS.xy + _WindSpeed * _Time.y, _WindScale);
// 화면 X축으로 휘어주기(윗쪽이 더 크게: uv.y 가중)
output.positionCS.x += n * saturate(input.uv.y) * _WindIntensity;
output.positionCS.x -= 0.5; // 원 스니펫 오프셋 유지
프래그먼트(베이스 샘플 + 포그 옵션)
half3 rgb = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv).rgb;
half4 col = half4(rgb, 1); // Opaque 출력
#if defined(FOG_ANY)
col.rgb = MixFog(col.rgb, input.fogFactor);
#endif
return col;
렌더 상태: Blend One Zero, ZWrite On, ZTest LEqual, Cull Back (불투명 기본).
05
버텍스 오프셋

코드보기
목적: 셀 가이드가 파동+오프셋과 함께 스케일-인 등장하고, 마스크 텍스처로 투명 합성.
필수 입력
_MainTex.r(밝기 마스크), _TransTex.b(알파), 인스턴스 _Color, _CellSize, _TimeInput, _SrcBlend/_DstBlend.
정점(핵심 변형)
float2 cell = UNITY_ACCESS_INSTANCED_PROP(Props,_CellSize)*0.5 - 0.5;
float wave = (1 + sin(_Time.y*18)) * 0.01 * input.color.a;
float t0 = UNITY_ACCESS_INSTANCED_PROP(Props,_TimeInput);
float scale = saturate((_Time.y - t0) * 5.2);
input.positionOS.y = (input.positionOS.y + cell.y + wave) * scale;
input.positionOS.x = (input.positionOS.x - (input.color.a*cell.x + wave)) * scale;
프래그먼트(색/알파)
float r = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.texcoord).r;
half4 col = input.color * lerp(0.8h, 1.8h, r); // 밝기 강조
col.a = SAMPLE_TEXTURE2D(_TransTex, sampler_TransTex, input.texcoord).b; // 알파
return col;
렌더 상태
Queue=Transparent, Blend [_SrcBlend][_DstBlend], ZWrite Off, Cull Off, Offset -1,-1.
06
구름 그림자 쉐이더

코드보기
목적: 월드 좌표의 구름 텍스쳐를 쉐이더로 흐르는 효과를 바닥과 오브젝트에 동일한 연산을 맞춰 마치 구름으로 인해 그늘지는 듯한 효과를 표현.
핵심 입력
_WorldMap(r), _WorldmapFalloff=(off,*,pow,intensity), _GroundTint, _CLOUD_SHADOW, _CloudShadowMap(r), _G_CloudScale, _CloudScroll.xy, _GrassScale.
정점(필수 전달)
float2 wxz = GetVertexPositionInputs(posOS).positionWS.xy /*or .xz*/; // 월드 평면 좌표
o.uv.zw = wxz; // 클라우드용
프래그먼트 핵심 로직
// 1) 기본색
float3 col = _GroundTint.rgb;
// 2) 구름 그림자 (옵션, 월드 고정 + 시간 스크롤)
#ifdef _CLOUD_SHADOW
float2 cuv = o.uv.zw / max(_G_CloudScale, 1e-4) + _Time.y * _CloudScroll.xy;
float m = SAMPLE(_CloudShadowMap, cuv).r; // 밝을수록 그림자↑
col = lerp(col, col * 0.6, m); // 감산적 보간(40% 감광 한계)
#endif
return float4(col, 1);
07
Height Fog

월드 기준 z 값의 0 을 기준으로 오브젝트와 지면의 블랜딩을 주는 기능입니다.
코드보기
목적: Opaque Unlit 표면에 인스턴스/머티리얼 틴트를 마스크로 가중하고, 옵션으로 로컬 고도 안개를 적용.
핵심 입력
_BaseMap, _BaseColor, _BaseAlphaMap.r, _BaseAlphaMap2.r, _Opacity, _HEIGHTFOG_LOCAL, _HeightFogFactor(x=startY,y=endY), _FogColor; (인스턴싱) Props._BaseColor, Props._Opacity.
정점(필수 전달)
o.uv = TRANSFORM_TEX(input.uv, _BaseMap);
o.positionWS = GetVertexPositionInputs(input.positionOS).positionWS; // HeightFog용
프래그먼트 핵심 로직
// 1) 색: Base * (머티리얼/인스턴스 컬러를 AlphaMap2로 가중)
half4 baseTex = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, i.uv);
half m2 = SAMPLE_TEXTURE2D(_BaseAlphaMap2, sampler_BaseAlphaMap2, i.uv).r;
half3 col = baseTex.rgb;
col *= lerp(1, _BaseColor.rgb, m2);
col *= lerp(1, INST(_BaseColor).rgb, m2);
// 2) 로컬 Height Fog(옵션)
#ifdef _HEIGHTFOG_LOCAL
float h0 = _HeightFogFactor.x, h1 = _HeightFogFactor.y;
float t = saturate((i.positionWS.y - h0) / max(h1 - h0, 1e-5));
col = lerp(_FogColor.rgb, col, t);
#endif
렌더 상태
Blend One Zero, ZWrite On, ZTest LEqual, Cull Back (불투명 기본).
08
Light2D Interact 쉐이더

Light2D 텍스쳐와 Interact 하여 오브젝트 쉐이더의 색이나 쉐이딩을 추가 적용하는 기능입니다.
코드보기
목적: 2D 라이트 RTT와 폴루션 맵을 이용해 화면공간에서 오염 반응 컬러를 가변적으로 블렌딩.
핵심 입력: _G_Light2D_RTT(rgb), _PollutedColor(rgba), _PollutedMap(rgb), alphaTex.r, _MaskBlending.
핵심 로직(프래그먼트)
// 1) 스크린 마스크(라이트 기반)
screenUV = ComputeScreenPos(...)/w;
L = SAMPLE(_G_Light2D_RTT, screenUV, LOD=2).rgb;
adjB = lerp(L.b, L.b*(1-L.g), L.g);
screenMask = lerp(L.r, 0, adjB*L.r);
// 2) 반응 계수(시간적 톱니파 + 안정화)
value = smoothstep(1, 2.2, (screenMask + 5)*8);
saw = value - pow(value, 256);
react = saturate(min(1 - value + saw, max(1, value*64)));
react = smoothstep(lerp(1, react, screenMask), 0, 0.1);
// 3) 오염 색 블렌드(Overlay 가변)
blendFactor = normalize(max(0, _PollutedColor.rgb - 0.5));
overlay(a,b) = lerp(2*a*b, 1 - 2*(1-a)*(1-b), blendFactor);
pollutedMap = SAMPLE(_PollutedMap, uv).rgb;
isFull = step(2.999, dot(pollutedMap,1)); // 거의 흰색=완전 오염
blendColor = lerp( overlay(pollutedMap,_PollutedColor.rgb),
overlay(finalColor.rgb,_PollutedColor.rgb),
isFull );
pollutedMap = lerp(blendColor, blendColor*pollutedMap, isFull);
// 4) 최종 가중치 및 적용
maskTerm = lerp((alphaTex.r - 0.5)*2, 1, _MaskBlending);
w = lerp(react * _PollutedColor.a * maskTerm,
react * _PollutedColor.a, isFull);
finalColor.rgb = lerp(finalColor.rgb, pollutedMap, w);
효과 요약: 라이트 조건과 마스크에 따라 오염 색(Overlay) 를 가중 적용하고, 완전 오염 구역은 맵 우선, 그 외는 기존 색 기반으로 자연스럽게 전이.
09
Flipbook Animation

4x4 타입의 flipbook 텍스쳐를 이용하여 animation 효과를 주었습니다.
코드보기
목적: 스프라이트 시트(N×N)에서 시간 기반으로 프레임을 선택해 플립북 애니메이션을 표시.
핵심 입력
_MainTex(플립북 시트), _MaxFrames(총 프레임), _FPS, _1x1(그리드 한 변의 칸수=N).
정점(프레임 선택 + UV 재매핑)
// 현재 프레임 인덱스
float tile = floor(fmod(_Time.y * _FPS, _MaxFrames));
// N×N 그리드에서 tile → (x,y) 타일 좌표로
float N = _1x1;
float2 stepUV = 1.0 / float2(N, N);
float x = tile - N * floor(tile / N);
float y = (N - 1) - floor(tile / N); // 상단→하단 순서(Y 뒤집기)
// 타일 UV로 변환
float2 uvFB = (IN.uv + float2(x, y)) * stepUV;
프래그먼트(샘플링)
return SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uvFB);
비고
Y 플립이 필요한 시트는 y = (N-1) - floor(tile/N)처럼 처리(코드의 Invert=(0,1)과 동일).
MaxFrames < N*N인 경우 남는 칸은 자동으로 건너뜀(시간 루프).
10
정화 탱크 쉐이더

메시의 높이를 버텍스 쉐이더 z 값을 조정하여 탱크안의 액체가 차오르는 효과와 컬러 변화를 구현하였습니다.
코드보기
1) 액체
목적
탱크 내부 액체의 채움 높이와 정화 정도(색 전이), 간단한 하이라이트를 표현.
핵심 입력
_MaxHeight(채움 상한), _Purification(0→_Color1, 1→_Color2), _Color1/_Color2.
정점(채움 높이 반영)
float clampedZ = clamp(0, IN.positionOS.z, _MaxHeight*2.5 + 0.65);
OUT.positionCS = TransformObjectToHClip(float3(IN.positionOS.x, IN.positionOS.y, clampedZ));
오브젝트 Z축을 상한으로 클램프하여 액체 표면 높이 연출(탱크 스케일 보정 포함).
프래그먼트(색/하이라이트)
float3 base = lerp(_Color1, _Color2, _Purification);
float edge = dot(normalize(IN.normalWS), normalize(float3(0.3,0.5,0.3)));
finalColor.rgb = lerp(base, base + 0.4, edge); // 간단 스페큘라 느낌
렌더 상태
불투명(Opaque) 지오메트리로 먼저 렌더.
2) 유리
목적
용기 유리의 림/스팟 하이라이트를 투명 합성으로 오버레이.
핵심 입력
_RimColor, _RimCutoff, _SpotDirection, _SpotSize, _SpotOpacity.
정점(필수 전달)
OUT.normalWS = GetVertexNormalInputs(IN.normalOS).normalWS;
OUT.viewDirWS = GetWorldSpaceViewDir(GetVertexPositionInputs(IN.positionOS).positionWS);
프래그먼트(림/스팟 + 합성)
float rimA = smoothstep(0.5 - _RimCutoff, 0.5, pow(saturate(dot(normalize(normalWS), normalize(viewDirWS))), 3));
float spotA = pow(saturate(dot(normalize(normalWS), normalize(_SpotDirection))), _SpotSize) * _SpotOpacity;
final.rgb = (rimA + spotA) * _RimColor;
렌더 상태/정렬
Blend One OneMinusSrcAlpha, ZWrite On, ZTest LEqual
→ 액체(Opaque) 위에 유리를 투명 합성, ZWrite 온으로 가장자리 정렬 안정화.
레이어링 가이드
액체(불투명) 먼저 렌더 → 채움 높이/색 확정.
유리(투명) 후사 → 림/스팟 하이라이트로 입체감 부여.
11
카메라 거리에 따른 스케일 조정

카메라와 지면의 거리의 값을 하여 오브젝트의 스케일을 키워 오브젝트의 단순화 및 가독성을 높이는 쉐이더입니다.
코드보기
목적: 거리 기반 스케일링과 스크린-스페이스 디더 페이드(인스턴싱 가변)를 조건부로 적용.
핵심 입력
_Distance(기준거리), _Scale_MinMax(r=min,g=max).
핵심 로직
// 2) 월드/클립 좌표 업데이트
float3 posWS = TransformObjectToWorld(input.positionOS);
output.positionWS = posWS;
// 3) 거리 스케일: 카메라까지의 거리로 객체 크기 조절(클램프)
float dist = distance(vertexInput.positionWS.xyz, _WorldSpaceCameraPos.xyz);
float s = clamp(dist / _Distance, _Scale_MinMax.r, _Scale_MinMax.g);
input.positionOS.xyz *= s;
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
효과 요약
멀수록(또는 기준거리 대비) 스케일이 최소 최대값으로 선형 변화한다.
12
EDM 쉐이더

이벤트 BGM 의 Frequency를 Visulizing 하여 이를 쉐이더에서 받아와 그라데이션형태로 보간하여 이를 시각화하였습니다
코드보기
목적: _AudioSpectrum 텍스처를 샘플해 막대/격자형 레벨 미터로 시각화. _Simplicity(지수 곡선)와 _Intensity(스케일)로 반응 곡선을 제어.
핵심 입력
_AudioSpectrum(R에 스펙트럼), _Simplicity(감마/지수), _Intensity(세기), _UseGrid(0=막대, 1=격자), _MainTex(베이스 색).
정점(통과)
OUT.position = TransformObjectToHClip(IN.position.xyz);
OUT.uv = IN.uv;
프래그먼트(핵심 로직)
격자 모드(_UseGrid > 0.1): 2D 셀 → 1D 인덱싱 후 샘플
float bands = 25.0, rows = 3.0;
float bi = floor(uv.x * bands);
float ri = floor(uv.y * rows);
float idx = clamp(bi + ri * bands, 0, bands*rows - 1);
float u = (idx / (bands*rows)) * 0.5; // 스펙트럼 텍스처의 좌측 절반 사용
float v = SAMPLE_TEXTURE2D(_AudioSpectrum, sampler_AudioSpectrum, float2(u,0)).r;
float g = pow(v, _Simplicity) * _Intensity;
float a = (g > 0.01) ? 1 : 0;
return half4(lerp(mainTex*0.4, mainTex, a), 1);
막대 모드(기본): X축 밴드 선택 후 8분할 높이 충족 여부로 켜기
const float bands = 20.0;
float bi = floor(uv.x * bands);
float u = (bi / bands) * 0.5; // 좌측 절반 사용
float v = clamp(SAMPLE_TEXTURE2D(_AudioSpectrum, sampler_AudioSpectrum, float2(u,0)).r, 0, 1);
float g = pow(v, _Simplicity) * _Intensity;
const float h = 0.125; // 1/8
float seg = floor(uv.y * 8.0);
float a = (seg == 0 && g >= h) || (seg > 0 && g >= seg*h) ? 1 : 0;
return half4(lerp(mainTex*0.2, mainTex, a), 1);
시각 요지
격자: 밴드×행 셀별 on/off로 매트릭스형 레벨 미터.
막대: 8단 분절 막대가 스펙트럼 세기에 맞춰 위로 점등.
색은 mainTex를 lerp로 밝히는 방식(알파는 1, 불투명).
13
Dithering Fade 쉐이더

카메라가 멀어지고 단계별 심리스를 표현할때 디더 효과를 통해 자연스러운 페이드 효과를 내도록 하였습니다.
코드보기
목적: 거리 기반 스케일링과 스크린-스페이스 디더 페이드(인스턴싱 가변)를 조건부로 적용.
핵심 입력
_DISTANCESCALE(키워드), Props._Dither(인스턴스별 디더 강도), _Distance(기준거리),
핵심 로직
// 1) 월드/클립 좌표 업데이트
float3 posWS = TransformObjectToWorld(input.positionOS);
output.positionWS = posWS;
// 2) 거리 스케일: 카메라까지의 거리로 객체 크기 조절(클램프)
float dist = distance(vertexInput.positionWS.xyz, _WorldSpaceCameraPos.xyz);
float s = clamp(dist / _Distance, _Scale_MinMax.r, _Scale_MinMax.g);
input.positionOS.xyz *= s;
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
효과 요약
멀수록(또는 기준거리 대비) 스케일이 선형 변화하고,
화면 패턴 기반 디더링 컷아웃으로 부드러운 소멸/등장을 인스턴스별 강도로 제어.