| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- #define PI 3.141592653
- #define PI2 6.283185307
- // *****
- // begin http://theorangeduck.com/page/avoiding-shader-conditionals
- // *****
- float4 when_eq(float4 x, float4 y) {
- return 1.0 - abs(sign(x - y));
- }
- float4 when_neq(float4 x, float4 y) {
- return abs(sign(x - y));
- }
- float4 when_gt(float4 x, float4 y) {
- return max(sign(x - y), 0.0);
- }
- float4 when_lt(float4 x, float4 y) {
- return max(sign(y - x), 0.0);
- }
- float4 when_ge(float4 x, float4 y) {
- return 1.0 - when_lt(x, y);
- }
- float4 when_le(float4 x, float4 y) {
- return 1.0 - when_gt(x, y);
- }
- float4 and(float4 a, float4 b) {
- return a * b;
- }
- float4 or(float4 a, float4 b) {
- return min(a + b, 1.0);
- }
- float4 xor(float4 a, float4 b) {
- return (a + b) % 2.0;
- }
- float4 not(float4 a) {
- return 1.0 - a;
- }
- // *****
- // end http://theorangeduck.com/page/avoiding-shader-conditionals
- // *****
- float2 prepare(float2 uv, float distanceFromCameraPlane) {
- float2 pos = float2(uv.x * _XScale, uv.y * _YScale);
- // figure out the size of a pixel in world units at the z depth of the
- // shape, which is sort of the same as fwidth(pos) but works on more
- // platforms. won't look right in 3D unless the shape is billboarded,
- // but this is Shapes2D and not Shapes3D so we'll have to live with it.
- if (_PixelSize == 0) {
- if (unity_OrthoParams.w == 0) {
- // get the vertical fov, thanks http://answers.unity3d.com/questions/770838/how-can-i-extract-the-fov-information-from-the-pro.html
- float t = unity_CameraProjection._m11;
- float fov = atan(1.0 / t);
- // get the distance from the current point world position to the
- // camera's plane, and then figure out the pixel size based on the
- // fov at that distance. probably not actually "correct" but it
- // seems to work well regardless of fov or camera angle/position.
- float h = tan(fov) * distanceFromCameraPlane * 2;
- _PixelSize = h / _ScreenParams.y;
- } else {
- _PixelSize = (_ScreenParams.z - 1) * unity_OrthoParams.x * 2;
- }
- }
- // put a little resolution-independent AA on things if desired
- if (_Blur == 0) {
- // fwidth is not supported on all GLES platforms, so we are now using
- // the _PixelSize method which should be available everywhere (though
- // only when using an orthographic camera)
- // float2 aa = fwidth(pos);
- // _Blur = length(aa);
- _Blur = sqrt(_PixelSize * _PixelSize * 2);
- if (_OutlineSize > 0) {
- // subtract the AA blur from the outline so sizes stay mostly
- // correct and outlines don't look too thick when scaled down.
- // don't clamp _OutlineSize to 0 or it will break comparisons in
- // other places (should fix that at some point).
- _OutlineSize -= _Blur;
- }
- }
- float min_dim = min(_XScale, _YScale) / 2;
-
- // blur on the outside of the shape in world coords
- _OuterBlur = max(min(_Blur, min_dim - _OutlineSize), 0);
- // blur on the inside of the outline in world coords
- _InnerBlur = max(min(_OuterBlur, min_dim - _OuterBlur - _OutlineSize), 0);
-
- // return the local position of the pixel in the quad centered at 0, 0
- return pos;
- }
- // get the 2-dimensional cross product
- float cross2(float2 p1, float2 p2) {
- return p1.x * p2.y - p1.y * p2.x;
- }
- // returns the distance from a point to a line described by two points
- float distance_to_line(float2 pos, float2 p1, float2 p2) {
- return abs((p2.x - p1.x) * (p1.y - pos.y) - (p1.x - pos.x) * (p2.y - p1.y))
- / distance(p1, p2);
- }
- // http://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
- float distance_to_line_segment(float2 pos, float2 p1, float2 p2) {
- float l2 = pow(distance(p1, p2), 2);
- float t = clamp(dot(pos - p1, p2 - p1) / l2, 0, 1);
- float2 projection = p1 + t * (p2 - p1);
- return distance(pos, projection);
- }
- // returns 1 on the ellipse and <1 inside the ellipse
- float ellipse(float2 pos, float2 radii) {
- return pow(pos[0], 2) / pow(radii[0], 2) + pow(pos[1], 2) / pow(radii[1], 2);
- }
- // returns the distance to the ellipse from the center at angle theta
- float ellipse_distance(float theta, float2 radii) {
- return (radii[0] * radii[1]) / sqrt(pow(radii[0], 2)
- * pow(sin(theta), 2) + pow(radii[1], 2) * pow(cos(theta), 2));
- }
- float2 point_on_ellipse(float theta, float2 radii) {
- float x = (radii.x * radii.y) / sqrt(pow(radii.y, 2) + pow(radii.x, 2) * pow(tan(theta), 2));
- return float2(x, sqrt(1 - pow(x / radii.x, 2)) * radii.y);
- }
- float2 rotate_fill(float2 fpos) {
- float2 old_fpos = fpos;
- fpos.x = old_fpos.x * cos(_FillRotation)
- - old_fpos.y * sin(_FillRotation);
- fpos.y = old_fpos.x * sin(_FillRotation)
- + old_fpos.y * cos(_FillRotation);
- return fpos;
- }
- // returns the fill color for the given uv point in the quad.
- // @param uv the uv coords from -0.5 to 0.5
- fixed4 fill(float2 uv) {
- float2 fpos = float2(uv.x * _XScale, uv.y * _YScale);
-
- #if FILL_NONE
- return fixed4(0, 0, 0, 0);
- #elif FILL_OUTLINE_COLOR
- return _OutlineColor;
- #elif FILL_SOLID_COLOR
- // fill with the fill color
- return _FillColor;
- #elif FILL_GRADIENT
- // gradient
- // todo - simplify and try to remove conditionals
- fpos = rotate_fill(fpos);
- fpos += float2(_FillOffsetX, _FillOffsetY);
- float gmin = 0, gmax = 0, current = 0;
- if (_GradientType == 0) {
- // linear gradient
- if (_GradientAxis == 0) {
- gmin = -_XScale / 2 + _GradientStart * _XScale;
- gmax = _XScale / 2;
- current = fpos.x;
- } else {
- gmin = -_YScale / 2 + _GradientStart * _YScale;
- gmax = _YScale / 2;
- current = fpos.y;
- }
- } else if (_GradientType == 1) {
- // cylindrical gradient
- if (_GradientAxis == 0) {
- gmin = _GradientStart / 2 * _XScale;
- gmax = _XScale / 2;
- current = abs(fpos.x);
- } else {
- gmin = _GradientStart / 2 * _YScale;
- gmax = _YScale / 2;
- current = abs(fpos.y);
- }
- } else {
- // radial gradient
- gmax = length(float2(_XScale, _YScale)) / 2;
- gmin = gmax * _GradientStart;
- current = length(fpos);
- }
- if (current < gmin)
- return _FillColor;
- if (gmax == gmin)
- return _FillColor2;
- return lerp(_FillColor, _FillColor2, (current - gmin) / (gmax - gmin));
- #elif FILL_GRID
- // grid - background is _FillColor, lines are _FillColor2
- fpos = rotate_fill(fpos);
- fpos += float2(_FillOffsetX, _FillOffsetY);
- // float edge = min(fwidth(fpos) * 2, _GridSize);
- float edge = min(_PixelSize * 2, _GridSize);
- // webgl breaks with component-wise ops for some reason and only shows vertical
- // grid lines...sigh
- // float2 p = abs(frac(fpos / _GridSize) * _GridSize * 2 - _GridSize);
- // float2 mix = smoothstep(_GridSize - _LineSize - edge, _GridSize - _LineSize, p);
- // return lerp(_FillColor, _FillColor2, max(mix.x, mix.y));
- float px = abs(frac(fpos.x / _GridSize) * _GridSize * 2 - _GridSize);
- float py = abs(frac(fpos.y / _GridSize) * _GridSize * 2 - _GridSize);
- float mixx = smoothstep(_GridSize - _LineSize - edge, _GridSize - _LineSize, px);
- float mixy = smoothstep(_GridSize - _LineSize - edge, _GridSize - _LineSize, py);
- return lerp(_FillColor, _FillColor2, max(mixx, mixy));
- #elif FILL_CHECKERBOARD
- // checkerboard
- fpos = rotate_fill(fpos);
- fpos += float2(_FillOffsetX, _FillOffsetY);
- // float edge = min(fwidth(fpos), _GridSize);
- float edge = min(_PixelSize, _GridSize);
- float2 p = frac(fpos / _GridSize);
- float2 mix = smoothstep(0, edge / _GridSize, p);
- float tile = abs(floor(fpos.y / _GridSize) + floor(fpos.x / _GridSize)) % 2;
- fixed4 color1 = tile * _FillColor + (1 - tile) * _FillColor2;
- fixed4 color2 = tile * _FillColor2 + (1 - tile) * _FillColor;
- return lerp(color1, color2, min(mix.x, mix.y));
- #elif FILL_STRIPES
- // stripes
- fpos = rotate_fill(fpos);
- fpos += float2(_FillOffsetX, _FillOffsetY);
- // float edge = min(fwidth(fpos) * 2, _GridSize);
- float edge = min(_PixelSize * 2, _GridSize);
- float p = abs(frac(fpos.x / _GridSize) * _GridSize * 2 - _GridSize);
- float mix = smoothstep(_GridSize - _LineSize - edge, _GridSize - _LineSize, p);
- return lerp(_FillColor, _FillColor2, mix);
- #elif FILL_TEXTURE
- // texture
- fpos = rotate_fill(fpos);
- fpos /= float2(_XScale, _YScale);
- fpos += float2(0.5, 0.5);
- fpos += float2(_FillOffsetX, _FillOffsetY);
- fpos /= float2(_FillScaleX, _FillScaleY);
- return tex2D(_FillTexture, fpos);
- #endif
- }
- // this seems a little nicer than smoothstep for antialiasing, though
- // not necessarily for large amounts of blurring...there are tradeoffs,
- // might want to separate blur from antialias
- float blur(float edge1, float edge2, float amount) {
- return clamp(lerp(0, 1, (amount - edge1) / (edge2 - edge1)), 0, 1);
- // return clamp(pow(max(0, amount - edge1), 2) / pow(edge2 - edge1, 2), 0, 1);
- // return clamp((amount - edge1) / (edge2 - edge1), 0, 1);
- // amount = clamp(amount, edge1, edge2);
- // return pow((amount - edge1) / max(edge2 - edge1, 0.0001), 0.9);
- // amount = clamp((amount - edge1) / (edge2 - edge1), 0.0, 1.0);
- // return amount*amount*amount*(amount*(amount*6 - 15) + 10);
- }
- fixed4 outline_fill_blend(float dist, fixed4 fill_color, fixed4 outline_color,
- float outer_blur, float outline, float inner_blur) {
- float mix = blur(outline, inner_blur, dist);
- #if FILL_NONE
- fixed4 color = outline_color;
- color.a *= (1 - mix);
- #else
- fixed4 color = lerp(outline_color, fill_color, mix);
- #endif
- float alpha = blur(0, 1, dist / outer_blur);
- color.a *= alpha;
- return color;
- }
- fixed4 fill_blend(float dist, fixed4 fill_color, float outer_blur) {
- float alpha = blur(0, outer_blur, dist);
- fixed4 color = fill_color;
- color.a *= alpha;
- return color;
- }
- // given a distance from the very edge of the shape going inwards, returns the
- // color that should be at the current point
- fixed4 color_from_distance(float dist, fixed4 fill_color, fixed4 outline_color) {
- if (_OutlineSize == 0) {
- return fill_blend(dist, fill_color, _OuterBlur);
- } else {
- float outline = _OuterBlur + _OutlineSize;
- float inner_blur = outline + _InnerBlur;
- return outline_fill_blend(dist, fill_color, outline_color,
- _OuterBlur, outline, inner_blur);
- }
- }
|