yum/3ner

A toon shader for Unity's BIRP.

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

yumCloth bugfixes; burley tiling scaling fix69f2735

master
6.4 KiB180 linesraw
1#ifndef __BURLEY_INC
2#define __BURLEY_INC
3
4#include "data.cginc"
5#include "math.cginc"
6
7#if defined(_BURLEY_TILING) || defined(TRIPLANAR_BURLEY)
8float2 burley_tri_to_cart(float2 tri_coord) {
9  return float2(
10      tri_coord.x + tri_coord.y * 0.5f,
11      tri_coord.y * SQRT_3_OVER_2);
12}
13
14float3 burley_apply_blend_gamma(float3 weights, float gamma) {
15  weights = pow(weights, gamma);
16  return weights / (weights.x + weights.y + weights.z);
17}
18
19// Equation 4 (first half).
20float3 burley_soft_clipping_lower_half(float3 x_hat, float w_hat) {
21  float linear_start = 0.25f * (2.0f - w_hat);
22  float3 linear_value = (x_hat - 0.5f) / w_hat + 0.5f;
23  float3 linear_mask = step(float3(linear_start, linear_start, linear_start), x_hat);
24
25  if (w_hat >= TWO_OVER_THREE) {
26    float3 t = x_hat / (2.0f - w_hat);
27    float3 quadratic = 8.0f * (1.0f / w_hat - 1.0f) * t * t + (3.0f - 2.0f / w_hat) * t;
28    return lerp(quadratic, linear_value, linear_mask);
29  }
30
31  float quadratic_start = 0.25f * (2.0f - 3.0f * w_hat);
32  float3 d = (x_hat - quadratic_start) / w_hat;
33  float3 quadratic = d * d;
34  float3 quadratic_mask = step(float3(quadratic_start, quadratic_start, quadratic_start), x_hat);
35  float3 result = quadratic * quadratic_mask;
36  return lerp(result, linear_value, linear_mask);
37}
38
39// Equation 4.
40float3 burley_soft_clipping_contrast(float3 x_hat, float w_hat) {
41  float3 upper_mask = step(0.5f, x_hat);
42  float3 lower_x = min(x_hat, 1.0f - x_hat);
43  float3 lower_y = burley_soft_clipping_lower_half(lower_x, w_hat);
44  return lerp(lower_y, 1.0f - lower_y, upper_mask);
45}
46
47float3 burley_apply_soft_clipping(float3 gaussian_color, float3 weights) {
48  float w_hat = sqrt(dot(weights, weights));
49  return burley_soft_clipping_contrast(gaussian_color, w_hat);
50}
51
52float3 burley_degaussianize(texture2D lut, float3 gaussian_color, bool decode_srgb = true) {
53  float2 uv_r = float2(gaussian_color.r, 0.5f);
54  float2 uv_g = float2(gaussian_color.g, 0.5f);
55  float2 uv_b = float2(gaussian_color.b, 0.5f);
56  float lut_r = lut.Sample(linear_clamp_s, uv_r).r;
57  float lut_g = lut.Sample(linear_clamp_s, uv_g).g;
58  float lut_b = lut.Sample(linear_clamp_s, uv_b).b;
59  float3 restored = float3(lut_r, lut_g, lut_b);
60  return decode_srgb ? srgb_to_linear(restored) : restored;
61}
62
63struct BurleyPatchTransform {
64  float2 uv;
65  float2 dx;
66  float2 dy;
67  float2x2 uv_to_patch;
68};
69
70BurleyPatchTransform burley_make_patch_transform(float2 uv, float2 uv_dx, float2 uv_dy,
71                                                 float2 tri_vertex, float input_scale) {
72  float3 cube_id = float3(tri_vertex.x, tri_vertex.y, -tri_vertex.x - tri_vertex.y);
73  float3 tile_rand3 = hash33_fast(cube_id);
74  float2 vertex_uv = burley_tri_to_cart(tri_vertex);
75  // Map the unit-radius hex support to the unit square so arbitrary rotation
76  // stays within bounds.
77  float2 local_uv = (uv - vertex_uv);
78  // Apply input scaling.
79  local_uv *= input_scale;
80  float2 sample_dx = uv_dx * (0.5f * input_scale);
81  float2 sample_dy = uv_dy * (0.5f * input_scale);
82  // Rotate.
83  float theta = hash31_ff(tile_rand3) * TAU - PI;
84#if defined(_BURLEY_TILING_ROTATION_CONSTRAINT)
85  theta *= _Burley_Tiling_Rotation_Constraint;
86#endif  // _BURLEY_TILING_ROTATION_CONSTRAINT
87  float s;
88  float c;
89  sincos(theta, s, c);
90  float2x2 rot = float2x2(c, -s, s, c);
91  local_uv = mul(rot, local_uv);
92  sample_dx = mul(rot, sample_dx);
93  sample_dy = mul(rot, sample_dy);
94  // Apply randomized offset, staying within bounds.
95  // The scaled-and-rotated footprint is bounded by [-Input_Scale / 2, Input_Scale / 2],
96  // so we can offset by [(1 - Input_Scale) / 2].
97  float2 random_offset = (tile_rand3.yz * 2.0f - 1.0f) * (0.5f * (1.0f - input_scale));
98  local_uv += random_offset;
99  // Finally, remap onto [0, 1].
100  local_uv += 0.5f;
101
102  BurleyPatchTransform patch;
103  patch.uv = local_uv;
104  patch.dx = sample_dx;
105  patch.dy = sample_dy;
106  patch.uv_to_patch = rot * (0.5f * input_scale);
107  return patch;
108}
109
110float4 burley_sample_patch(texture2D tex, BurleyPatchTransform patch) {
111  return tex.SampleGrad(
112      aniso4_trilinear_repeat_s, patch.uv, patch.dx, patch.dy);
113}
114#endif  // _BURLEY_TILING || TRIPLANAR_BURLEY
115
116#if defined(_BURLEY_TILING)
117struct BurleyTilingContext {
118  BurleyPatchTransform patch_0;
119  BurleyPatchTransform patch_1;
120  BurleyPatchTransform patch_2;
121  float3 weights;
122  float2 base_uv;
123  float uv_scale;
124};
125
126static BurleyTilingContext _burley_ctx;
127
128void burley_tiling_setup(float2 base_uv) {
129  _burley_ctx.base_uv = base_uv;
130  float2 uv = base_uv - 0.5;
131  // Scale so that any rotation remains within [0, 1] bounds.
132  uv *= TWO_OVER_SQRT_3;
133  uv /= _Burley_Tiling_Output_Scale;
134  _burley_ctx.uv_scale = TWO_OVER_SQRT_3 / _Burley_Tiling_Output_Scale;
135  float3 hex_coord = cart_to_hex(uv);
136  float2 tri_coord = hex_coord.yz;
137  float2 tri_cell = floor(tri_coord);
138  float2 tri_frac = tri_coord - tri_cell;
139  float2 vertex_0;
140  float2 vertex_1;
141  float2 vertex_2;
142  float3 baryc;
143  if (tri_frac.x + tri_frac.y < 1.0f) {
144    vertex_0 = tri_cell;
145    vertex_1 = tri_cell + float2(1.0f, 0.0f);
146    vertex_2 = tri_cell + float2(0.0f, 1.0f);
147    baryc = float3(1.0f - (tri_frac.x + tri_frac.y), tri_frac.x, tri_frac.y);
148  } else {
149    vertex_0 = tri_cell + 1.0f;
150    vertex_1 = tri_cell + float2(0.0f, 1.0f);
151    vertex_2 = tri_cell + float2(1.0f, 0.0f);
152    baryc = float3(tri_frac.x + tri_frac.y - 1.0f, 1.0f - tri_frac.x, 1.0f - tri_frac.y);
153  }
154
155  float input_scale = _Burley_Tiling_Input_Scale;
156  _burley_ctx.weights = burley_apply_blend_gamma(baryc, _Burley_Tiling_Blend_Gamma);
157  float2 uv_dx = ddx(uv);
158  float2 uv_dy = ddy(uv);
159  _burley_ctx.patch_0 = burley_make_patch_transform(uv, uv_dx, uv_dy, vertex_0, input_scale);
160  _burley_ctx.patch_1 = burley_make_patch_transform(uv, uv_dx, uv_dy, vertex_1, input_scale);
161  _burley_ctx.patch_2 = burley_make_patch_transform(uv, uv_dx, uv_dy, vertex_2, input_scale);
162}
163#endif  // _BURLEY_TILING
164
165#if defined(_BURLEY_TILING_HEIGHTMAP) || defined(_BURLEY_TILING_AMBIENT_OCCLUSION)
166float burley_sample_scalar(texture2D tex, texture2D lut) {
167  float4 patch_0 = burley_sample_patch(tex, _burley_ctx.patch_0);
168  float4 patch_1 = burley_sample_patch(tex, _burley_ctx.patch_1);
169  float4 patch_2 = burley_sample_patch(tex, _burley_ctx.patch_2);
170  float4 gaussian_blend = patch_0 * _burley_ctx.weights.x +
171      patch_1 * _burley_ctx.weights.y +
172      patch_2 * _burley_ctx.weights.z;
173  return burley_degaussianize(
174      lut,
175      burley_apply_soft_clipping(gaussian_blend.rgb, _burley_ctx.weights),
176      false).r;
177}
178#endif  // _BURLEY_TILING_HEIGHTMAP || _BURLEY_TILING_AMBIENT_OCCLUSION
179
180#endif  // __BURLEY_INC