summaryrefslogtreecommitdiffstats
path: root/ssfd.cginc
blob: df45d81cf538aa2fbfb4950502303b493116ee55 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#ifndef __SSFD_INC
#define __SSFD_INC

#include "globals.cginc"

#if defined(_SSFD)
float ssfd(float2 uv, float scale, float max_fwidth, float2 uv_offset, texture3D noise)
{
  //float uv_fw = fwidth(uv.x) + fwidth(uv.y);
  // Original paper uses SVD instead of fwidth.
  float2x2 M = float2x2(ddx(uv), ddy(uv));
  float2x2 MtM = mul(transpose(M), M);
  float trace = MtM[0][0] + MtM[1][1];
  float det = determinant(MtM);
  // Calculate eigenvalues using quadratic formula.
  float tmp = sqrt(trace * trace - 4 * det);
  float e1 = (trace + tmp) * 0.5;
  float e2 = (trace - tmp) * 0.5;
  float2 singular_values = sqrt(float2(e1, e2));
  // Logic from original paper: the smaller eigenvalue corresponds to the
  // largest amount of stretching, so we use it to determine when to
  // subdivide.
  float uv_fw = singular_values.y;
  uv_fw *= scale;

  uint width, height, depth;
  noise.GetDimensions(width, height, depth);
  float bayer_res = sqrt(depth);

  // Suppose max_fwidth is 1.
  // uv_fw is 16. That means UV is changing a lot per pixel. That means we want to shrink the scale of the UV.
  // Factor is 16.
  // log_2(factor) is 4.
  // Divide original by 16.
  float fw_factor = max(uv_fw / max_fwidth, 1e-6);
  // Fractal transitions need to happen in octaves to match the Bayer
  // self-similarity used by the reference implementation.
  float fractal_level = log2(fw_factor);
  float fractal_level_floor = floor(fractal_level);
  float fractal_remainder = fractal_level - fractal_level_floor;

  float uv_scale = exp2(-fractal_level_floor);
  uv *= uv_scale;
  uv += uv_offset * uv_scale;

  float n_layers = depth;
  float min_sub_layer = max(1.0, 0.25 * n_layers);
  float max_sub_layer = n_layers;

  // Only the top 3/4 of the layer stack is fractally self-similar.
  float sub_layer = lerp(min_sub_layer, max_sub_layer, 1 - fractal_remainder);
  float uvw = (sub_layer - 0.5) / n_layers;

  float3 uv_3d = float3(uv, uvw);

  float dither = noise.SampleLevel(linear_repeat_s, uv_3d, 0);

  return dither;
}
#endif  // _SSFD

void apply_ssfd(v2f i, float2 uv, LightData l, float3 normal, inout float3 albedo) {
#if defined(_SSFD)
  float ssfd_mask = ssfd(uv, _SSFD_Scale, _SSFD_Max_Fwidth, 0, _SSFD_Noise);
  float ssfd_mask_fw = fwidth(ssfd_mask);
  // TODO I think anti aliasing is probably broken
  //float light_amount = 1.0 - (dot(l.indirect.diffuse_dominant_dir, normal) * 0.5 + 0.5);
  float light_amount = 0;
  float ssfd_threshold = saturate(light_amount + _SSFD_Threshold);
  ssfd_mask = smoothstep(ssfd_threshold - ssfd_mask_fw * 0.5, ssfd_threshold + ssfd_mask_fw * 0.5, ssfd_mask);
  albedo = lerp(albedo, _SSFD_Tint.rgb, ssfd_mask);
#endif
}

#endif  // __SSFD_INC