yum/3ner

A toon shader for Unity's BIRP.

git clone https://git.yummers.dev/yum/3ner

yumssfd bugfixes072fa89

master
2.6 KiB75 linesraw
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