yum-archive/Tooner
A toon shader for Unity's BIRP.
git clone https://git.yummers.dev/yum-archive/Tooner
ccbf413
master
1#include "UnityCG.cginc" 2 3#include "globals.cginc" 4#include "iq_sdf.cginc" 5#include "interpolators.cginc" 6 7#ifndef __ZWRITE_ABOMINATION_INC 8#define __ZWRITE_ABOMINATION_INC 9 10#if defined(_GIMMICK_ZWRITE_ABOMINATION) 11 12struct ZWriteAbominationPBR { 13 float3 worldPos; 14 float3 normal; 15 float4 albedo; 16 float metallic; 17 float roughness; 18 float depth; 19}; 20 21#define BODY_PART_BODY 0 22#define BODY_PART_LENS 1 23#define BODY_PART_EYE_WHITE 2 24#define BODY_PART_PUPIL 3 25#define BODY_PART_ARM 4 26#define BODY_PART_LEG 5 27#define BODY_PART_MOUTH 6 28#define BODY_PART_DENIM 7 29#define BODY_PART_DENIM_STRAP 7 30#define BODY_PART_EYE_STRAP 8 31 32float zwrite_abomination_map(float3 p, out uint body_part) { 33 float epsilon = 1E-4; 34 float d; 35 float s = _Gimmick_ZWrite_Abomination_Global_Scale; 36 p = p / s; // Scale all positions first 37 38 // Capsule representing body 39 { 40 float body_half_height = _Gimmick_ZWrite_Abomination_Body_Half_Height; 41 float body_radius = _Gimmick_ZWrite_Abomination_Body_Radius; 42 float body_d = distance_from_capsule(p, float3(0, -body_half_height, 0), float3(0, body_half_height, 0), body_radius); 43 body_part = BODY_PART_BODY; 44 d = body_d; 45 } 46 47 // Capsule representing denim 48 { 49 float3 denim_center = _Gimmick_ZWrite_Abomination_Denim_Center; 50 float3 pp = p - denim_center; 51 float denim_half_height = _Gimmick_ZWrite_Abomination_Denim_Half_Height; 52 float denim_radius = _Gimmick_ZWrite_Abomination_Denim_Radius; 53 float denim_d = distance_from_capsule(pp, float3(0, -denim_half_height, 0), float3(0, denim_half_height, 0), denim_radius); 54 body_part = denim_d < d ? BODY_PART_DENIM : body_part; 55 d = min(d, denim_d); 56 } 57 58 // Torus representing denim strap 59 { 60 float3 strap_center = _Gimmick_ZWrite_Abomination_Denim_Strap_Center; 61 float3 pp = p; 62 pp.x = abs(pp.x); 63 pp -= strap_center; 64 float theta = _Gimmick_ZWrite_Abomination_Denim_Strap_Z_Theta; 65 float2x2 rot = float2x2(float2(cos(theta), -sin(theta)), float2(sin(theta), cos(theta))); 66 pp.xy = mul(rot, pp.xy); 67 pp = pp.zyx; 68 69 float strap_theta = _Gimmick_ZWrite_Abomination_Denim_Strap_Theta; 70 float strap_ra = _Gimmick_ZWrite_Abomination_Denim_Strap_RA; 71 float strap_rb = _Gimmick_ZWrite_Abomination_Denim_Strap_RB; 72 float strap_d = distance_from_capped_torus(pp, float2(sin(strap_theta), cos(strap_theta)), strap_ra, strap_rb); 73 body_part = strap_d < d ? BODY_PART_DENIM_STRAP : body_part; 74 d = min(d, strap_d); 75 } 76 77 // Metal ring around the eye 78 { 79 float3 eye_center = _Gimmick_ZWrite_Abomination_Eye_Center; 80 float3 pp = (p - eye_center).xzy; 81 float lens_radius = _Gimmick_ZWrite_Abomination_Lens_Radius; 82 float lens_depth = _Gimmick_ZWrite_Abomination_Lens_Depth; 83 float lens_thickness = _Gimmick_ZWrite_Abomination_Lens_Thickness; 84 float lens_d0 = distance_from_capped_cylinder(pp, lens_depth, lens_radius); 85 float lens_d1 = distance_from_capped_cylinder(pp, lens_depth + 0.1, lens_radius - lens_thickness); 86 float lens_d = op_sub(lens_d1, lens_d0); 87 body_part = lens_d < d ? BODY_PART_LENS : body_part; 88 d = min(d, lens_d); 89 90 // White of eye 91 float eye_white_d = distance_from_capped_cylinder(pp, lens_depth * 0.5, lens_radius - lens_thickness * 0.5); 92 body_part = eye_white_d < d ? BODY_PART_EYE_WHITE : body_part; 93 d = min(d, eye_white_d); 94 95 // Pupil 96 float pupil_d = length(pp) - _Gimmick_ZWrite_Abomination_Pupil_Radius; 97 body_part = pupil_d < d ? BODY_PART_PUPIL : body_part; 98 d = min(d, pupil_d); 99 } 100 101 // Strap holding metal ring to head 102 { 103 float3 pp = p; 104 pp.y -= _Gimmick_ZWrite_Abomination_Eye_Center.y; 105 float strap_d = distance_from_capped_cylinder(pp, _Gimmick_ZWrite_Abomination_Lens_Strap_Height, _Gimmick_ZWrite_Abomination_Body_Radius * 1.001); 106 body_part = strap_d < d ? BODY_PART_EYE_STRAP : body_part; 107 d = min(d, strap_d); 108 } 109 110 // Mouth 111 { 112 float3 mouth_center = _Gimmick_ZWrite_Abomination_Mouth_Center; 113 float3 pp = p; 114 pp -= mouth_center; 115 pp.y = -pp.y; 116 float theta = _Gimmick_ZWrite_Abomination_Mouth_Theta; 117 float ra = _Gimmick_ZWrite_Abomination_Mouth_RA; 118 float rb = _Gimmick_ZWrite_Abomination_Mouth_RB; 119 float mouth_d = distance_from_capped_torus(pp, float2(sin(theta), cos(theta)), ra, rb); 120 body_part = mouth_d < d ? BODY_PART_MOUTH : body_part; 121 d = min(d, mouth_d); 122 } 123 124 // Arms 125 { 126 float3 arm_center = _Gimmick_ZWrite_Abomination_Arm_Center; 127 float3 pp = p; 128 pp.x = abs(pp.x); // Symmetrize along x axis 129 pp -= arm_center; 130 float arm_radius = _Gimmick_ZWrite_Abomination_Arm_Radius; 131 float arm_half_length = _Gimmick_ZWrite_Abomination_Arm_Half_Length; 132 float arm_d = distance_from_capsule(pp, float3(-arm_half_length, 0, 0), float3(arm_half_length, 0, 0), arm_radius); 133 body_part = arm_d < d ? BODY_PART_ARM : body_part; 134 d = min(d, arm_d); 135 } 136 137 // Legs 138 { 139 float3 leg_center = _Gimmick_ZWrite_Abomination_Leg_Center; 140 float3 pp = p.yxz; 141 pp.y = abs(pp.y); // Symmetrize 142 pp -= leg_center; 143 float leg_radius = _Gimmick_ZWrite_Abomination_Leg_Radius; 144 float leg_half_length = _Gimmick_ZWrite_Abomination_Leg_Half_Length; 145 float leg_d = distance_from_capsule(pp, float3(-leg_half_length, 0, 0), float3(leg_half_length, 0, 0), leg_radius); 146 body_part = leg_d < d ? BODY_PART_LEG : body_part; 147 d = min(d, leg_d); 148 } 149 150 return d * s; 151} 152 153// TODO tetrahedral normals 154float3 zwrite_abomination_normal(float3 p) { 155 float3 epsilon = float3(1, 0, 0) * _Gimmick_ZWrite_Abomination_Normal_Epsilon; 156 uint body_part; 157 return normalize(float3( 158 zwrite_abomination_map(p + epsilon.xyy, body_part) - zwrite_abomination_map(p - epsilon.xyy, body_part), 159 zwrite_abomination_map(p + epsilon.yxy, body_part) - zwrite_abomination_map(p - epsilon.yxy, body_part), 160 zwrite_abomination_map(p + epsilon.yyx, body_part) - zwrite_abomination_map(p - epsilon.yyx, body_part) 161 )); 162} 163 164ZWriteAbominationPBR zwrite_abomination(in v2f i) 165{ 166 float3 cam_pos = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1)); 167 float3 rd = normalize(i.objPos - cam_pos); 168 float3 ro = cam_pos + rd * _Gimmick_ZWrite_Abomination_Initial_Step_Size; 169 float l = length(ro); 170 float scale_factor = pow(l, 1.5); 171 ro /= scale_factor; 172 ro += _Gimmick_ZWrite_Abomination_Global_Offset; 173 174 float3 forward_axis = float3(0, 0, 1); 175 // We apply a factor of -1 to shift the result forward by a phase shift of pi. 176 float cos_t = -dot(normalize(rd.xz), forward_axis.xz); 177 // We want to get sin(t) using the identity: 178 // || a x b || = || a || || b || sin(t) 179 // For normal vectors, this simplifies to: 180 // || a x b || = sin(t) 181 // The issue is that the norm operator loses the sign. 182 // We can estimate the sign by assuming that `rd` and `forward_axis` are on 183 // the xz plane. 184 // If that's the case, then the cross product is necessarily constrained to 185 // the y axis. 186 float sin_t_sign = sign(cross(rd, forward_axis).y); 187 // Here we use the identity: 188 // sin(t) = sqrt(1 - cos(t)^2) 189 // We simply apply the sign correction `sin_t_sign` to the result. 190 // We then invert it, since the goal is not to amplify the rotation, but 191 // to negate it. 192 // Finally, we add a phase correction to make the abomination face us. 193 float sin_t = sqrt(1 - cos_t * cos_t) * sin_t_sign; 194 float2x2 face_me_rot = float2x2(cos_t, -sin_t, sin_t, cos_t); 195 float2x2 face_me_rot_inv = float2x2(cos_t, sin_t, -sin_t, cos_t); 196 ro.xz = mul(face_me_rot, ro.xz); 197 rd.xz = mul(face_me_rot, rd.xz); 198 199 // Raytrace ro onto sphere containing sim 200 201#if 1 202 { 203 bool no_intersection = false; 204 float distance_to_sphere = 1E6; 205 float circle_radius = 0.5 * scale_factor; 206 { 207 float3 l = ro; 208 float a = 1; 209 float b = 2 * dot(rd, l); 210 float c = dot(l, l) - circle_radius * circle_radius; 211 float t0, t1; 212 if (solveQuadratic(a, b, c, t0, t1)) { 213 no_intersection = (t0 < 0) * (t1 < 0); 214 const bool inside_sphere = (t0 < 0) * (t1 > 0); 215 if (!inside_sphere) { 216 distance_to_sphere = no_intersection ? distance_to_sphere : min(max(t0, 0), max(t1, 0)); 217 ro += distance_to_sphere * rd; 218 } 219 } 220 } 221 clip(no_intersection ? -1 : 1); 222 } 223#endif 224 225 const float MIN_HIT_DIST = _Gimmick_ZWrite_Abomination_Min_Hit_Dist; 226 const float MAX_DIST = 1; 227 const uint MARCH_STEPS = _Gimmick_ZWrite_Abomination_March_Steps; 228 float total_dist = 0; 229 float d; 230 uint body_part; 231 for (uint ii = 0; ii < MARCH_STEPS; ++ii) { 232 float3 p = ro + rd * total_dist; 233 d = zwrite_abomination_map(p, body_part); 234 if (d < MIN_HIT_DIST) { 235 break; 236 } 237 total_dist += d; 238 } 239 240 ZWriteAbominationPBR pbr; 241 if (d < MIN_HIT_DIST) { 242 float3 p = ro + rd * total_dist; 243 pbr.worldPos = mul(unity_ObjectToWorld, float4(p, 1.0)).xyz; 244 245 float3 c = 1; 246 c = (body_part == BODY_PART_BODY) ? _Gimmick_ZWrite_Abomination_Skin_Color : c; 247 c = (body_part == BODY_PART_ARM) ? _Gimmick_ZWrite_Abomination_Skin_Color : c; 248 c = (body_part == BODY_PART_LEG) ? _Gimmick_ZWrite_Abomination_Skin_Color : c; 249 c = (body_part == BODY_PART_LENS) ? _Gimmick_ZWrite_Abomination_Lens_Color : c; 250 c = (body_part == BODY_PART_EYE_WHITE) ? 1 : c; 251 c = (body_part == BODY_PART_PUPIL) ? 0 : c; 252 c = (body_part == BODY_PART_MOUTH) ? 0 : c; 253 c = (body_part == BODY_PART_DENIM) ? _Gimmick_ZWrite_Abomination_Denim_Color : c; 254 c = (body_part == BODY_PART_DENIM_STRAP) ? _Gimmick_ZWrite_Abomination_Denim_Color : c; 255 c = (body_part == BODY_PART_EYE_STRAP) ? _Gimmick_ZWrite_Abomination_Lens_Strap_Color : c; 256 257 float metallic = 0; 258 metallic = (body_part == BODY_PART_BODY) ? _Gimmick_ZWrite_Abomination_Skin_Metallic : metallic; 259 metallic = (body_part == BODY_PART_ARM) ? _Gimmick_ZWrite_Abomination_Skin_Metallic : metallic; 260 metallic = (body_part == BODY_PART_LEG) ? _Gimmick_ZWrite_Abomination_Skin_Metallic : metallic; 261 metallic = (body_part == BODY_PART_LENS) ? _Gimmick_ZWrite_Abomination_Lens_Metallic : metallic; 262 metallic = (body_part == BODY_PART_EYE_WHITE) ? 0 : metallic; 263 metallic = (body_part == BODY_PART_PUPIL) ? 0 : metallic; 264 metallic = (body_part == BODY_PART_MOUTH) ? 0 : metallic; 265 metallic = (body_part == BODY_PART_DENIM) ? _Gimmick_ZWrite_Abomination_Denim_Metallic : metallic; 266 metallic = (body_part == BODY_PART_DENIM_STRAP) ? _Gimmick_ZWrite_Abomination_Denim_Metallic : metallic; 267 metallic = (body_part == BODY_PART_EYE_STRAP) ? _Gimmick_ZWrite_Abomination_Lens_Strap_Metallic : metallic; 268 269 float roughness = 0; 270 roughness = (body_part == BODY_PART_BODY) ? _Gimmick_ZWrite_Abomination_Skin_Roughness : roughness; 271 roughness = (body_part == BODY_PART_ARM) ? _Gimmick_ZWrite_Abomination_Skin_Roughness : roughness; 272 roughness = (body_part == BODY_PART_LEG) ? _Gimmick_ZWrite_Abomination_Skin_Roughness : roughness; 273 roughness = (body_part == BODY_PART_LENS) ? _Gimmick_ZWrite_Abomination_Lens_Roughness : roughness; 274 roughness = (body_part == BODY_PART_EYE_WHITE) ? 0 : roughness; 275 roughness = (body_part == BODY_PART_PUPIL) ? 0 : roughness; 276 roughness = (body_part == BODY_PART_MOUTH) ? 1 : roughness; 277 roughness = (body_part == BODY_PART_DENIM) ? _Gimmick_ZWrite_Abomination_Denim_Roughness : roughness; 278 roughness = (body_part == BODY_PART_DENIM_STRAP) ? _Gimmick_ZWrite_Abomination_Denim_Roughness : roughness; 279 roughness = (body_part == BODY_PART_EYE_STRAP) ? _Gimmick_ZWrite_Abomination_Lens_Strap_Roughness : roughness; 280 281 pbr.albedo = float4(c, 1); 282 pbr.metallic = metallic; 283 pbr.roughness = roughness; 284 float3 normal = zwrite_abomination_normal(p); 285 // TODO inverse of this matrix? 286 normal.xz = mul(face_me_rot_inv, normal.xz); 287 pbr.normal = UnityObjectToWorldNormal(normal); 288 float4 clip_pos = mul(UNITY_MATRIX_VP, float4(mul(unity_ObjectToWorld, float4(p, 1.0)))); 289 pbr.depth = clip_pos.z / clip_pos.w; 290 } else { 291 pbr.worldPos = i.worldPos; 292 pbr.albedo = 0; 293 pbr.metallic = 0; 294 pbr.roughness = 1; 295 pbr.normal = i.normal; 296 pbr.depth = -1E6; 297 } 298 299 return pbr; 300} 301 302#endif // _GIMMICK_ZWRITE_ABOMINATION 303#endif // __ZWRITE_ABOMINATION_INC