Polygon.cginc 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. #if POLYGON_8
  2. #define MAX_VERTS 8
  3. #elif POLYGON_16
  4. #define MAX_VERTS 16
  5. #elif POLYGON_24
  6. #define MAX_VERTS 24
  7. #elif POLYGON_32
  8. #define MAX_VERTS 32
  9. #elif POLYGON_40
  10. #define MAX_VERTS 40
  11. #elif POLYGON_48
  12. #define MAX_VERTS 48
  13. #elif POLYGON_56
  14. #define MAX_VERTS 56
  15. #elif POLYGON_64 || POLYGON_MAP
  16. #define MAX_VERTS 64
  17. #endif
  18. float4 _Verts[MAX_VERTS];
  19. int _NumVerts; // the number of vertices in the polygon
  20. sampler2D _PolyMap;
  21. // this algorithm is slow and kind of silly to have in a shader, but it works!
  22. // returns <0 if the point is outside the poly.
  23. float get_closest_distance(float2 pos, float2 radii) {
  24. // simplifies the algo - hopefully this value is higher than any real value
  25. float closest_distance = 99999999;
  26. int nodes = 0; // used for testing if the point is inside the poly, see below
  27. // you can't do variable-length loops in webgl (or es2 technically I think),
  28. // hence the constant...so we have to iterate through MAX_VERTS rather than
  29. // _NumVerts which is the number of vertices actually in our poly
  30. for (int i = 0; i < MAX_VERTS; i++) {
  31. // loop_over is 1 when we're past the number of sides in the poly
  32. float loop_over = when_ge(i, _NumVerts);
  33. // each "vert" contains its edge partner in zw
  34. float4 side = _Verts[i];
  35. // todo - try and do angular line joins instead of curved? for concave
  36. // line joins we get curves, but for convex we get angles. :(
  37. float dist = distance_to_line_segment(pos, side.xy, side.zw) + 99999999 * loop_over;
  38. closest_distance = min(dist, closest_distance);
  39. // thanks to http://alienryderflex.com/polygon/. if the number of nodes (points
  40. // where a polygon line crosses the horizontal axis of the test position) to the
  41. // left of the test position is odd, then the point is inside the poly!
  42. // avoid conditionals - they tend to break on some platforms :/
  43. float intersects_y = or(and(when_lt(side.y, pos.y), when_ge(side.w, pos.y)),
  44. and(when_lt(side.w, pos.y), when_ge(side.y, pos.y)));
  45. float node_x = side.x + (pos.y - side.y) / (side.w - side.y) * (side.z - side.x);
  46. nodes += when_lt(node_x, pos.x) * intersects_y * (1 - loop_over);
  47. // I think conditionals based on uniforms are ok? but trying to return
  48. // or break out of this loop early makes galaxy 5 phones hang at the
  49. // unity splash screen. works fine on other platforms I've tested, but
  50. // I don't actually know if it's a speed boost anyway.
  51. // if (i == _NumVerts - 1)
  52. // break;
  53. }
  54. return closest_distance * when_eq(nodes % 2, 1) + -1 * when_neq(nodes % 2, 1);
  55. }
  56. // tells you which side of the line p1->p2 pos is
  57. int point_line_test(float2 pos, float2 p1, float2 p2) {
  58. return when_gt((pos.x - p1.x) * (p2.y - p1.y) - (pos.y - p1.y) * (p2.x - p1.x), 0);
  59. }
  60. fixed4 frag(v2f i) : SV_Target {
  61. float2 pos = prepare(i.uv, i.modelPos.z);
  62. #if !POLYGON_MAP
  63. // dist is < 0 when pos is outside the poly
  64. float dist = get_closest_distance(pos, float2(_XScale, _YScale) / 2);
  65. float is_inside = when_ge(dist, 0);
  66. #else
  67. // use the polygon map texture optimization. the texture gives us the pre-computed
  68. // two closest sides to the center of the texel, and what operation to perform.
  69. fixed4 data = tex2D(_PolyMap, i.uv + 0.5);
  70. // the indices of the two closest lines are in the r & g components,
  71. // with 100 added to the index if the line test result is positive
  72. int index1 = (int) (data.r * 256.0);
  73. int index2 = (int) (data.g * 256.0);
  74. float4 l1 = _Verts[index1];
  75. float4 l2 = _Verts[index2];
  76. // check if the points are inside the lines
  77. int ptest1 = point_line_test(pos, l1.xy, l1.zw);
  78. int ptest2 = point_line_test(pos, l2.xy, l2.zw);
  79. // shortest distance to one of the lines
  80. float dist1 = distance_to_line_segment(pos, l1.xy, l1.zw);
  81. float dist2 = distance_to_line_segment(pos, l2.xy, l2.zw);
  82. float dist = min(dist1, dist2);
  83. // mode == 0, all pixels in the texel are outside the poly
  84. // mode == 1, all pixels in the texel are inside the poly
  85. // mode == 2, must be inside first line in texel
  86. // mode == 3, must be inside both lines
  87. // mode == 4, must be inside at least one of the lines
  88. // mode == 5, must be inside closest line in the texel
  89. int mode = (int) (data.a * 256.0);
  90. float is_inside = when_eq(mode, 1)
  91. + when_eq(mode, 2) * ptest1
  92. + when_eq(mode, 3) * and(ptest1, ptest2)
  93. + when_eq(mode, 4) * or(ptest1, ptest2);
  94. // + when_eq(mode, 5) *
  95. // (when_lt(dist1, dist2) * ptest1
  96. // + when_gt(dist1, dist2) * ptest2
  97. // + when_eq(dist1, dist2) * and(ptest1, ptest2));
  98. #endif
  99. fixed4 color = color_from_distance(dist, fill(i.uv), _OutlineColor) * i.color;
  100. if (_PreMultiplyAlpha == 1)
  101. color.rgb *= color.a;
  102. if (_UseClipRect == 1)
  103. color.a *= UnityGet2DClipping(i.modelPos.xy, _ClipRect);
  104. clip(color.a - 0.001);
  105. return is_inside * color;
  106. }