1// Compatibility #ifdefs needed for parameters 2#ifdef GL_ES 3#define COMPAT_PRECISION mediump 4#else 5#define COMPAT_PRECISION 6#endif 7 8// Parameter lines go here: 9#pragma parameter RETRO_PIXEL_SIZE "Retro Pixel Size" 0.84 0.0 1.0 0.01 10#ifdef PARAMETER_UNIFORM 11// All parameter floats need to have COMPAT_PRECISION in front of them 12uniform COMPAT_PRECISION float RETRO_PIXEL_SIZE; 13#else 14#define RETRO_PIXEL_SIZE 0.84 15#endif 16 17#if defined(VERTEX) 18 19#if __VERSION__ >= 130 20#define COMPAT_VARYING out 21#define COMPAT_ATTRIBUTE in 22#define COMPAT_TEXTURE texture 23#else 24#define COMPAT_VARYING varying 25#define COMPAT_ATTRIBUTE attribute 26#define COMPAT_TEXTURE texture2D 27#endif 28 29#ifdef GL_ES 30#define COMPAT_PRECISION mediump 31#else 32#define COMPAT_PRECISION 33#endif 34 35COMPAT_ATTRIBUTE vec4 VertexCoord; 36COMPAT_ATTRIBUTE vec4 COLOR; 37COMPAT_ATTRIBUTE vec4 TexCoord; 38COMPAT_VARYING vec4 COL0; 39COMPAT_VARYING vec4 TEX0; 40// out variables go here as COMPAT_VARYING whatever 41 42vec4 _oPosition1; 43uniform mat4 MVPMatrix; 44uniform COMPAT_PRECISION int FrameDirection; 45uniform COMPAT_PRECISION int FrameCount; 46uniform COMPAT_PRECISION vec2 OutputSize; 47uniform COMPAT_PRECISION vec2 TextureSize; 48uniform COMPAT_PRECISION vec2 InputSize; 49 50// compatibility #defines 51#define vTexCoord TEX0.xy 52#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize 53#define OutSize vec4(OutputSize, 1.0 / OutputSize) 54 55void main() 56{ 57 gl_Position = MVPMatrix * VertexCoord; 58 TEX0.xy = VertexCoord.xy; 59// Paste vertex contents here: 60} 61 62#elif defined(FRAGMENT) 63 64#if __VERSION__ >= 130 65#define COMPAT_VARYING in 66#define COMPAT_TEXTURE texture 67out vec4 FragColor; 68#else 69#define COMPAT_VARYING varying 70#define FragColor gl_FragColor 71#define COMPAT_TEXTURE texture2D 72#endif 73 74#ifdef GL_ES 75#ifdef GL_FRAGMENT_PRECISION_HIGH 76precision highp float; 77#else 78precision mediump float; 79#endif 80#define COMPAT_PRECISION mediump 81#else 82#define COMPAT_PRECISION 83#endif 84 85uniform COMPAT_PRECISION int FrameDirection; 86uniform COMPAT_PRECISION int FrameCount; 87uniform COMPAT_PRECISION vec2 OutputSize; 88uniform COMPAT_PRECISION vec2 TextureSize; 89uniform COMPAT_PRECISION vec2 InputSize; 90uniform sampler2D Texture; 91COMPAT_VARYING vec4 TEX0; 92// in variables go here as COMPAT_VARYING whatever 93 94// compatibility #defines 95#define Source Texture 96#define vTexCoord TEX0.xy 97 98#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize 99#define OutSize vec4(OutputSize, 1.0 / OutputSize) 100 101// delete all 'params.' or 'registers.' or whatever in the fragment 102float iGlobalTime = float(FrameCount)*0.025; 103vec2 iResolution = OutputSize.xy; 104 105// ============================================================================================ 106// Base-10 digit extraction of Pi using a lovely formula I discovered which 107// has a nice base-10 feel to it! 108// 109// pi/4 = 7/10 + 22/200 - 52/3000 - 312/40000 + 2/5000000 + 2852/6000000 + ... 110// 111// The nth term is given by ImaginaryPart[3(3i+1)^n - (2i-1)^n] / n10^n 112 113// The code represents each part of the imaginary number in the spiral terms 114// using a BIGNUM encoded in a vec4 - i.e. each float uses numbers less than 115// 2^24. This works in C++ with IEEE! But it depends on the vagaries of your 116// GPU and WebGL translation if you see the correct digits of pi. Sorry! 117 118// Thanks to Fabrice for spotting that the 56th digit was wrong. Fixed now! 119 120// Derivation of the formula: 121// 122// Using the logarithmic form of ArcTan(x), find the Taylor series of ArcTan(x) about 1/a 123// 124// ArcTan(x) = ArcTan(1/a) + SumOverN of [Q(n) * a^n * (x-1/a)^n / n(1+a^2)] 125// 126// The 1+a^2 on the bottom is interesting! It suggests values of a of 2, 3 or 7. 127// 128// Q(n) = i * ((-ia - 1)^n - (ia-1)^n)/2 129// 130// Which, for our purposes, we can simplify to just ImaginaryPart[ (ia - 1)^n ] 131// 132// This gives us the final formula. 133// 134// ArcTan(x) - ArcTan(1/a) = 1/n(1+a^2) * (x-1/a)^n * a^n * ImaginaryPart[ (ia - 1)^n ] 135// 136// For judicious choices of x and a we can get useful formulae. For instance: 137// 138// ArcTan(3/4) - ArcTan(1/2) = SumOverN of 1/n10^n * ImaginaryPart[( 2i-1)^n] 139// ArcTan(2/3) - ArcTan(1/3) = SumOverN of 1/n10^n * ImaginaryPart[( 3i-1)^n] 140// ArcTan(0/3) - ArcTan(1/3) = SumOverN of 1/n10^n * ImaginaryPart[(-3i+1)^n] 141// 142// Using the well known ArcTan addition formula 143// 144// ArcTan(3/4) - ArcTan(1/2) = ArcTan(2/11) 145// ArcTan(2/3) - ArcTan(1/3) = ArcTan(3/11) 146// ArcTan(0/3) - ArcTan(1/3) = -ArcTan(1/3) ... this is easy :) 147// 148// Then all we need to get pi is the Machin type formula, pi/4 = 3 ArcTan[1/3] - ArcTan[2/11] 149// 150// QED 151// 152// https://www.wolframalpha.com/input/?i=Sum+for+n+%3D+1+to+100+of+4+Im%5B3(3i%2B1)%5En-(2i-1)%5En%5D%2F(n+10%5En) 153// 154 155// Feeling brave? Try using ivecs for an extra 20 digits. Only works if you 156// have full 32-precision ints... and your WebGL implementation may crash! 157#define USE_INTEGERS 1 158 159#if USE_INTEGERS 160 161const int POW10_PER_COMPONENT = 7; 162const int BASE_FOR_NUMBER = 10000000; 163const int MAX_DIGIT = 60; 164const int MAX_1OVER3_TERMS = 122; 165const int MAX_2OVER11_TERMS = 93; 166 167bool IsZero(ivec4 lo) 168{ 169 return lo.x == 0 && lo.y == 0 && lo.z == 0 && lo.w == 0; 170} 171 172// Returns +1(a>b), 0, -1(a<b) 173int CompareAbsValues(ivec4 a, ivec4 b) 174{ 175 if (a.w > b.w) {return +1;} 176 if (a.w < b.w) {return -1;} 177 178 if (a.z > b.z) {return +1;} 179 if (a.z < b.z) {return -1;} 180 181 if (a.y > b.y) {return +1;} 182 if (a.y < b.y) {return -1;} 183 184 if (a.x > b.x) {return +1;} 185 if (a.x < b.x) {return -1;} 186 187 return 0; 188} 189 190void DivMod(int a, int b, out int out_div, out int out_mod) 191{ 192 if (a == 0) 193 { 194 out_div = 0; 195 out_mod = 0; 196 197 return; 198 } 199 200 out_div = a / b; 201 out_mod = a - out_div * b; 202} 203 204int Mod(int a, int b) 205{ 206 int div = a / b; 207 int mod = a - b * div; 208 209 return mod; 210} 211 212ivec4 Div(ivec4 a, int divisor, out int out_mod) 213{ 214 ivec4 ans = a; 215 216 if (ans.w != 0) 217 { 218 int div_w; 219 int mod_w; 220 221 DivMod(ans.w, divisor, div_w, mod_w); 222 223 ans.w = div_w; 224 ans.z += mod_w * BASE_FOR_NUMBER; 225 } 226 227 if (ans.z != 0) 228 { 229 int div_z; 230 int mod_z; 231 232 DivMod(ans.z, divisor, div_z, mod_z); 233 234 ans.z = div_z; 235 ans.y += mod_z * BASE_FOR_NUMBER; 236 } 237 238 if (ans.y != 0) 239 { 240 int div_y; 241 int mod_y; 242 243 DivMod(ans.y, divisor, div_y, mod_y); 244 245 ans.y = div_y; 246 ans.x += mod_y * BASE_FOR_NUMBER; 247 } 248 249 if (ans.x != 0) 250 { 251 int div_x; 252 int mod_x; 253 254 DivMod(ans.x, divisor, div_x, mod_x); 255 256 ans.x = div_x; 257 258 out_mod = mod_x; 259 } 260 else 261 { 262 out_mod = 0; 263 } 264 265 return ans; 266} 267 268ivec4 Double(ivec4 a) 269{ 270 ivec4 ans = a + a; 271 272 if (ans.x >= BASE_FOR_NUMBER) 273 { 274 ans.x -= BASE_FOR_NUMBER; 275 ans.y += 1; 276 } 277 278 if (ans.y >= BASE_FOR_NUMBER) 279 { 280 ans.y -= BASE_FOR_NUMBER; 281 ans.z += 1; 282 } 283 284 if (ans.z >= BASE_FOR_NUMBER) 285 { 286 ans.z -= BASE_FOR_NUMBER; 287 ans.w += 1; 288 } 289 290 return ans; 291} 292 293ivec4 Treble(ivec4 a) 294{ 295 ivec4 ans = a + a + a; 296 297 if (ans.x >= BASE_FOR_NUMBER) 298 { 299 ans.x -= BASE_FOR_NUMBER; 300 ans.y += 1; 301 302 if (ans.x >= BASE_FOR_NUMBER) 303 { 304 ans.x -= BASE_FOR_NUMBER; 305 ans.y += 1; 306 } 307 } 308 309 if (ans.y >= BASE_FOR_NUMBER) 310 { 311 ans.y -= BASE_FOR_NUMBER; 312 ans.z += 1; 313 314 if (ans.y >= BASE_FOR_NUMBER) 315 { 316 ans.y -= BASE_FOR_NUMBER; 317 ans.z += 1; 318 } 319 } 320 321 if (ans.z >= BASE_FOR_NUMBER) 322 { 323 ans.z -= BASE_FOR_NUMBER; 324 ans.w += 1; 325 326 if (ans.z >= BASE_FOR_NUMBER) 327 { 328 ans.z -= BASE_FOR_NUMBER; 329 ans.w += 1; 330 } 331 } 332 333 return ans; 334} 335 336ivec4 Add(ivec4 a, ivec4 b) 337{ 338 ivec4 ans = a + b; 339 340 if (ans.x >= BASE_FOR_NUMBER) 341 { 342 ans.x -= BASE_FOR_NUMBER; 343 ans.y += 1; 344 } 345 346 if (ans.y >= BASE_FOR_NUMBER) 347 { 348 ans.y -= BASE_FOR_NUMBER; 349 ans.z += 1; 350 } 351 352 if (ans.z >= BASE_FOR_NUMBER) 353 { 354 ans.z -= BASE_FOR_NUMBER; 355 ans.w += 1; 356 } 357 358 return ans; 359} 360 361// a must be > b 362ivec4 Sub(ivec4 a, ivec4 b) 363{ 364 ivec4 ans = a - b; 365 366 if (ans.x < 0) 367 { 368 ans.x += BASE_FOR_NUMBER; 369 ans.y -= 1; 370 } 371 372 if (ans.y < 0) 373 { 374 ans.y += BASE_FOR_NUMBER; 375 ans.z -= 1; 376 } 377 378 if (ans.z < 0) 379 { 380 ans.z += BASE_FOR_NUMBER; 381 ans.w -= 1; 382 } 383 384 return ans; 385} 386 387ivec4 Add(ivec4 a, bool aneg, ivec4 b, bool bneg, out bool out_a_plus_b_neg) 388{ 389 if (aneg == bneg) 390 { 391 out_a_plus_b_neg = aneg; 392 393 return Add(a,b); 394 } 395 396 // Signs are different. 397 int sign = CompareAbsValues(a,b); 398 399 if (sign == 0) 400 { 401 out_a_plus_b_neg = false; 402 403 return ivec4(0,0,0,0); 404 } 405 406 if (sign < 0) 407 { 408 out_a_plus_b_neg = bneg; 409 410 return Sub(b,a); 411 } 412 413 out_a_plus_b_neg = aneg; 414 415 return Sub(a,b); 416} 417 418// Divides by BASE_FOR_NUMBER. 419void ApplyShift(out ivec4 a) 420{ 421 a.x = a.y; 422 a.y = a.z; 423 a.z = a.w; 424 a.w = 0; 425} 426 427// Return Frac(10^power * Abs(num)/denom) 428float GetFractionalPart(ivec4 numerator, int denominator, int power_of_ten) 429{ 430 if (power_of_ten >= 0) 431 { 432 int m; 433 Div(numerator, denominator, m); 434 435 const int MAX_ITERS = MAX_DIGIT / POW10_PER_COMPONENT; 436 437 for (int iter = 0; iter < MAX_ITERS; iter++) 438 { 439 if (power_of_ten < POW10_PER_COMPONENT) 440 { 441 break; 442 } 443 444 m *= BASE_FOR_NUMBER; 445 m = Mod(m, denominator); 446 447 power_of_ten -= POW10_PER_COMPONENT; 448 449 if (m == 0) 450 { 451 return 0.0; 452 } 453 } 454 455 if (power_of_ten >= 4) {m = Mod(10000 * m, denominator); power_of_ten -= 4;} 456 if (power_of_ten >= 2) {m = Mod(100 * m, denominator); power_of_ten -= 2;} 457 if (power_of_ten >= 1) {m = Mod(10 * m, denominator); power_of_ten -= 1;} 458 459 return float(m) / float(denominator); 460 } 461 462 const int NUM_POWERS_OF_10_TO_KEEP = 4; 463 464 // Throw away terms we don't need. 465 const int MAX_ITERS = MAX_DIGIT / POW10_PER_COMPONENT; 466 467 for (int iter = 0; iter < MAX_ITERS; iter++) 468 { 469 if (power_of_ten + POW10_PER_COMPONENT > -NUM_POWERS_OF_10_TO_KEEP) 470 { 471 break; 472 } 473 474 ApplyShift(numerator); 475 476 if (IsZero(numerator)) 477 { 478 return 0.0; 479 } 480 481 power_of_ten += POW10_PER_COMPONENT; 482 } 483 484 // Divide by the denominator to get the fractional part in the wrong place... 485 int the_mod; 486 487 numerator = Div(numerator, denominator, the_mod); 488 489 float ans = float(the_mod) / float(denominator); 490 491 // We can't divide by more than 100 at a time. 492 const int MAX_DIV100_ITERS = (NUM_POWERS_OF_10_TO_KEEP + POW10_PER_COMPONENT) / 2; 493 494 for (int iter = 0; iter < MAX_DIV100_ITERS; iter++) 495 { 496 if (power_of_ten > -2) 497 { 498 break; 499 } 500 501 numerator = Div(numerator, 100, the_mod); 502 503 ans += float(the_mod); 504 ans *= 0.01; 505 506 power_of_ten += 2; 507 } 508 509 // And one more if required. 510 if (power_of_ten == -1) 511 { 512 numerator = Div(numerator, 100, the_mod); 513 514 ans += float(the_mod); 515 ans *= 0.1; 516 } 517 518 return ans - floor(ans); 519} 520 521// Im((2i - 1)^n) / (10^n n) 522float GetNthDigitOfSpiral2(int nth_digit) 523{ 524 int num_terms = 8 + (MAX_2OVER11_TERMS-8) * nth_digit / (MAX_DIGIT-1); 525 int shift = 0; 526 float sum = 0.0; 527 528 ivec4 re = ivec4(1,0,0,0); bool re_neg = true; 529 ivec4 im = ivec4(2,0,0,0); bool im_neg = false; 530 531 for (int term = 1; term < MAX_2OVER11_TERMS; term++) 532 { 533 int shifted_digit = nth_digit - term + shift; 534 535 float f = GetFractionalPart(im, term, shifted_digit); 536 537 if (im_neg) 538 { 539 sum -= f; 540 } 541 else 542 { 543 sum += f; 544 } 545 546 if (im.w*3 < 0 || 547 re.w*3 < 0) 548 { 549 int mod; 550 551 im = Div(im,10,mod); 552 re = Div(re,10,mod); 553 554 shift += 1; 555 } 556 557 bool new_re_neg; 558 bool new_im_neg; 559 560 ivec4 new_re = Add(Double(im), !im_neg, re, !re_neg, new_re_neg); 561 ivec4 new_im = Add(Double(re), re_neg, im, !im_neg, new_im_neg); 562 563 re = new_re; re_neg = new_re_neg; 564 im = new_im; im_neg = new_im_neg; 565 566 if (term == num_terms) 567 { 568 break; 569 } 570 } 571 572 sum = sum - floor(sum); 573 574 return sum; 575} 576 577// Im((3i + 1)^n) / (10^n n) 578float GetNthDigitOfSpiral3(int nth_digit) 579{ 580 int num_terms = 8 + (MAX_1OVER3_TERMS-8) * nth_digit / (MAX_DIGIT-1); 581 int shift = 0; 582 float sum = 0.0; 583 584 ivec4 re = ivec4(1,0,0,0); bool re_neg = false; 585 ivec4 im = ivec4(3,0,0,0); bool im_neg = false; 586 587 for (int term = 1; term < MAX_1OVER3_TERMS; term++) 588 { 589 int shifted_digit = nth_digit - term + shift; 590 591 float f = GetFractionalPart(im, term, shifted_digit); 592 593 if (im_neg) 594 { 595 sum -= f; 596 } 597 else 598 { 599 sum += f; 600 } 601 602 if (im.w*3 < 0 || 603 re.w*3 < 0) 604 { 605 int mod; 606 607 im = Div(im,10,mod); 608 re = Div(re,10,mod); 609 610 shift += 1; 611 } 612 613 bool new_re_neg; 614 bool new_im_neg; 615 616 ivec4 new_re = Add(Treble(im), !im_neg, re, re_neg, new_re_neg); 617 ivec4 new_im = Add(Treble(re), re_neg, im, im_neg, new_im_neg); 618 619 re = new_re; re_neg = new_re_neg; 620 im = new_im; im_neg = new_im_neg; 621 622 if (term == num_terms) 623 { 624 break; 625 } 626 } 627 628 sum = sum - floor(sum); 629 630 return sum; 631} 632 633int GetNthDigitOfPi(int nth_digit) 634{ 635 float a = GetNthDigitOfSpiral3(nth_digit); 636 float b = GetNthDigitOfSpiral2(nth_digit); 637 638 float s = 4.0 * (a*3.0-b); 639 640 s -= floor(s); 641 642 int digit = int(floor(10.0 * s)); 643 644 return digit; 645} 646 647#else 648 649const float POW10_PER_COMPONENT = 5.0; 650const float BASE_FOR_NUMBER = 100000.0; 651const float MAX_DIGIT = 40.0; 652const float MAX_1OVER3_TERMS = 80.0; 653const float MAX_2OVER11_TERMS = 60.0; 654 655bool IsZero(vec4 a) 656{ 657 return a.x == 0.0 && a.y == 0.0 && a.z == 0.0 && a.w == 0.0; 658} 659 660// Returns +1(a>b), 0, -1(a<b) 661float CompareAbsValues(vec4 a, vec4 b) 662{ 663 if (a.w > b.w) {return +1.0;} 664 if (a.w < b.w) {return -1.0;} 665 666 if (a.z > b.z) {return +1.0;} 667 if (a.z < b.z) {return -1.0;} 668 669 if (a.y > b.y) {return +1.0;} 670 if (a.y < b.y) {return -1.0;} 671 672 if (a.x > b.x) {return +1.0;} 673 if (a.x < b.x) {return -1.0;} 674 675 return 0.0; 676} 677 678void DivMod(float a, float b, out float out_div, out float out_mod) 679{ 680 if (a == 0.0) 681 { 682 out_div = 0.0; 683 out_mod = 0.0; 684 685 return; 686 } 687 688 float d = floor(a / b); 689 690 out_div = d; 691 out_mod = a - d * b; 692} 693 694float Mod(float a, float b) 695{ 696 float d = floor(a / b); 697 float mod = a - d * b; 698 699 return mod; 700} 701 702vec4 Div(vec4 a, float divisor, out float out_mod) 703{ 704 vec4 ans = a; 705 706 if (ans.w != 0.0) 707 { 708 float div_w; 709 float mod_w; 710 711 DivMod(ans.w, divisor, div_w, mod_w); 712 713 ans.w = div_w; 714 ans.z += mod_w * BASE_FOR_NUMBER; 715 } 716 717 if (ans.z != 0.0) 718 { 719 float div_z; 720 float mod_z; 721 722 DivMod(ans.z, divisor, div_z, mod_z); 723 724 ans.z = div_z; 725 ans.y += mod_z * BASE_FOR_NUMBER; 726 } 727 728 if (ans.y != 0.0) 729 { 730 float div_y; 731 float mod_y; 732 733 DivMod(ans.y, divisor, div_y, mod_y); 734 735 ans.y = div_y; 736 ans.x += mod_y * BASE_FOR_NUMBER; 737 } 738 739 if (ans.x != 0.0) 740 { 741 float div_x; 742 float mod_x; 743 744 DivMod(ans.x, divisor, div_x, mod_x); 745 746 ans.x = div_x; 747 748 out_mod = mod_x; 749 } 750 else 751 { 752 out_mod = 0.0; 753 } 754 755 return ans; 756} 757 758vec4 Double(vec4 a) 759{ 760 vec4 ans = a + a; 761 762 if (ans.x >= BASE_FOR_NUMBER) 763 { 764 ans.x -= BASE_FOR_NUMBER; 765 ans.y += 1.0; 766 } 767 768 if (ans.y >= BASE_FOR_NUMBER) 769 { 770 ans.y -= BASE_FOR_NUMBER; 771 ans.z += 1.0; 772 } 773 774 if (ans.z >= BASE_FOR_NUMBER) 775 { 776 ans.z -= BASE_FOR_NUMBER; 777 ans.w += 1.0; 778 } 779 780 return ans; 781} 782 783vec4 Treble(vec4 a) 784{ 785 vec4 ans = a + a + a; 786 787 if (ans.x >= BASE_FOR_NUMBER) 788 { 789 ans.x -= BASE_FOR_NUMBER; 790 ans.y += 1.0; 791 792 if (ans.x >= BASE_FOR_NUMBER) 793 { 794 ans.x -= BASE_FOR_NUMBER; 795 ans.y += 1.0; 796 } 797 } 798 799 if (ans.y >= BASE_FOR_NUMBER) 800 { 801 ans.y -= BASE_FOR_NUMBER; 802 ans.z += 1.0; 803 804 if (ans.y >= BASE_FOR_NUMBER) 805 { 806 ans.y -= BASE_FOR_NUMBER; 807 ans.z += 1.0; 808 } 809 } 810 811 if (ans.z >= BASE_FOR_NUMBER) 812 { 813 ans.z -= BASE_FOR_NUMBER; 814 ans.w += 1.0; 815 816 if (ans.z >= BASE_FOR_NUMBER) 817 { 818 ans.z -= BASE_FOR_NUMBER; 819 ans.w += 1.0; 820 } 821 } 822 823 return ans; 824} 825 826vec4 Add(vec4 a, vec4 b) 827{ 828 vec4 ans = a + b; 829 830 if (ans.x >= BASE_FOR_NUMBER) 831 { 832 ans.x -= BASE_FOR_NUMBER; 833 ans.y += 1.0; 834 } 835 836 if (ans.y >= BASE_FOR_NUMBER) 837 { 838 ans.y -= BASE_FOR_NUMBER; 839 ans.z += 1.0; 840 } 841 842 if (ans.z >= BASE_FOR_NUMBER) 843 { 844 ans.z -= BASE_FOR_NUMBER; 845 ans.w += 1.0; 846 } 847 848 return ans; 849} 850 851// a must be > b 852vec4 Sub(vec4 a, vec4 b) 853{ 854 vec4 ans = a - b; 855 856 if (ans.x < 0.0) 857 { 858 ans.x += BASE_FOR_NUMBER; 859 ans.y -= 1.0; 860 } 861 862 if (ans.y < 0.0) 863 { 864 ans.y += BASE_FOR_NUMBER; 865 ans.z -= 1.0; 866 } 867 868 if (ans.z < 0.0) 869 { 870 ans.z += BASE_FOR_NUMBER; 871 ans.w -= 1.0; 872 } 873 874 return ans; 875} 876 877vec4 Add(vec4 a, bool aneg, vec4 b, bool bneg, out bool out_a_plus_b_neg) 878{ 879 if (aneg == bneg) 880 { 881 out_a_plus_b_neg = aneg; 882 883 return Add(a,b); 884 } 885 886 // Signs are different. 887 float sign = CompareAbsValues(a,b); 888 889 if (sign == 0.0) 890 { 891 out_a_plus_b_neg = false; 892 893 return vec4(0.0,0.0,0.0,0.0); 894 } 895 896 if (sign < 0.0) 897 { 898 out_a_plus_b_neg = bneg; 899 900 return Sub(b,a); 901 } 902 903 out_a_plus_b_neg = aneg; 904 905 return Sub(a,b); 906} 907 908// Divides by BASE_FOR_NUMBER. 909void ApplyShift(out vec4 a) 910{ 911 a.x = a.y; 912 a.y = a.z; 913 a.z = a.w; 914 a.w = 0.0; 915} 916 917// Return Frac(10^power * Abs(num)/denom) 918float GetFractionalPart(vec4 numerator, float denominator, float power_of_ten) 919{ 920 if (power_of_ten >= 0.0) 921 { 922 float m; 923 Div(numerator, denominator, m); 924 925 const int MAX_ITERS = int(MAX_DIGIT / POW10_PER_COMPONENT); 926 927 for (int iter = 0; iter < MAX_ITERS; iter++) 928 { 929 if (power_of_ten < POW10_PER_COMPONENT) 930 { 931 break; 932 } 933 934 m *= BASE_FOR_NUMBER; 935 m = Mod(m, denominator); 936 937 power_of_ten -= POW10_PER_COMPONENT; 938 939 if (m == 0.0) 940 { 941 return 0.0; 942 } 943 } 944 945 if (power_of_ten >= 4.0) {m = Mod(10000.0 * m, denominator); power_of_ten -= 4.0;} 946 if (power_of_ten >= 2.0) {m = Mod(100.0 * m, denominator); power_of_ten -= 2.0;} 947 if (power_of_ten >= 1.0) {m = Mod(10.0 * m, denominator); power_of_ten -= 1.0;} 948 949 return float(m) / float(denominator); 950 } 951 952 const float NUM_POWERS_OF_10_TO_KEEP = 4.0; 953 954 // Throw away terms we don't need. 955 const int MAX_ITERS = int(MAX_DIGIT / POW10_PER_COMPONENT); 956 957 for (int iter = 0; iter < MAX_ITERS; iter++) 958 { 959 if (power_of_ten + POW10_PER_COMPONENT > -NUM_POWERS_OF_10_TO_KEEP) 960 { 961 break; 962 } 963 964 ApplyShift(numerator); 965 966 if (IsZero(numerator)) 967 { 968 return 0.0; 969 } 970 971 power_of_ten += POW10_PER_COMPONENT; 972 } 973 974 // Divide by the denominator to get the fractional part in the wrong place... 975 float the_mod; 976 977 numerator = Div(numerator, denominator, the_mod); 978 979 float ans = float(the_mod) / float(denominator); 980 981 // We can't divide by more than 100 at a time. 982 const int MAX_DIV100_ITERS = int(NUM_POWERS_OF_10_TO_KEEP + POW10_PER_COMPONENT) / 2; 983 984 for (int iter = 0; iter < MAX_DIV100_ITERS; iter++) 985 { 986 if (power_of_ten > -2.0) 987 { 988 break; 989 } 990 991 numerator = Div(numerator, 100.0, the_mod); 992 993 ans += float(the_mod); 994 ans *= 0.01; 995 996 power_of_ten += 2.0; 997 } 998 999 // And one more if required. 1000 if (power_of_ten == -1.0) 1001 { 1002 numerator = Div(numerator, 10.0, the_mod); 1003 1004 ans += float(the_mod); 1005 ans *= 0.1; 1006 } 1007 1008 return ans - floor(ans); 1009} 1010 1011// Im((2i - 1)^n) / (10^n n) 1012float GetNthDigitOfSpiral2(float nth_digit) 1013{ 1014 int num_terms = int(8.0 + (MAX_2OVER11_TERMS - 8.0) * nth_digit / (MAX_DIGIT-1.0)); 1015 float shift = 0.0; 1016 float sum = 0.0; 1017 1018 vec4 re = vec4(1.0, 0.0, 0.0, 0.0); bool re_neg = true; 1019 vec4 im = vec4(2.0, 0.0, 0.0, 0.0); bool im_neg = false; 1020 1021 for (int term = 1; term < int(MAX_2OVER11_TERMS); term++) 1022 { 1023 float shifted_digit = nth_digit - float(term) + shift; 1024 1025 float f = GetFractionalPart(im, float(term), shifted_digit); 1026 1027 if (im_neg) 1028 { 1029 sum -= f; 1030 } 1031 else 1032 { 1033 sum += f; 1034 } 1035 1036 if (im.w * 2.0 > BASE_FOR_NUMBER || 1037 re.w * 2.0 > BASE_FOR_NUMBER) 1038 { 1039 float mod; 1040 1041 im = Div(im,10.0,mod); 1042 re = Div(re,10.0,mod); 1043 1044 shift += 1.0; 1045 } 1046 1047 bool new_re_neg; 1048 bool new_im_neg; 1049 1050 vec4 new_re = Add(Double(im), !im_neg, re, !re_neg, new_re_neg); 1051 vec4 new_im = Add(Double(re), re_neg, im, !im_neg, new_im_neg); 1052 1053 re = new_re; re_neg = new_re_neg; 1054 im = new_im; im_neg = new_im_neg; 1055 1056 if (term == num_terms) 1057 { 1058 break; 1059 } 1060 } 1061 1062 sum = sum - floor(sum); 1063 1064 return sum; 1065} 1066 1067// Im((3i + 1)^n) / (10^n n) 1068float GetNthDigitOfSpiral3(float nth_digit) 1069{ 1070 int num_terms = int(8.0 + (MAX_1OVER3_TERMS - 8.0) * nth_digit / (MAX_DIGIT-1.0)); 1071 float shift = 0.0; 1072 float sum = 0.0; 1073 1074 vec4 re = vec4(1.0, 0.0, 0.0, 0.0); bool re_neg = false; 1075 vec4 im = vec4(3.0, 0.0, 0.0, 0.0); bool im_neg = false; 1076 1077 for (int term = 1; term < int(MAX_1OVER3_TERMS); term++) 1078 { 1079 float shifted_digit = nth_digit - float(term) + shift; 1080 1081 float f = GetFractionalPart(im, float(term), shifted_digit); 1082 1083 if (im_neg) 1084 { 1085 sum -= f; 1086 } 1087 else 1088 { 1089 sum += f; 1090 } 1091 1092 if (im.w * 3.0 > BASE_FOR_NUMBER || 1093 re.w * 3.0 > BASE_FOR_NUMBER) 1094 { 1095 float mod; 1096 1097 im = Div(im,10.0,mod); 1098 re = Div(re,10.0,mod); 1099 1100 shift += 1.0; 1101 } 1102 1103 bool new_re_neg; 1104 bool new_im_neg; 1105 1106 vec4 new_re = Add(Treble(im), !im_neg, re, re_neg, new_re_neg); 1107 vec4 new_im = Add(Treble(re), re_neg, im, im_neg, new_im_neg); 1108 1109 re = new_re; re_neg = new_re_neg; 1110 im = new_im; im_neg = new_im_neg; 1111 1112 if (term == num_terms) 1113 { 1114 break; 1115 } 1116 } 1117 1118 sum = sum - floor(sum); 1119 1120 return sum; 1121} 1122 1123int GetNthDigitOfPi(float nth_digit) 1124{ 1125 float a = GetNthDigitOfSpiral3(nth_digit); 1126 float b = GetNthDigitOfSpiral2(nth_digit); 1127 1128 float s = 4.0 * (a*3.0-b); 1129 1130 s -= floor(s); 1131 1132 int digit = int(floor(10.0 * s)); 1133 1134 return digit; 1135} 1136 1137 1138#endif 1139 1140 1141 1142 1143// ============================================================================================ 1144 1145bool Is0To1(float x) 1146{ 1147 return x >= 0.0 && x < 1.0; 1148} 1149 1150bool Is0To1(vec2 uv) 1151{ 1152 return 1153 uv.x >= 0.0 && uv.x < 1.0 && 1154 uv.y >= 0.0 && uv.y < 1.0; 1155} 1156 1157// ============================================================================================ 1158 1159float GetPositionAlongLineSegmentNearestToPoint(vec2 line_segment_a, vec2 line_segment_b, vec2 point) 1160{ 1161 vec2 p = point - line_segment_a; 1162 vec2 l = line_segment_b - line_segment_a; 1163 1164 float dprod = dot(p,l); 1165 float len2 = dot(l,l); 1166 1167 return clamp(dprod / len2, 0.0, 1.0); 1168} 1169 1170float GetPositionAlongLineNearestToPoint(vec2 line_segment_a, vec2 line_segment_b, vec2 point) 1171{ 1172 vec2 p = point - line_segment_a; 1173 vec2 l = line_segment_b - line_segment_a; 1174 1175 float dprod = dot(p,l); 1176 float len2 = dot(l,l); 1177 1178 return dprod / len2; 1179} 1180 1181float GetDistSqFromLineSegmentToPoint(vec2 line_segment_a, vec2 line_segment_b, vec2 point) 1182{ 1183 float t = GetPositionAlongLineSegmentNearestToPoint(line_segment_a, line_segment_b, point); 1184 1185 vec2 to_nearest = point - mix(line_segment_a, line_segment_b, t); 1186 1187 return dot(to_nearest, to_nearest); 1188} 1189 1190vec3 GetPointOnCubicSpline(vec3 cp0, vec3 cp1, vec3 cp2, vec3 cp3, float t) 1191{ 1192 float p = t; 1193 float n = 1.0 - t; 1194 1195 vec3 ans; 1196 1197 ans = cp0 * (n*n*n); 1198 ans += cp1 * (n*n*p*3.0); 1199 ans += cp2 * (n*p*p*3.0); 1200 ans += cp3 * (p*p*p); 1201 1202 return ans; 1203} 1204 1205vec2 GetPointOnCubicSpline(vec2 cp0, vec2 cp1, vec2 cp2, vec2 cp3, float t) 1206{ 1207 float p = t; 1208 float n = 1.0 - t; 1209 1210 vec2 ans; 1211 1212 ans = cp0 * (n*n*n); 1213 ans += cp1 * (n*n*p*3.0); 1214 ans += cp2 * (n*p*p*3.0); 1215 ans += cp3 * (p*p*p); 1216 1217 return ans; 1218} 1219 1220float GetNearestPointAlongCubicSpline(vec2 cp0, vec2 cp1, vec2 cp2, vec2 cp3, vec2 uv) 1221{ 1222 float t = GetPositionAlongLineSegmentNearestToPoint(cp0,cp3, uv); 1223 1224 // Now refine once. 1225 { 1226 float t0 = max(0.0, t - 0.1); 1227 float t1 = min(1.0, t + 0.1); 1228 1229 vec2 p0 = GetPointOnCubicSpline(cp0,cp1,cp2,cp3, t0); 1230 vec2 p1 = GetPointOnCubicSpline(cp0,cp1,cp2,cp3, t1); 1231 1232 t = clamp(mix(t0,t1,GetPositionAlongLineNearestToPoint(p0,p1, uv)), 0.0, 1.0); 1233 } 1234 1235 return t; 1236} 1237 1238// ============================================================================================ 1239 1240float GetColourForLineSegment(vec3 cp0, vec3 cp1, vec2 uv, float pixel_uv_size) 1241{ 1242 float t = GetPositionAlongLineSegmentNearestToPoint(cp0.xy,cp1.xy, uv); 1243 1244 vec2 best_point = mix(cp0.xy,cp1.xy,t); 1245 float best_thickness = mix(cp0.z, cp1.z, t*t); // Non-linear for thickness... 1246 float best_dist = length(uv - best_point.xy); 1247 float best_surface = best_thickness - best_dist; 1248 1249 float aa = smoothstep(0.0, pixel_uv_size, best_surface); 1250 1251 return aa; 1252} 1253 1254float GetColourForCubicSpline(vec3 cp0, vec3 cp1, vec3 cp2, vec3 cp3, vec2 uv, float pixel_uv_size) 1255{ 1256 float t = GetNearestPointAlongCubicSpline(cp0.xy,cp1.xy,cp2.xy,cp3.xy, uv); 1257 vec3 best_point = GetPointOnCubicSpline(cp0,cp1,cp2,cp3, t); 1258 float best_dist = length(uv - best_point.xy); 1259 float best_surface = best_point.z - best_dist; 1260 1261 float aa = smoothstep(0.0, pixel_uv_size, best_surface); 1262 1263 return aa; 1264} 1265 1266float GetColourForEllispse(vec2 origin, vec2 scale, float thickness, vec2 uv, float pixel_uv_size) 1267{ 1268 vec2 r = (uv - origin) / scale; 1269 vec2 n = normalize(r) * scale + origin; 1270 1271 // Stylish! 1272 thickness *= 1.0+r.x*r.y*0.5; 1273 1274 float d = distance(n, uv); 1275 float s = thickness - d; 1276 1277 float aa = smoothstep(0.0, pixel_uv_size, s); 1278 1279 return aa; 1280} 1281 1282// ============================================================================================ 1283// THE FONT 1284 1285// Control over the aspect ratio. 1286#define CHAR_ASPECT 0.75 1287#define CHAR_HEIGHT 0.95 1288#define AR(x) (((x)-0.5)*CHAR_ASPECT+0.5) 1289#define H(y) ((y)*CHAR_HEIGHT) 1290 1291bool IsDigitUV(vec2 uv) 1292{ 1293 return uv.x > AR(0.0) && uv.x < AR(1.0) && uv.y > 0.01 * CHAR_HEIGHT && uv.y < 0.99 * CHAR_HEIGHT; 1294} 1295 1296float Char0(vec2 uv, float pixel_uv_size) 1297{ 1298 return GetColourForEllispse(vec2(0.5,0.5*CHAR_HEIGHT),vec2(0.35 * CHAR_ASPECT,0.35),0.09,uv,pixel_uv_size); 1299} 1300 1301float Char1(vec2 uv, float pixel_uv_size) 1302{ 1303 float s0 = GetColourForLineSegment(vec3(0.52,0.9*CHAR_HEIGHT,0.09),vec3(0.50,0.15*CHAR_HEIGHT,0.13), uv, pixel_uv_size); 1304 float s1 = GetColourForLineSegment(vec3(0.52,0.9*CHAR_HEIGHT,0.09),vec3(0.30,0.75*CHAR_HEIGHT,0.06), uv, pixel_uv_size); 1305 1306 return max(s0,s1); 1307} 1308 1309float Char2(vec2 uv, float pixel_uv_size) 1310{ 1311 float s0 = GetColourForCubicSpline(vec3(AR(0.4),H(0.80),0.16), vec3(AR(0.8),H(0.6),0.06), vec3(AR(0.6),H(0.4),0.03), vec3(AR(0.2),H(0.15),0.13), uv, pixel_uv_size); 1312 float s1 = GetColourForCubicSpline(vec3(AR(0.2),H(0.15),0.13), vec3(AR(0.4),H(0.3),0.08), vec3(AR(0.6),H(0.2),0.08), vec3(AR(0.8),H(0.20),0.16), uv, pixel_uv_size); 1313 1314 return max(s0,s1); 1315} 1316 1317float Char3(vec2 uv, float pixel_uv_size) 1318{ 1319 float s0 = GetColourForCubicSpline(vec3(AR(0.5),H(0.6),0.09), vec3(AR(0.7),H(0.7),0.08), vec3(AR(0.6),H(0.9),0.07), vec3(AR(0.4),H(0.87),0.11), uv, pixel_uv_size); 1320 float s1 = GetColourForCubicSpline(vec3(AR(0.5),H(0.6),0.09), vec3(AR(1.0),H(0.4),0.05), vec3(AR(0.5),H(0.1),0.06), vec3(AR(0.2),H(0.20),0.16), uv, pixel_uv_size); 1321 1322 return max(s0,s1); 1323} 1324 1325float Char4(vec2 uv, float pixel_uv_size) 1326{ 1327 float s0 = GetColourForCubicSpline(vec3(AR(0.15),H(0.4),0.11), vec3(AR(0.3),H(0.5),0.06), vec3(AR(0.3),H(0.7),0.06), vec3(AR(0.2),H(0.80),0.11), uv, pixel_uv_size); 1328 float s1 = GetColourForCubicSpline(vec3(AR(0.18),H(0.4),0.10), vec3(AR(0.4),H(0.5),0.06), vec3(AR(0.6),H(0.3),0.06), vec3(AR(0.8),H(0.40),0.13), uv, pixel_uv_size); 1329 float s2 = GetColourForCubicSpline(vec3(AR(0.60),H(0.6),0.09), vec3(AR(0.5),H(0.4),0.06), vec3(AR(0.6),H(0.3),0.06), vec3(AR(0.5),H(0.15),0.09), uv, pixel_uv_size); 1330 1331 return max(s0,max(s1,s2)); 1332} 1333 1334float Char5(vec2 uv, float pixel_uv_size) 1335{ 1336 float s0 = GetColourForCubicSpline(vec3(AR(0.3),H(0.80),0.10), vec3(AR(0.5),H(0.70),0.07), vec3(AR(0.6),H(0.80),0.07), vec3(AR(0.8),H(0.80),0.11), uv, pixel_uv_size); 1337 float s2 = GetColourForCubicSpline(vec3(AR(0.3),H(0.55),0.09), vec3(AR(0.8),H(0.55),0.03), vec3(AR(0.9),H(0.25),0.09), vec3(AR(0.5),H(0.15),0.11), uv, pixel_uv_size); 1338 1339 float s1 = GetColourForLineSegment(vec3(AR(0.3),H(0.55),0.09), vec3(AR(0.3),H(0.8),0.10), uv, pixel_uv_size); 1340 1341 return max(s0,max(s1,s2)); 1342} 1343 1344float Char6(vec2 uv, float pixel_uv_size) 1345{ 1346 float s0 = GetColourForEllispse(vec2(0.5,0.35*CHAR_HEIGHT),vec2(0.3*CHAR_ASPECT,0.25*CHAR_HEIGHT),0.08,uv,pixel_uv_size); 1347 float s1 = GetColourForCubicSpline(vec3(AR(0.2),H(0.23),0.08), vec3(AR(0.1),H(0.6),0.09), vec3(AR(0.2),H(0.9),0.02), vec3(AR(0.6),H(0.85),0.13), uv, pixel_uv_size); 1348 1349 return max(s0,s1); 1350} 1351 1352float Char7(vec2 uv, float pixel_uv_size) 1353{ 1354 float s0 = GetColourForCubicSpline(vec3(AR(0.2),H(0.8),0.11), vec3(AR(0.4),H(0.7),0.06), vec3(AR(0.6),H(0.8),0.06), vec3(AR(0.8),H(0.8),0.13), uv, pixel_uv_size); 1355 float s1 = GetColourForCubicSpline(vec3(AR(0.8),H(0.8),0.13), vec3(AR(0.5),H(0.6),0.02), vec3(AR(0.4),H(0.4),0.06), vec3(AR(0.5),H(0.2),0.16), uv, pixel_uv_size); 1356 1357 return max(s0,s1); 1358} 1359 1360float Char8(vec2 uv, float pixel_uv_size) 1361{ 1362 float s0 = GetColourForEllispse(vec2(0.5,0.35*CHAR_HEIGHT),vec2(0.3 * CHAR_ASPECT,0.25*CHAR_HEIGHT),0.08,uv,pixel_uv_size); 1363 float s1 = GetColourForEllispse(vec2(0.5,0.76*CHAR_HEIGHT),vec2(0.2 * CHAR_ASPECT,0.14*CHAR_HEIGHT),0.07,uv,pixel_uv_size); 1364 1365 return max(s0,s1); 1366} 1367 1368float Char9(vec2 uv, float pixel_uv_size) 1369{ 1370 return Char6(vec2(1.0-uv.x, CHAR_HEIGHT-uv.y),pixel_uv_size); 1371} 1372 1373#undef AR 1374#undef H 1375 1376 1377float CharDot(vec2 uv, float pixel_uv_size) 1378{ 1379 float thickness = 0.1; 1380 1381 float d = distance(uv, vec2(0.8,0.2)); 1382 float s = thickness - d; 1383 1384 float aa = smoothstep(0.0, pixel_uv_size, s); 1385 1386 return aa; 1387} 1388 1389float CharPi(vec2 uv, float pixel_uv_size) 1390{ 1391 float s0 = GetColourForCubicSpline(vec3(0.20,0.85,0.13), vec3(0.4,0.8,0.10), vec3(0.70,0.8,0.075), vec3(0.8,0.9,0.09), uv, pixel_uv_size); 1392 float s1 = GetColourForCubicSpline(vec3(0.35,0.80,0.10), vec3(0.4,0.6,0.08), vec3(0.40,0.3,0.010), vec3(0.2,0.2,0.08), uv, pixel_uv_size); 1393 float s2 = GetColourForCubicSpline(vec3(0.70,0.80,0.08), vec3(0.7,0.7,0.08), vec3(0.73,0.4,0.050), vec3(0.9,0.2,0.04), uv, pixel_uv_size); 1394 1395 return (min(s0+s1+s2,1.0) + max(s0,max(s1,s2))) * 0.5; 1396} 1397 1398float CharDigit(int digit, vec2 uv, float pixel_uv_size) 1399{ 1400 if (digit < 5) 1401 { 1402 if (digit == 0) {return Char0(uv,pixel_uv_size);} 1403 if (digit == 1) {return Char1(uv,pixel_uv_size);} 1404 if (digit == 2) {return Char2(uv,pixel_uv_size);} 1405 if (digit == 3) {return Char3(uv,pixel_uv_size);} 1406 1407 return Char4(uv,pixel_uv_size); 1408 } 1409 else 1410 { 1411 if (digit == 5) {return Char5(uv,pixel_uv_size);} 1412 if (digit == 6) {return Char6(uv,pixel_uv_size);} 1413 if (digit == 7) {return Char7(uv,pixel_uv_size);} 1414 if (digit == 8) {return Char8(uv,pixel_uv_size);} 1415 1416 return Char9(uv,pixel_uv_size); 1417 } 1418} 1419 1420// ============================================================================================ 1421// GRAPHICS PRIMS 1422 1423vec2 ApplyWarp(vec2 uv, float seed) 1424{ 1425 uv += cos(4.0 * uv.x - seed*67.0) * vec2(0.007, 0.009); 1426 uv += sin(5.0 * uv.y - seed*89.0) * vec2(0.009,-0.008); 1427 1428 return uv; 1429} 1430 1431vec2 ApplyBulge(vec2 uv) 1432{ 1433 //return uv - normalize(uv - vec2(0,5)) * smoothstep(3.0, 12.0, distance(uv, vec2(0,7))) + sin( cos(uv.x * uv.x * 0.05) + iGlobalTime) * 0.04; 1434 return uv + normalize(uv - vec2(0,20.0)) * 2.0 + sin( cos(uv.x * uv.x * 0.06) + iGlobalTime) * 0.05; 1435} 1436 1437// box_dx/dy normalised please. Returns colour/alpha. 1438vec2 Box(vec2 box_origin, vec2 box_dx, vec2 box_dy, vec2 box_dims, float thickness, float roundness, vec2 uv, float pixel_uv_size) 1439{ 1440 vec2 r = uv - box_origin; 1441 1442 { 1443 float x = dot(r, box_dx); 1444 float y = dot(r, box_dy); 1445 1446 r = ApplyWarp(vec2(x,y), 1.2); 1447 } 1448 1449 vec2 r_in_box = clamp(r, vec2(0.0,0.0), box_dims); 1450 1451 float dist_from_box = distance(r, r_in_box); 1452 float surface = dist_from_box - thickness; 1453 1454 float colour = smoothstep(pixel_uv_size, 0.0, surface); 1455 float alpha = colour; 1456 1457 if (r == r_in_box) 1458 { 1459 vec2 box_half_size = box_dims * 0.5; 1460 vec2 from_mid = r - box_half_size; 1461 vec2 to_corner = sign(from_mid); 1462 vec2 nearest_corner = box_half_size + to_corner * box_half_size; 1463 vec2 corner_sphere = nearest_corner - to_corner * roundness; 1464 vec2 to_edge = abs(box_half_size) - abs(from_mid); 1465 1466 float dist_from_edge = min(to_edge.x, to_edge.y); 1467 1468 colour = smoothstep(pixel_uv_size, 0.0, dist_from_edge); 1469 1470 if (sign(r - corner_sphere) == to_corner) 1471 { 1472 float dist_from_corner = roundness - distance(corner_sphere, r); 1473 1474 colour = max(colour, smoothstep(pixel_uv_size, 0.0, dist_from_corner)); 1475 } 1476 } 1477 1478 return vec2(colour, alpha); 1479} 1480 1481vec2 Circle(vec2 circle_origin, float circle_radius, float thickness, vec2 uv, float pixel_uv_size) 1482{ 1483 float d = distance(uv, circle_origin); 1484 1485 float surface = abs(d - circle_radius) - thickness * 0.5; 1486 float colour = smoothstep(pixel_uv_size, 0.0, surface); 1487 float alpha = (d < circle_radius) ? 1.0 : colour; 1488 1489 return vec2(colour, alpha); 1490} 1491 1492// Circle with a sprial in the middle! 1493vec2 Wheel(vec2 wheel_origin, float wheel_radius, float wheel_angle, float thickness, vec2 uv, float pixel_uv_size) 1494{ 1495 vec2 r = uv - wheel_origin; 1496 1497 // We need wheel space, w. 1498 float sa = sin(wheel_angle); 1499 float ca = cos(wheel_angle); 1500 1501 vec2 w = vec2( 1502 dot(r, vec2( sa,ca)), 1503 dot(r, vec2(-ca,sa))); 1504 1505 // Wonky please! 1506 w = ApplyWarp(w, 0.11); 1507 1508 float d = length(w); 1509 1510 float surface = abs(d - wheel_radius) - thickness * 0.5; 1511 float colour = smoothstep(pixel_uv_size, 0.0, surface); 1512 float alpha = (d < wheel_radius) ? 1.0 : colour; 1513 1514 if (d < wheel_radius) 1515 { 1516 if (d < thickness * 2.0) 1517 { 1518 colour = smoothstep(pixel_uv_size, 0.0, d - thickness * 1.0); 1519 } 1520 1521 // So the point on the spiral, s. 1522 float a = atan(w.y,w.x); 1523 float d = a + 3.1; 1524 d *= d; 1525 d *= 0.027; 1526 d *= wheel_radius; 1527 1528 vec2 s = normalize(w) * d; 1529 1530 float spiral_surface = distance(s, w) - thickness * 0.5; 1531 float spiral_colour = smoothstep(pixel_uv_size, 0.0, spiral_surface); 1532 1533 colour = max(colour, spiral_colour); 1534 } 1535 1536 return vec2(colour, alpha); 1537} 1538 1539// chimney_dims = width bot, height, width top. 1540vec2 Chimney(vec2 chimney_origin, vec2 chimney_dx, vec2 chimney_dy, vec3 chimney_dims, float anim, float thickness, vec2 uv, float pixel_uv_size) 1541{ 1542 vec2 r = vec2( 1543 dot(uv - chimney_origin, chimney_dx), 1544 dot(uv - chimney_origin, chimney_dy)); 1545 1546 if (r.y < 0.0 || r.y > chimney_dims.y + thickness) 1547 { 1548 return vec2(0.0, 0.0); 1549 } 1550 1551 r.x = abs(r.x); 1552 1553 if (r.x > chimney_dims.x + thickness + chimney_dims.y * 0.4) 1554 { 1555 return vec2(0.0, 0.0); 1556 } 1557 1558 float b = 1.0 - anim; 1559 float q = 1.0-b*b*b*b; 1560 float a = 2.0*(1.0-q)*q; 1561 1562 float down = 1.0 - a * 0.5; 1563 float push = a; 1564 1565 chimney_dims.z *= 1.0 + a * 0.2; 1566 1567 vec2 cp0 = vec2(chimney_dims.x, 0.0); 1568 vec2 cp1 = vec2(chimney_dims.x - chimney_dims.y * 0.33 * push, chimney_dims.y * 0.33); 1569 vec2 cp2 = vec2(chimney_dims.z, chimney_dims.y * 0.66 * down); 1570 vec2 cp3 = vec2(chimney_dims.z, chimney_dims.y * down); 1571 1572 float t = GetNearestPointAlongCubicSpline(cp0,cp1,cp2,cp3, r); 1573 1574 vec2 pt = GetPointOnCubicSpline(cp0,cp1,cp2,cp3, t); 1575 1576 float dist_from_edge = distance(pt, r); 1577 float surface = dist_from_edge - thickness * 0.5; 1578 float colour = smoothstep(pixel_uv_size, 0.0, surface); 1579 float alpha = (r.x < pt.x) ? 1.0 : colour; 1580 1581 // Top? 1582 float bend = 1.0 - a * 0.3; 1583 1584 //if (alpha > 0.0 && r.y > chimney_dims.y * bend - thickness) 1585 { 1586 cp0 = vec2(chimney_dims.z * -1.0, chimney_dims.y * down); 1587 cp1 = vec2(chimney_dims.z * -0.3, chimney_dims.y * down * bend); 1588 cp2 = vec2(chimney_dims.z * +0.3, chimney_dims.y * down * bend); 1589 cp3 = vec2(chimney_dims.z * +1.0, chimney_dims.y * down); 1590 1591 t = GetNearestPointAlongCubicSpline(cp0,cp1,cp2,cp3, r); 1592 1593 pt = GetPointOnCubicSpline(cp0,cp1,cp2,cp3, t); 1594 1595 dist_from_edge = distance(pt, r); 1596 surface = dist_from_edge - thickness * 0.5; 1597 colour = max(colour, smoothstep(pixel_uv_size, 0.0, surface)); 1598 alpha = (r.y > pt.y) ? colour : alpha; 1599 } 1600 1601 return vec2(colour, alpha); 1602} 1603 1604float Puff(float puff, float seed, vec2 uv, float pixel_uv_size) 1605{ 1606 if (!Is0To1(uv)) 1607 { 1608 return 0.0; 1609 } 1610 1611 const float GRID_SIZE = 4.0; 1612 1613 vec2 grid = uv * vec2(GRID_SIZE, GRID_SIZE); 1614 vec2 gsquare = floor(grid); 1615 1616 seed *= gsquare.x + 7.8; 1617 seed *= gsquare.y + 9.8; 1618 seed = sin(seed); 1619 1620 float random_colour = mix( 1.0, 2.0, fract(seed * 15.67)); 1621 float random_time = mix(-0.3, 0.3, fract(seed * 27.89)); 1622 vec2 random_middle = vec2( 1623 mix(0.35, 0.65, fract(seed * 37.22)), 1624 mix(0.35, 0.65, fract(seed * 47.89))); 1625 1626 float dist_from_middle_of_puff = distance(gsquare + vec2(0.5,0.5), vec2(GRID_SIZE,GRID_SIZE) * 0.5); 1627 float dist_from_middle_of_square = distance(grid, gsquare + random_middle); 1628 1629 float shape = max(0.0, 1.25 - dist_from_middle_of_puff / (GRID_SIZE*0.5-0.5)); 1630 float start_time = mix(0.5, 0.0, dist_from_middle_of_puff / (GRID_SIZE * 0.5)); 1631 float s = clamp(puff + start_time + random_time, 0.0, 1.0); 1632 float fade = random_colour * shape * smoothstep(1.0,0.9,s); 1633 float size = (1.0-s)*s * 1.2; 1634 1635 float colour = smoothstep(pixel_uv_size * GRID_SIZE, 0.0, abs(dist_from_middle_of_square - size)-0.01); 1636 1637 return colour * fade; 1638} 1639 1640float PrintNumber(float n, vec2 digit_uv, vec2 uv, float pixel_uv_size) 1641{ 1642 vec2 rel_uv = uv - digit_uv; 1643 1644 if (rel_uv.y < 0.0 || rel_uv.y > 1.0) 1645 { 1646 return 0.0; 1647 } 1648 1649 float digit_place = floor(rel_uv.x - 3.0); 1650 1651 if (digit_place >= 1.0 || digit_place <= -3.0) 1652 { 1653 return 0.0; 1654 } 1655 1656 int digit = int(10.0 * fract(n * pow(10.0, digit_place))); 1657 1658 return CharDigit(digit, fract(rel_uv), pixel_uv_size); 1659} 1660 1661 1662// ============================================================================================ 1663 1664#define SPEED_SCALE 3.0 1665 1666float GetTrainXAtTime(float time) 1667{ 1668 if (time < 2.0) 1669 { 1670 return SPEED_SCALE * (time*time / 4.0 - 3.5); 1671 } 1672 1673 return SPEED_SCALE * (time - 4.5); 1674} 1675 1676vec2 GetCameraPosAtTime(float time) 1677{ 1678 if (time < 12.0) 1679 { 1680 return vec2(time*time / 24.0, 0.0) * SPEED_SCALE; 1681 } 1682 1683 float t = time - 12.0; 1684 1685 if (t > 60.0) 1686 { 1687 return vec2(61.0, 0.0) * SPEED_SCALE; 1688 } 1689 1690 if (t > 50.0) 1691 { 1692 float x = (t - 50.0) / 20.0; 1693 1694 return vec2(56.0 + 20.0*x - 20.0*x*x, 0.0) * SPEED_SCALE; 1695 } 1696 1697 return vec2(6.0+t, 0.0) * SPEED_SCALE; 1698} 1699 1700float GetTrackHeight(float world_x) 1701{ 1702 return 2.5 + sin(world_x * 0.29) + sin(world_x * 0.47) * 0.43; 1703} 1704 1705void GetTrainWheelPositions(float time, out vec2 out_wheel_0, out vec2 out_wheel_1) 1706{ 1707 const float TRAIN_WHEELBASE = 3.0; 1708 1709 float train_x0 = GetTrainXAtTime(time); 1710 float train_x1 = train_x0 + TRAIN_WHEELBASE; 1711 1712 vec2 train_wheel_0 = vec2(train_x0, GetTrackHeight(train_x0)); 1713 vec2 train_wheel_1 = vec2(train_x1, GetTrackHeight(train_x1)); 1714 1715 train_wheel_1 = train_wheel_0 + normalize(train_wheel_1 - train_wheel_0) * TRAIN_WHEELBASE; 1716 train_wheel_1.y = GetTrackHeight(train_wheel_1.x); 1717 train_wheel_1 = train_wheel_0 + normalize(train_wheel_1 - train_wheel_0) * TRAIN_WHEELBASE; 1718 train_wheel_1.y = GetTrackHeight(train_wheel_1.x); 1719 1720 out_wheel_0 = train_wheel_0; 1721 out_wheel_1 = train_wheel_1; 1722} 1723 1724void GetTrainChimneyPosition(float time, out vec2 out_chimney_top, out vec2 out_chimney_dir) 1725{ 1726 vec2 wheel_0; 1727 vec2 wheel_1; 1728 1729 GetTrainWheelPositions(time, wheel_0, wheel_1); 1730 1731 vec2 dx = normalize(wheel_1 - wheel_0); 1732 vec2 dy = vec2(-dx.y,dx.x); 1733 1734 out_chimney_top = wheel_0 + dx * 2.25 + dy * 3.8; 1735 out_chimney_dir = dy; 1736} 1737 1738// ============================================================================================ 1739 1740#define SCREEN_HEIGHT 15.0 1741 1742void mainImage( out vec4 fragColor, in vec2 fragCoord ) 1743{ 1744 // Our sequence takes one minute. 1745 float time = min(max(0.0, iGlobalTime - 2.0) * (1.0 / 60.0),1.0) * 80.0; 1746 1747 // Our screen_uv space has (0,0) at the middle, bottom of the screen and is SCREEN_HEIGHT high and +/- 12*aspect/2 wide. 1748 vec2 uv = fragCoord.xy / iResolution.xy; 1749 float aspect = float(iResolution.x) / float(iResolution.y); 1750 1751 uv.x -= 0.5; 1752 uv.x *= aspect; 1753 uv *= SCREEN_HEIGHT; 1754 1755 // Size of a pixel in uv space for antialiasing. Enlarge to get extra creamy AA! 1756 float pixel_uv_size = 2.0 * SCREEN_HEIGHT / iResolution.y; 1757 1758 // Accumulate final colour into here. 1759 vec2 final_colour = vec2(0.0, 0.0); 1760 1761 // Do this now so we can blur the screen edges. 1762 float vignette; 1763 { 1764 float fade_out = smoothstep(65.0, 79.0, time); 1765 1766 uv *= 1.0 - fade_out * 0.15; 1767 uv.y += fade_out * 4.0; 1768 1769 vec2 vignette_uv = abs(vec2(2.04,2.0) * (fragCoord.xy / iResolution.xy) - vec2(1.02,1.0)); 1770 vignette_uv += fade_out * 0.5; 1771 vignette_uv *= vignette_uv; 1772 vignette_uv *= vignette_uv; 1773 vignette = mix(0.2 * (1.0 - fade_out), 1.0 - fade_out * fade_out * fade_out, 1.0 - vignette_uv.x - vignette_uv.y); 1774 1775 pixel_uv_size /= vignette + 0.1; 1776 } 1777 1778 // World space is in the same scale as uv space. 1779 vec2 camera_pos = GetCameraPosAtTime(time); 1780 1781 // The track first. 1782 vec2 world_pos = camera_pos + uv; 1783 1784 { 1785 float track_y = GetTrackHeight(world_pos.x); 1786 float dist_to_track = abs(track_y - world_pos.y - 0.1); 1787 float surface = dist_to_track - 0.05; 1788 float colour = smoothstep(pixel_uv_size, 0.0, surface); 1789 1790 final_colour.xy += colour; 1791 } 1792 1793 #if USE_INTEGERS 1794 const float DIGITS_PER_LINE = 15.0; 1795 const float LINES_OF_PI = (float(MAX_DIGIT) + DIGITS_PER_LINE - 1.0) / DIGITS_PER_LINE; 1796 #else 1797 const float DIGITS_PER_LINE = 10.0; 1798 const float LINES_OF_PI = ceil((MAX_DIGIT + DIGITS_PER_LINE - 1.0) / DIGITS_PER_LINE); 1799 #endif 1800 1801 const int NUM_DIGITS_IN_FLIGHT = 4; 1802 const float TIME_FOR_FLIGHT = 4.0; 1803 const float TIME_PER_PUFF = TIME_FOR_FLIGHT / float(NUM_DIGITS_IN_FLIGHT); 1804 const vec2 DEST_DIGIT_UV = vec2(DIGITS_PER_LINE * -0.5, SCREEN_HEIGHT * 0.65); 1805 const float KERNING = 0.9; 1806 const float TIME_TO_START_PUFFING = 3.0; 1807 1808 1809 float train_x = GetTrainXAtTime(time); 1810 1811 if (world_pos.x > train_x - 3.0 && world_pos.x < train_x + 5.0 && world_pos.y > SCREEN_HEIGHT * 0.05 && world_pos.y < SCREEN_HEIGHT * 0.6) 1812 { 1813 // Where is the train now? 1814 vec2 train_wheel_0; 1815 vec2 train_wheel_1; 1816 1817 GetTrainWheelPositions(time, train_wheel_0, train_wheel_1); 1818 1819 vec2 train_dx = normalize(train_wheel_1 - train_wheel_0); 1820 vec2 train_dy = vec2(-train_dx.y,train_dx.x); 1821 1822 // Big wheel. 1823 { 1824 float wheel_angle = time * 3.0; 1825 float wheel_wonky = sin(wheel_angle) * 0.05; 1826 1827 train_wheel_0 += train_dy * wheel_wonky; 1828 1829 vec2 wheel_uv = train_wheel_0 + train_dy * 1.0; 1830 float wheel_radius = 1.0; 1831 1832 vec2 wheel_colour = Wheel(wheel_uv, wheel_radius, wheel_angle, 0.1, world_pos, pixel_uv_size); 1833 1834 final_colour += wheel_colour * (1.0 - final_colour.y); 1835 } 1836 1837 // Little wheel. 1838 { 1839 float wheel_angle = time * 5.1; 1840 float wheel_wonky = sin(wheel_angle) * 0.04; 1841 1842 train_wheel_1 += train_dy * wheel_wonky; 1843 1844 vec2 wheel_uv = train_wheel_1 + train_dy * 0.6; 1845 float wheel_radius = 0.6; 1846 1847 vec2 wheel_colour = Wheel(wheel_uv, wheel_radius, wheel_angle, 0.1, world_pos, pixel_uv_size); 1848 1849 final_colour += wheel_colour * (1.0 - final_colour.y); 1850 } 1851 1852 // Recalculate train basis due to wonky wheels! 1853 train_dx = normalize(train_wheel_1 - train_wheel_0); 1854 train_dy = vec2(-train_dx.y,train_dx.x); 1855 1856 // ------------------------------------------------------------------------ 1857 // Train 1858 1859 { 1860 vec2 box_uv = train_wheel_0 - train_dx * 0.2 + train_dy * 0.6; 1861 vec2 box_dims = vec2(3.0, 2.0); 1862 vec2 box_colour = Box(box_uv, train_dx, train_dy, box_dims, 0.1, 0.1, world_pos, pixel_uv_size); 1863 1864 final_colour += box_colour * (1.0 - final_colour.y); 1865 1866 { 1867 vec2 cab_uv = box_uv + train_dx * 0.2 + train_dy * 2.1; 1868 vec2 cab_dx = train_dx; 1869 vec2 cab_dy = train_dy; 1870 vec2 cab_dims = vec2(1.0, 1.6); 1871 1872 vec2 cab_colour = Box(cab_uv, cab_dx, cab_dy, cab_dims, 0.1, 0.1, world_pos, pixel_uv_size); 1873 1874 if (dot(uv - cab_uv, cab_dx) < 1.0) 1875 { 1876 vec2 window_uv = cab_uv + cab_dx * 0.9 + cab_dy * 0.9; 1877 vec2 window_colour = Circle(window_uv, 0.4, 0.1, world_pos, pixel_uv_size); 1878 1879 cab_colour.x += window_colour.x * cab_colour.y; 1880 } 1881 1882 final_colour += cab_colour * (1.0 - final_colour.y); 1883 } 1884 1885 { 1886 vec2 chimney_uv = box_uv + train_dx * box_dims.x * 0.8 + train_dy * box_dims.y; 1887 vec2 chimney_dx = train_dx; 1888 vec2 chimney_dy = train_dy; 1889 vec3 chimney_dims = vec3(0.3, 1.1, 0.4); 1890 float chimney_anim = fract(time * TIME_PER_PUFF); 1891 1892 if (time < TIME_TO_START_PUFFING || time + TIME_PER_PUFF >= TIME_TO_START_PUFFING + float(MAX_DIGIT) * TIME_PER_PUFF) 1893 { 1894 chimney_anim = 0.0; 1895 } 1896 1897 vec2 chimney_colour = Chimney(chimney_uv, chimney_dx, chimney_dy, chimney_dims, chimney_anim, 0.1, world_pos, pixel_uv_size); 1898 1899 final_colour += chimney_colour * (1.0 - final_colour.y); 1900 } 1901 1902 { 1903 vec2 circle_uv = box_uv + train_dx * 3.0 + train_dy * 1.1; 1904 vec2 circle_colour = Circle(circle_uv, 0.6, 0.1, world_pos, pixel_uv_size); 1905 1906 final_colour += circle_colour * (1.0 - final_colour.y); 1907 } 1908 } 1909 } 1910 1911 // ------------------------------------------------------------------------ 1912 // The digits we've already found. 1913 1914 if (time < 79.0) 1915 { 1916 1917 vec2 found_uv = (ApplyBulge(uv) - DEST_DIGIT_UV) / vec2(KERNING,1.0); 1918 vec2 found_base = floor(found_uv); 1919 1920 // Which digit of pi? 1921 float line = -floor(found_base.y); 1922 float column = floor(found_base.x); 1923 1924 if (line >= 0.0 && column >= 0.0) 1925 { 1926 float nth_digit = float(MAX_DIGIT); // MAX_DIGIT -> Invalid. 1927 1928 if (column == 0.0) 1929 { 1930 // First column only has the first digit. 1931 if (line == 0.0) 1932 { 1933 nth_digit = -1.0; 1934 } 1935 } 1936 else 1937 if (column <= DIGITS_PER_LINE) 1938 { 1939 nth_digit = line * DIGITS_PER_LINE + (column - 1.0); 1940 } 1941 1942 if (nth_digit < float(MAX_DIGIT)) 1943 { 1944 // How many digits have arrived already? 1945 float num_arrived = floor(time - TIME_FOR_FLIGHT - TIME_TO_START_PUFFING); 1946 1947 if (nth_digit < num_arrived) 1948 { 1949 vec2 digit_uv = (found_uv - found_base) * vec2(KERNING,1.0) + vec2((1.0 - KERNING)*0.5, 0.0); 1950 1951 if (IsDigitUV(digit_uv)) 1952 { 1953 #if USE_INTEGERS 1954 int pi_digit = GetNthDigitOfPi(int(nth_digit)); 1955 #else 1956 int pi_digit = GetNthDigitOfPi(nth_digit); 1957 #endif 1958 final_colour.x += CharDigit(pi_digit, digit_uv, pixel_uv_size); 1959 } 1960 1961 if (nth_digit == -1.0) 1962 { 1963 final_colour.x += CharDot(digit_uv, pixel_uv_size); 1964 } 1965 } 1966 } 1967 } 1968 } 1969 1970 // ------------------------------------------------------------------------ 1971 // Digit puffs. 1972 1973 if (uv.x > SCREEN_HEIGHT * -0.7 && uv.y > SCREEN_HEIGHT * 0.3 && uv.y < SCREEN_HEIGHT * 0.9) 1974 { 1975 for (int i = 0; i < NUM_DIGITS_IN_FLIGHT; i++) 1976 { 1977 float offset_i = float(i) * (TIME_FOR_FLIGHT / float(NUM_DIGITS_IN_FLIGHT)); 1978 float puff_time = (time - offset_i - TIME_TO_START_PUFFING) / TIME_FOR_FLIGHT; 1979 1980 if (puff_time < 0.0) 1981 { 1982 continue; 1983 } 1984 1985 float puff_digit = floor(puff_time) * float(NUM_DIGITS_IN_FLIGHT) + float(i) - 1.0; 1986 1987 if (puff_digit >= 60.0) // Always puff even if no more digits... .to keep in sync with sound 1988 { 1989 continue; 1990 } 1991 1992 float puff_start_time = floor(puff_time) * TIME_FOR_FLIGHT + offset_i + TIME_TO_START_PUFFING; 1993 float puff_t = pow(fract(puff_time), 0.75); 1994 float puff_size = 1.0 + puff_t * 3.0; 1995 float digit_size = min(0.3 + puff_t, 1.0); 1996 vec2 camera_pos_at_start_time = GetCameraPosAtTime(puff_start_time); 1997 1998 vec2 chimney_pos_at_start_time; 1999 vec2 chimney_dir_at_start_time; 2000 2001 GetTrainChimneyPosition(puff_start_time, chimney_pos_at_start_time, chimney_dir_at_start_time); 2002 2003 vec2 puff_cp0 = chimney_pos_at_start_time; 2004 vec2 puff_cp1 = chimney_pos_at_start_time + chimney_dir_at_start_time * 5.0; 2005 vec2 puff_cp2 = puff_cp1 + vec2(0.0, -1.0); 2006 vec2 puff_cp3 = puff_cp1 + vec2(0.0, -2.0); 2007 2008 vec2 puff_pos = GetPointOnCubicSpline(puff_cp0, puff_cp1, puff_cp2, puff_cp3, puff_t); 2009 vec2 puff_uv = (world_pos - puff_pos)/puff_size+0.5; 2010 2011 { 2012 final_colour.x += Puff(puff_t * 1.5, puff_start_time + 5.6, puff_uv - 0.05, pixel_uv_size / puff_size) * (1.0 - final_colour.y); 2013 final_colour.x += Puff(puff_t * 1.5, puff_start_time + 7.9, puff_uv + 0.05, pixel_uv_size / puff_size) * (1.0 - final_colour.y); 2014 } 2015 2016 if (puff_digit >= float(MAX_DIGIT)) 2017 { 2018 continue; 2019 } 2020 2021 vec2 digit_dest_uv; 2022 2023 if (puff_digit == -1.0) 2024 { 2025 // This is the first digit of pi. 2026 digit_dest_uv = DEST_DIGIT_UV - (1.0 - KERNING) * 0.5; 2027 } 2028 else 2029 { 2030 #if USE_INTEGERS 2031 int line; 2032 int column; 2033 DivMod(int(puff_digit), int(DIGITS_PER_LINE), line, column); 2034 #else 2035 float line; 2036 float column; 2037 DivMod(puff_digit, DIGITS_PER_LINE, line, column); 2038 #endif 2039 2040 digit_dest_uv = DEST_DIGIT_UV + vec2((float(column) + 1.0) * KERNING - (1.0 - KERNING) * 0.5, -float(line)); 2041 } 2042 2043 // The digit of pi are we puffing out of the train. 2044 vec2 digit_cp0 = chimney_pos_at_start_time; 2045 vec2 digit_cp1 = chimney_pos_at_start_time + chimney_dir_at_start_time * 5.0; 2046 vec2 digit_cp3 = digit_dest_uv + camera_pos; 2047 vec2 digit_cp2 = digit_cp3 + vec2(2.0, -8.0); 2048 2049 float bulge_amount = smoothstep(0.0, 0.9, puff_t); 2050 vec2 bulged_uv = mix(uv, ApplyBulge(uv), bulge_amount); 2051 2052 vec2 digit_pos = GetPointOnCubicSpline(digit_cp0, digit_cp1, digit_cp2, digit_cp3, puff_t); 2053 vec2 digit_uv = (bulged_uv + camera_pos - digit_pos)/digit_size; 2054 2055 if (IsDigitUV(digit_uv)) 2056 { 2057 #if USE_INTEGERS 2058 int pi_digit = GetNthDigitOfPi(int(puff_digit)); 2059 #else 2060 int pi_digit = GetNthDigitOfPi(puff_digit); 2061 #endif 2062 2063 final_colour += CharDigit(pi_digit, digit_uv, pixel_uv_size) * (1.0 - final_colour.y); 2064 } 2065 } 2066 } 2067 2068 // Apply vignette 2069 final_colour.x = sqrt(clamp(final_colour.x,0.0,1.0)) * vignette; 2070 2071 if (time >= 75.0) 2072 { 2073 vec2 original_uv = fragCoord.xy / iResolution.xy; 2074 original_uv.x -= 0.5; 2075 original_uv.x *= aspect; 2076 original_uv *= SCREEN_HEIGHT; 2077 float original_pixel_uv_size = 2.0 * SCREEN_HEIGHT / iResolution.y; 2078 2079 vec2 pi_uv = (original_uv - vec2(0.0, SCREEN_HEIGHT * 0.62)) * 0.4 + 0.5; 2080 float pi = CharPi(pi_uv, original_pixel_uv_size) * 4.0 * smoothstep(75.0,80.0,time); 2081 final_colour.x += sqrt(pi); 2082 } 2083 2084 fragColor = vec4(final_colour.xxx,1.0); 2085} 2086 2087 void main(void) 2088{ 2089 //just some shit to wrap shadertoy's stuff 2090 vec2 FragCoord = vTexCoord.xy*OutputSize.xy; 2091 mainImage(FragColor,FragCoord); 2092} 2093#endif 2094