Unity Shader Graph 커스텀 함수 노드
Shader Graph의 Custom Function Node는 HLSL 코드를 직접 작성해 기본 제공 노드로는 표현하기 어려운 수학 함수, 특수 효과, 성능 최적화 셰이더를 구현할 때 사용합니다.
1. Custom Function Node 추가
섹션 제목: “1. Custom Function Node 추가”Shader Graph 창 → 빈 공간 우클릭 → Create Node → Custom Function또는 노드 검색 창에서 "Custom Function" 입력Node 설정:
- Name: 함수 이름 (HLSL 함수명과 동일)
- Source:
String(인라인 코드) 또는File(외부 .hlsl 파일) - Inputs/Outputs: 포트 정의
2. String 방식 — 인라인 HLSL
섹션 제목: “2. String 방식 — 인라인 HLSL”/* Custom Function Node 설정: Name: Remap Inputs: In(Float), InMin(Float), InMax(Float), OutMin(Float), OutMax(Float) Outputs: Out(Float) Body: */
void Remap_float(float In, float InMin, float InMax, float OutMin, float OutMax, out float Out){ Out = OutMin + (In - InMin) * (OutMax - OutMin) / (InMax - InMin);}3. File 방식 — 외부 .hlsl 파일
섹션 제목: “3. File 방식 — 외부 .hlsl 파일”#ifndef CUSTOM_FUNCTIONS_INCLUDED#define CUSTOM_FUNCTIONS_INCLUDED
// 물결 효과void WaveOffset_float(float2 UV, float Speed, float Frequency, float Amplitude, float Time, out float2 Out){ float wave = sin(UV.x * Frequency + Time * Speed) * Amplitude; Out = float2(UV.x, UV.y + wave);}
// 디졸브 효과void Dissolve_float(float2 UV, Texture2D NoiseTexture, SamplerState NoiseSampler, float Threshold, float EdgeWidth, out float Alpha, out float Edge){ float noise = SAMPLE_TEXTURE2D(NoiseTexture, NoiseSampler, UV).r; Alpha = step(Threshold, noise); Edge = step(Threshold - EdgeWidth, noise) - Alpha;}
#endifCustom Function Node에서 File 모드로 이 파일을 참조하고 함수명을 지정합니다.
4. 텍스처 샘플링
섹션 제목: “4. 텍스처 샘플링”// Texture2D와 SamplerState를 받아야 함void SampleWithOffset_float( float2 UV, float2 Offset, Texture2D MainTex, SamplerState Sampler, out float4 Color){ Color = SAMPLE_TEXTURE2D(MainTex, Sampler, UV + Offset);}Shader Graph에서 포트 타입:
- 텍스처 입력:
Texture2D타입 - 샘플러 입력:
SamplerState타입 (별도 포트)
5. 노멀 맵 변환 (TBN 매트릭스)
섹션 제목: “5. 노멀 맵 변환 (TBN 매트릭스)”void ApplyNormalMap_float( float3 NormalMap, // 노멀 맵 샘플 (0~1) float3 Normal, // 버텍스 노멀 (월드) float3 Tangent, // 버텍스 탄젠트 (월드) float3 Bitangent, // 버텍스 바이탄젠트 (월드) out float3 WorldNormal){ // 0~1 → -1~1 범위로 변환 float3 n = NormalMap * 2.0 - 1.0;
// TBN 매트릭스로 탄젠트→월드 변환 float3x3 tbn = float3x3(Tangent, Bitangent, Normal); WorldNormal = normalize(mul(n, tbn));}6. 절차적 패턴 — 체커보드
섹션 제목: “6. 절차적 패턴 — 체커보드”void Checkerboard_float(float2 UV, float Scale, out float Out){ float2 scaled = UV * Scale; float2 id = floor(scaled); Out = fmod(id.x + id.y, 2.0);}7. half 정밀도로 모바일 최적화
섹션 제목: “7. half 정밀도로 모바일 최적화”// 모바일에서는 half 정밀도로 성능 향상void RimLight_half(half3 ViewDir, half3 Normal, half RimPower, out half RimFactor){ half NdotV = saturate(dot(Normal, ViewDir)); RimFactor = pow(1.0h - NdotV, RimPower);}8. 디버그 — 값 시각화
섹션 제목: “8. 디버그 — 값 시각화”// UV 좌표를 색상으로 시각화void DebugUV_float(float2 UV, out float4 Color){ Color = float4(UV.x, UV.y, 0.0, 1.0);}
// 노멀을 색상으로 시각화void DebugNormal_float(float3 Normal, out float4 Color){ Color = float4(Normal * 0.5 + 0.5, 1.0);}Custom Function Node의 핵심은 함수 이름이 {Name}_float (또는 _half) 형식이어야 한다는 것입니다. 외부 .hlsl 파일을 사용하면 여러 Shader Graph에서 함수를 재사용할 수 있고, 버전 관리도 용이합니다. 텍스처 샘플링은 반드시 SAMPLE_TEXTURE2D 매크로를 사용해 SRP 호환성을 유지하세요.