yum/3ner
A toon shader for Unity's BIRP.
git clone https://git.yummers.dev/yum/3ner
69f2735
master
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