Common.cginc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. #define PI 3.141592653
  2. #define PI2 6.283185307
  3. // *****
  4. // begin http://theorangeduck.com/page/avoiding-shader-conditionals
  5. // *****
  6. float4 when_eq(float4 x, float4 y) {
  7. return 1.0 - abs(sign(x - y));
  8. }
  9. float4 when_neq(float4 x, float4 y) {
  10. return abs(sign(x - y));
  11. }
  12. float4 when_gt(float4 x, float4 y) {
  13. return max(sign(x - y), 0.0);
  14. }
  15. float4 when_lt(float4 x, float4 y) {
  16. return max(sign(y - x), 0.0);
  17. }
  18. float4 when_ge(float4 x, float4 y) {
  19. return 1.0 - when_lt(x, y);
  20. }
  21. float4 when_le(float4 x, float4 y) {
  22. return 1.0 - when_gt(x, y);
  23. }
  24. float4 and(float4 a, float4 b) {
  25. return a * b;
  26. }
  27. float4 or(float4 a, float4 b) {
  28. return min(a + b, 1.0);
  29. }
  30. float4 xor(float4 a, float4 b) {
  31. return (a + b) % 2.0;
  32. }
  33. float4 not(float4 a) {
  34. return 1.0 - a;
  35. }
  36. // *****
  37. // end http://theorangeduck.com/page/avoiding-shader-conditionals
  38. // *****
  39. float2 prepare(float2 uv, float distanceFromCameraPlane) {
  40. float2 pos = float2(uv.x * _XScale, uv.y * _YScale);
  41. // figure out the size of a pixel in world units at the z depth of the
  42. // shape, which is sort of the same as fwidth(pos) but works on more
  43. // platforms. won't look right in 3D unless the shape is billboarded,
  44. // but this is Shapes2D and not Shapes3D so we'll have to live with it.
  45. if (_PixelSize == 0) {
  46. if (unity_OrthoParams.w == 0) {
  47. // get the vertical fov, thanks http://answers.unity3d.com/questions/770838/how-can-i-extract-the-fov-information-from-the-pro.html
  48. float t = unity_CameraProjection._m11;
  49. float fov = atan(1.0 / t);
  50. // get the distance from the current point world position to the
  51. // camera's plane, and then figure out the pixel size based on the
  52. // fov at that distance. probably not actually "correct" but it
  53. // seems to work well regardless of fov or camera angle/position.
  54. float h = tan(fov) * distanceFromCameraPlane * 2;
  55. _PixelSize = h / _ScreenParams.y;
  56. } else {
  57. _PixelSize = (_ScreenParams.z - 1) * unity_OrthoParams.x * 2;
  58. }
  59. }
  60. // put a little resolution-independent AA on things if desired
  61. if (_Blur == 0) {
  62. // fwidth is not supported on all GLES platforms, so we are now using
  63. // the _PixelSize method which should be available everywhere (though
  64. // only when using an orthographic camera)
  65. // float2 aa = fwidth(pos);
  66. // _Blur = length(aa);
  67. _Blur = sqrt(_PixelSize * _PixelSize * 2);
  68. if (_OutlineSize > 0) {
  69. // subtract the AA blur from the outline so sizes stay mostly
  70. // correct and outlines don't look too thick when scaled down.
  71. // don't clamp _OutlineSize to 0 or it will break comparisons in
  72. // other places (should fix that at some point).
  73. _OutlineSize -= _Blur;
  74. }
  75. }
  76. float min_dim = min(_XScale, _YScale) / 2;
  77. // blur on the outside of the shape in world coords
  78. _OuterBlur = max(min(_Blur, min_dim - _OutlineSize), 0);
  79. // blur on the inside of the outline in world coords
  80. _InnerBlur = max(min(_OuterBlur, min_dim - _OuterBlur - _OutlineSize), 0);
  81. // return the local position of the pixel in the quad centered at 0, 0
  82. return pos;
  83. }
  84. // get the 2-dimensional cross product
  85. float cross2(float2 p1, float2 p2) {
  86. return p1.x * p2.y - p1.y * p2.x;
  87. }
  88. // returns the distance from a point to a line described by two points
  89. float distance_to_line(float2 pos, float2 p1, float2 p2) {
  90. return abs((p2.x - p1.x) * (p1.y - pos.y) - (p1.x - pos.x) * (p2.y - p1.y))
  91. / distance(p1, p2);
  92. }
  93. // http://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
  94. float distance_to_line_segment(float2 pos, float2 p1, float2 p2) {
  95. float l2 = pow(distance(p1, p2), 2);
  96. float t = clamp(dot(pos - p1, p2 - p1) / l2, 0, 1);
  97. float2 projection = p1 + t * (p2 - p1);
  98. return distance(pos, projection);
  99. }
  100. // returns 1 on the ellipse and <1 inside the ellipse
  101. float ellipse(float2 pos, float2 radii) {
  102. return pow(pos[0], 2) / pow(radii[0], 2) + pow(pos[1], 2) / pow(radii[1], 2);
  103. }
  104. // returns the distance to the ellipse from the center at angle theta
  105. float ellipse_distance(float theta, float2 radii) {
  106. return (radii[0] * radii[1]) / sqrt(pow(radii[0], 2)
  107. * pow(sin(theta), 2) + pow(radii[1], 2) * pow(cos(theta), 2));
  108. }
  109. float2 point_on_ellipse(float theta, float2 radii) {
  110. float x = (radii.x * radii.y) / sqrt(pow(radii.y, 2) + pow(radii.x, 2) * pow(tan(theta), 2));
  111. return float2(x, sqrt(1 - pow(x / radii.x, 2)) * radii.y);
  112. }
  113. float2 rotate_fill(float2 fpos) {
  114. float2 old_fpos = fpos;
  115. fpos.x = old_fpos.x * cos(_FillRotation)
  116. - old_fpos.y * sin(_FillRotation);
  117. fpos.y = old_fpos.x * sin(_FillRotation)
  118. + old_fpos.y * cos(_FillRotation);
  119. return fpos;
  120. }
  121. // returns the fill color for the given uv point in the quad.
  122. // @param uv the uv coords from -0.5 to 0.5
  123. fixed4 fill(float2 uv) {
  124. float2 fpos = float2(uv.x * _XScale, uv.y * _YScale);
  125. #if FILL_NONE
  126. return fixed4(0, 0, 0, 0);
  127. #elif FILL_OUTLINE_COLOR
  128. return _OutlineColor;
  129. #elif FILL_SOLID_COLOR
  130. // fill with the fill color
  131. return _FillColor;
  132. #elif FILL_GRADIENT
  133. // gradient
  134. // todo - simplify and try to remove conditionals
  135. fpos = rotate_fill(fpos);
  136. fpos += float2(_FillOffsetX, _FillOffsetY);
  137. float gmin = 0, gmax = 0, current = 0;
  138. if (_GradientType == 0) {
  139. // linear gradient
  140. if (_GradientAxis == 0) {
  141. gmin = -_XScale / 2 + _GradientStart * _XScale;
  142. gmax = _XScale / 2;
  143. current = fpos.x;
  144. } else {
  145. gmin = -_YScale / 2 + _GradientStart * _YScale;
  146. gmax = _YScale / 2;
  147. current = fpos.y;
  148. }
  149. } else if (_GradientType == 1) {
  150. // cylindrical gradient
  151. if (_GradientAxis == 0) {
  152. gmin = _GradientStart / 2 * _XScale;
  153. gmax = _XScale / 2;
  154. current = abs(fpos.x);
  155. } else {
  156. gmin = _GradientStart / 2 * _YScale;
  157. gmax = _YScale / 2;
  158. current = abs(fpos.y);
  159. }
  160. } else {
  161. // radial gradient
  162. gmax = length(float2(_XScale, _YScale)) / 2;
  163. gmin = gmax * _GradientStart;
  164. current = length(fpos);
  165. }
  166. if (current < gmin)
  167. return _FillColor;
  168. if (gmax == gmin)
  169. return _FillColor2;
  170. return lerp(_FillColor, _FillColor2, (current - gmin) / (gmax - gmin));
  171. #elif FILL_GRID
  172. // grid - background is _FillColor, lines are _FillColor2
  173. fpos = rotate_fill(fpos);
  174. fpos += float2(_FillOffsetX, _FillOffsetY);
  175. // float edge = min(fwidth(fpos) * 2, _GridSize);
  176. float edge = min(_PixelSize * 2, _GridSize);
  177. // webgl breaks with component-wise ops for some reason and only shows vertical
  178. // grid lines...sigh
  179. // float2 p = abs(frac(fpos / _GridSize) * _GridSize * 2 - _GridSize);
  180. // float2 mix = smoothstep(_GridSize - _LineSize - edge, _GridSize - _LineSize, p);
  181. // return lerp(_FillColor, _FillColor2, max(mix.x, mix.y));
  182. float px = abs(frac(fpos.x / _GridSize) * _GridSize * 2 - _GridSize);
  183. float py = abs(frac(fpos.y / _GridSize) * _GridSize * 2 - _GridSize);
  184. float mixx = smoothstep(_GridSize - _LineSize - edge, _GridSize - _LineSize, px);
  185. float mixy = smoothstep(_GridSize - _LineSize - edge, _GridSize - _LineSize, py);
  186. return lerp(_FillColor, _FillColor2, max(mixx, mixy));
  187. #elif FILL_CHECKERBOARD
  188. // checkerboard
  189. fpos = rotate_fill(fpos);
  190. fpos += float2(_FillOffsetX, _FillOffsetY);
  191. // float edge = min(fwidth(fpos), _GridSize);
  192. float edge = min(_PixelSize, _GridSize);
  193. float2 p = frac(fpos / _GridSize);
  194. float2 mix = smoothstep(0, edge / _GridSize, p);
  195. float tile = abs(floor(fpos.y / _GridSize) + floor(fpos.x / _GridSize)) % 2;
  196. fixed4 color1 = tile * _FillColor + (1 - tile) * _FillColor2;
  197. fixed4 color2 = tile * _FillColor2 + (1 - tile) * _FillColor;
  198. return lerp(color1, color2, min(mix.x, mix.y));
  199. #elif FILL_STRIPES
  200. // stripes
  201. fpos = rotate_fill(fpos);
  202. fpos += float2(_FillOffsetX, _FillOffsetY);
  203. // float edge = min(fwidth(fpos) * 2, _GridSize);
  204. float edge = min(_PixelSize * 2, _GridSize);
  205. float p = abs(frac(fpos.x / _GridSize) * _GridSize * 2 - _GridSize);
  206. float mix = smoothstep(_GridSize - _LineSize - edge, _GridSize - _LineSize, p);
  207. return lerp(_FillColor, _FillColor2, mix);
  208. #elif FILL_TEXTURE
  209. // texture
  210. fpos = rotate_fill(fpos);
  211. fpos /= float2(_XScale, _YScale);
  212. fpos += float2(0.5, 0.5);
  213. fpos += float2(_FillOffsetX, _FillOffsetY);
  214. fpos /= float2(_FillScaleX, _FillScaleY);
  215. return tex2D(_FillTexture, fpos);
  216. #endif
  217. }
  218. // this seems a little nicer than smoothstep for antialiasing, though
  219. // not necessarily for large amounts of blurring...there are tradeoffs,
  220. // might want to separate blur from antialias
  221. float blur(float edge1, float edge2, float amount) {
  222. return clamp(lerp(0, 1, (amount - edge1) / (edge2 - edge1)), 0, 1);
  223. // return clamp(pow(max(0, amount - edge1), 2) / pow(edge2 - edge1, 2), 0, 1);
  224. // return clamp((amount - edge1) / (edge2 - edge1), 0, 1);
  225. // amount = clamp(amount, edge1, edge2);
  226. // return pow((amount - edge1) / max(edge2 - edge1, 0.0001), 0.9);
  227. // amount = clamp((amount - edge1) / (edge2 - edge1), 0.0, 1.0);
  228. // return amount*amount*amount*(amount*(amount*6 - 15) + 10);
  229. }
  230. fixed4 outline_fill_blend(float dist, fixed4 fill_color, fixed4 outline_color,
  231. float outer_blur, float outline, float inner_blur) {
  232. float mix = blur(outline, inner_blur, dist);
  233. #if FILL_NONE
  234. fixed4 color = outline_color;
  235. color.a *= (1 - mix);
  236. #else
  237. fixed4 color = lerp(outline_color, fill_color, mix);
  238. #endif
  239. float alpha = blur(0, 1, dist / outer_blur);
  240. color.a *= alpha;
  241. return color;
  242. }
  243. fixed4 fill_blend(float dist, fixed4 fill_color, float outer_blur) {
  244. float alpha = blur(0, outer_blur, dist);
  245. fixed4 color = fill_color;
  246. color.a *= alpha;
  247. return color;
  248. }
  249. // given a distance from the very edge of the shape going inwards, returns the
  250. // color that should be at the current point
  251. fixed4 color_from_distance(float dist, fixed4 fill_color, fixed4 outline_color) {
  252. if (_OutlineSize == 0) {
  253. return fill_blend(dist, fill_color, _OuterBlur);
  254. } else {
  255. float outline = _OuterBlur + _OutlineSize;
  256. float inner_blur = outline + _InnerBlur;
  257. return outline_fill_blend(dist, fill_color, outline_color,
  258. _OuterBlur, outline, inner_blur);
  259. }
  260. }