yum/3ner
A toon shader for Unity's BIRP.
git clone https://git.yummers.dev/yum/3ner
072fa89
master
1#ifndef __SSFD_INC 2#define __SSFD_INC 3 4#include "globals.cginc" 5 6#if defined(_SSFD) 7float ssfd(float2 uv, float scale, float max_fwidth, float2 uv_offset, texture3D noise) 8{ 9 //float uv_fw = fwidth(uv.x) + fwidth(uv.y); 10 // Original paper uses SVD instead of fwidth. 11 float2x2 M = float2x2(ddx(uv), ddy(uv)); 12 float2x2 MtM = mul(transpose(M), M); 13 float trace = MtM[0][0] + MtM[1][1]; 14 float det = determinant(MtM); 15 // Calculate eigenvalues using quadratic formula. 16 float tmp = sqrt(trace * trace - 4 * det); 17 float e1 = (trace + tmp) * 0.5; 18 float e2 = (trace - tmp) * 0.5; 19 float2 singular_values = sqrt(float2(e1, e2)); 20 // Logic from original paper: the smaller eigenvalue corresponds to the 21 // largest amount of stretching, so we use it to determine when to 22 // subdivide. 23 float uv_fw = singular_values.y; 24 uv_fw *= scale; 25 26 uint width, height, depth; 27 noise.GetDimensions(width, height, depth); 28 float bayer_res = sqrt(depth); 29 30 // Suppose max_fwidth is 1. 31 // uv_fw is 16. That means UV is changing a lot per pixel. That means we want to shrink the scale of the UV. 32 // Factor is 16. 33 // log_2(factor) is 4. 34 // Divide original by 16. 35 float fw_factor = max(uv_fw / max_fwidth, 1e-6); 36 // Fractal transitions need to happen in octaves to match the Bayer 37 // self-similarity used by the reference implementation. 38 float fractal_level = log2(fw_factor); 39 float fractal_level_floor = floor(fractal_level); 40 float fractal_remainder = fractal_level - fractal_level_floor; 41 42 float uv_scale = exp2(-fractal_level_floor); 43 uv *= uv_scale; 44 uv += uv_offset * uv_scale; 45 46 float n_layers = depth; 47 float min_sub_layer = max(1.0, 0.25 * n_layers); 48 float max_sub_layer = n_layers; 49 50 // Only the top 3/4 of the layer stack is fractally self-similar. 51 float sub_layer = lerp(min_sub_layer, max_sub_layer, 1 - fractal_remainder); 52 float uvw = (sub_layer - 0.5) / n_layers; 53 54 float3 uv_3d = float3(uv, uvw); 55 56 float dither = noise.SampleLevel(linear_repeat_s, uv_3d, 0); 57 58 return dither; 59} 60#endif // _SSFD 61 62void apply_ssfd(v2f i, float2 uv, LightData l, float3 normal, inout float3 albedo) { 63#if defined(_SSFD) 64 float ssfd_mask = ssfd(uv, _SSFD_Scale, _SSFD_Max_Fwidth, 0, _SSFD_Noise); 65 float ssfd_mask_fw = fwidth(ssfd_mask); 66 // TODO I think anti aliasing is probably broken 67 //float light_amount = 1.0 - (dot(l.indirect.diffuse_dominant_dir, normal) * 0.5 + 0.5); 68 float light_amount = 0; 69 float ssfd_threshold = saturate(light_amount + _SSFD_Threshold); 70 ssfd_mask = smoothstep(ssfd_threshold - ssfd_mask_fw * 0.5, ssfd_threshold + ssfd_mask_fw * 0.5, ssfd_mask); 71 albedo = lerp(albedo, _SSFD_Tint.rgb, ssfd_mask); 72#endif 73} 74 75#endif // __SSFD_INC