본문으로 건너뛰기
CHOI HONGSU
1 min read

Shader code

 

Texture Set 1 Matcap Texture ( 256 px ) 1 Color Mask(R) + Normalmap(G,A) + Emissive Mask(B) (Packed) ( 512 px ) 1 Dissolve Noise Texture ( 32 px )

Matcap

Normal Pack

Build TBN matrix and convert Tangent space → World space

half3 tangentWS = normalize(input.tangentWS.xyz);
half tangentSign = input.tangentWS.w; // generate bitangent directly in the pixel via cross
half3 bitangentWS = normalize(cross(normalWS, tangentWS) * tangentSign);
half3x3 TBN = half3x3(tangentWS, bitangentWS, normalWS);
half3 finalNormalWS = normalize(mul(normalTS, TBN)); // World space normal

Typical TBN = compute the vertex bitangent and pass it to the pixel ( incurs Fetch Cost )

On mobile, fetch cost outweighs compute cost.

Optimized code = to reduce fetch cost, compute bitangent via cross in the pixel shader


World → View space normal conversion

half3 viewNormal = normalize(mul((float3x3)UNITY_MATRIX_V, finalNormalWS));

Matcap

half2 matcapUV = viewNormal.xy * 0.5h + 0.5h;
half4 matcapColor = SAMPLE_TEXTURE2D(_Matcap, sampler_Matcap, matcapUV);

Matcap samples lighting effects from a matcap texture by using the view-space normal direction as UV coordinates, without performing any real lighting calculation.

The reason MikkTSpace is not computed directly in the shader is that Unity already injects MikkTSpace-based tangents at the import / FBX stage.

struct Attributes
{
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
float3 normalOS : NORMAL; // Normal from the FBX, based on MikkTSpace
float4 tangentOS : TANGENT; // Tangent from the FBX, based on MikkTSpace (xyz+sign)
};

Building the TBN from tangentOS and normalOS in the Unity shader keeps everything unified under MikkTSpace through normal-map baking and FBX import.

Same quality as a Mikkelsen-baked normal map

Shader Graph로 코드 시각화

Unit Color Mask ( R Chanel )

Unit Color Mask ( R Chanel )

half stepEdgeSub = step(0.72h, maskR); half edgeSubtract = maskR (1.0h - stepEdgeSub); half stepEdgeMain = step(0.02h, edgeSubtract); half oneminusMain = 1.0h - stepEdgeMain; half4 maskBaseColor = half4(maskR, maskR, maskR, 1.0h); half4 colorMasked = maskBaseColor (1.0h - oneminusMain) + MainColor oneminusMain; half4 finalColor = colorMasked (1.0h - stepEdgeSub) + SubColor * stepEdgeSub;

R - Mask ( MainMask - 0 // SubMask - 1 ) ( Black - 0.05 // Gray - 0.08 // White - 0.2 )

Shader Graph로 코드 시각화

반사광 구현 ( Y AO )

half yNormal = finalNormalWS.y;
half aoFactor = 1.0h - smootherstep(_AOYThreshold - 0.5h, AOYThreshold + 0.5h, yNormal);
half4 aoTinted = matcapColor +
(BG_UnitAOColor aoFactor _AOTintStrength);

Y-normal direction based AO — bounce light ( Y AO ) implemented using only the normal, no AO texture required (optimized feature)

Apply bounce light ( Y AO ) color

Dissolve 기능

if (_Dissolve > 0.0h)
{
half noiseValue = SAMPLE_TEXTURE2D(_DissolveNoise, sampler_DissolveNoise, input.uv).r;
clip(noiseValue - _Dissolve);
}

When _Dissolve is 0 (not used at all) → the noise texture is not even sampled (performance saving)

When Dissolve > 0 → wherever the per-pixel noise value (0~1) minus Dissolve goes negative, clip is invoked, producing the Dissolve effect (the animation of parts disappearing along the noise pattern)

Rim Light

half3 viewSpaceNormal;
viewSpaceNormal.x = dot(UNITY_MATRIX_V[0].xyz, normalWS);
viewSpaceNormal.y = dot(UNITY_MATRIX_V[1].xyz, normalWS);
viewSpaceNormal.z = dot(UNITY_MATRIX_V[2].xyz, normalWS);

half rimFactor = dot(viewSpaceNormal, RimLightDirection);
half rimIntensity = step(rimFactor, -5.0h) *
UseRimLight;
finalColor += rimIntensity;

Rim light implementation unaffected by the normal map ( the rim forms along the silhouette )

half3 normalWS = normalize(input.normalWS);

The input.normalWS here is computed in the vertex shader as VertexNormalInputs normalInputs = GetVertexNormalInputs(input.normalOS, input.tangentOS) and passed to the pixel shader.

That is, the "vertex normal" imported from the modeling/FBX (=the normal baked into the mesh, which defines the silhouette).

Computed with normalWS → per-vertex world normal (normal map / detail not applied)

셰이더 코드 · FlashGambit · Choi Hongsu · Hongsu