Shader "CrispyPin/QRCode" { Properties { [HideInInspector] _("uv dummy",2D)=""{} [Header(QR Code parameters)] [Space] [IntRange] _Version("Version (must match data)", Range(1, 40)) = 1 [Enum(Low,1, Medium,0, Quartile,3, High,2)] _ECLevel("EC Level (must match data)", Integer) = 1 [IntRange] _Mask("Mask type", Range(0, 7)) = 1 _FrameSize("Frame size", Range(0, 2)) = 0.1 } SubShader { Tags { "RenderType"="Opaque" } LOD 100 CGPROGRAM #pragma surface s Standard struct Input{float2 uv_;}; uint _Version; uint _Mask; uint _ECLevel; float _FrameSize; #define EC_LEVEL _ECLevel #define MASK_TYPE _Mask #define VERSION _Version #define WIDTH (17 + VERSION * 4) #define ALIGNERS ((VERSION / 7) + 2) #define ALIGNER_SPACING_IDEAL ((WIDTH - 13) / (ALIGNERS-1)) #define ALIGNER_SPACING (ALIGNER_SPACING_IDEAL + ALIGNER_SPACING_IDEAL % 2) #define MISALIGNMENT ((WIDTH - 13) - ALIGNER_SPACING * ALIGNERS) #define ALIGNER_COUNT ((ALIGNERS * ALIGNERS - 3) * (VERSION > 1)) // #define BIT_COUNT (WIDTH * WIDTH - 225 - (WIDTH - 17)*2 - 25 * ALIGNER_COUNT + 10*(ALIGNERS-2)*(ALIGNERS>2) - 36 * (VERSION > 6)) static const uint FORMAT_BIT_SETS[32] = {0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0, 0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976, 0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b, 0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed}; #define FORMAT_BITS_RAW ((EC_LEVEL << 3) | MASK_TYPE) #define FORMAT_BITS FORMAT_BIT_SETS[FORMAT_BITS_RAW] static const uint VERSION_BIT_SETS[34] = {0x07C94,0x085BC,0x09A99,0x0A4D3,0x0BBF6,0x0C762,0x0D847,0x0E60D,0x0F928,0x10B78,0x1145D,0x12A17,0x13532,0x149A6,0x15683,0x168C9,0x177EC,0x18EC4,0x191E1,0x1AFAB,0x1B08E,0x1CC1A,0x1D33F,0x1ED75,0x1F250,0x209D5,0x216F0,0x228BA,0x2379F,0x24B0B,0x2542E,0x26A64,0x27541,0x28C69}; #define VERSION_BITS (VERSION > 6 ? VERSION_BIT_SETS[VERSION - 7] : 0) #define PINK float3(1, .3, .5) #define BLUE float3(0, .4, .7) float3 finder_pattern(uint x, uint y) { if (x < 6 && x > 0 && y < 6 && y > 0) return !(x < 5 && x > 1 && y < 5 && y > 1); return (x > 6 || y > 6); } float3 main (float2 uv){ const uint data[] = {0x40E746D7, 0x77660662, 0x5606D6E6, 0x068236E2, 0x5636C6F7, 0xE6571786, 0xEC184286, 0x92575616, 0x0742F616, 0x42660617, 0x044616C2, 0x52424266, 0x96112404, 0x52063742, 0xE6370762, 0x3606C697, 0x92F652C6, 0x07070704, 0x56E6EC67, 0x92076646, 0x07424646, 0x07863657, 0x4206E606, 0xC2767646, 0x92425211, 0x260776C6, 0x56460656, 0xF24696C6, 0x36071652, 0x26069796, 0xF206C206, 0xECF65656, 0x57428636, 0x560786E6, 0x968276E6, 0x06961246, 0xC6071606, 0x9711D2E6, 0x16360652, 0x57C24652, 0x52E6C296, 0x4246F607, 0x86C236D2, 0x6632EC07, 0x46B682D6, 0x0727E286, 0x04E27206, 0xC60617D6, 0x46570617, 0x06F70611, 0x4657E6C2, 0x52374604, 0x52260507, 0x17C26692, 0x17562226, 0x66162296, 0xEC862757, 0x06E24716, 0x9207C696, 0x46320616, 0x0737D7C2, 0x5652C707, 0xD6115237, 0x37970426, 0x96060757, 0xF7F207E6, 0x96463206, 0x06720726, 0x46D6EC06, 0x46324292, 0x56E71757, 0x37570746, 0xF7C28607, 0xC6160696, 0x5686F711, 0xD6F60606, 0x06E64737, 0x26362296, 0x86420752, 0x9652E6D7, 0xF7165227, 0xECF6F6F6, 0x46377792, 0x06975606, 0xF7F70696, 0x06F70742, 0x92524704, 0x4611D642, 0x62972646, 0x06974742, 0xB6575646, 0xF7375276, 0x0706E292, 0xD616EC56, 0x07063617, 0x82F62692, 0x04962276, 0x56522706, 0x9696B604, 0x0716C211, 0x96273636, 0xE0EC560E, 0x3C55514C, 0x6D15A023, 0x665E1264, 0x74673E1D, 0x5F5717CA, 0x4F40504B, 0x7421F8F3, 0xDA427336, 0x4AEF1540, 0x148EE697, 0xDA5E9475, 0x15C83730, 0x3C4A9094, 0x960142F8, 0xACF41F26, 0xB209D19B, 0xEC61B54D, 0x3215410D, 0xBD5BE302, 0xC15AF45B, 0xC49B130C, 0x8ABE32AC, 0x7B7ECDEE, 0x721EBEAB, 0x74100C8A, 0x7D387A18, 0x29B8F0C0, 0x70C95B49, 0x4FFE7EEA, 0xB6D82FB5, 0x6A2672C1, 0xA66BE33B, 0xEE4BD24C, 0xE066B984, 0x0DAC2A99, 0x9C9C9D92, 0x6C8A8D76, 0x9E42D757, 0x6E6FF527, 0x6312ADF9, 0x1830A821, 0xC1D52FB8, 0x9E7DDC14, 0x3CE8E58E, 0xD29811F0, 0x281E909F, 0xA7B9318F, 0xC67A5C75, 0xBBC270C9, 0x233ED044, 0x694734DE, 0x2C22AD0E, 0x405EE175, 0x54C5079D, 0x1C963D45, 0x3C036CC9, 0x122A41E0, 0x4E5C7F84, 0x7C6188FA, 0x450A9112, 0x5A7E5E71, 0x6707BD9A, 0x0297702F, 0xEE15E70F, 0xABB2D715, 0xAD6374FB, 0x98413E2D, 0x6C433255, 0x7A847F2B, 0x3E8FF100, 0x2A5CD1E7, 0x0367E909, 0xBEEF3E66, 0xF1F09C13, 0xAE0EDEBD, 0x6EE256B8, 0x2DF8908D, 0x0F57E334, 0x71AA45C7, 0x101C0E13, 0x144EE443, 0x7F9428CB, 0x4D8656D0, 0x1CF2E951, 0x9FC23499, 0x115B893B, 0x07384B8A, 0x7276CC05, 0x8E74B153, 0x4B3F5FE2, 0xFCED5974, 0xE994A837, 0x0ABD48CC, 0xAE47BEBE, 0x74D23E9B, 0x2E6B6030, 0x5A5D0669, 0x022F3C29, 0xE922DFFB, 0xBC3F0679, 0xD0AA2DE2, 0xA039F0CE, 0xF2E66AB5, 0xA81C4DCA, 0x381BD8A0, 0x0BD120C7, 0xD2C5FC9B, 0x4D39DDE1, 0x10F85E25, 0xBFCA6793, 0xB045ABA7, 0xD4994DC0, 0x20D7B404, 0x1C01E1A8, 0x88995798, 0x3BEB5A84, 0xB19FEA05, 0xBC1F2778, 0xAADED13F, 0xCE902F25, 0xFDE7BF8D, 0x2A7AABD2, 0x9D74F550, 0xFE143BFC, 0x38F759B2, 0xFEA4ECEE, 0x77B61A4F, 0x311A3C1F, 0x5CA77FD6, 0x3A53365B, 0xD9554AEC, 0xA956A683, 0x4F816FA3, 0x17816E04, 0x17135E8A, 0x30AF4638, 0x428CB7BE, 0x147EB239, 0x9CECC3BA, 0x5EF1BF08, 0x3FF568B3, 0xA87CEE55, 0x1C8C474C, 0x333DA5CC, 0xA1F1CF8C, 0x02122409, 0x74E46205, 0xE497AF30, 0x525685BD, 0x91FB8839, 0xCA2D504F, 0x9D70CC96, 0xDFA94318, 0x8A2B828C, 0x00D4DA56, 0x3CF15E80, 0x91C96A7E, 0xD5764DD6, 0x5E3FDC35, 0x8D63C2AD, 0xA3ECAD9A, 0x6A6940B7, 0xAD387855, 0xEE41DEA1, 0x39AC2F9A, 0xDEA8DCF4, 0x1388AF76, 0xB658B483, 0xA9E56DF6, 0xA64F0568, 0x112116C4, 0xD6A8E45D, 0x9633322A, 0xA90D0053, 0xDACA4265, 0xF6DCAA07, 0x14A7DFA3, 0x285C073F, 0xBF39BA6D, 0x7A3510C8, 0xB726A37D, 0x9F569403, 0x4F1A5D89, 0xF24F98BC, 0x36ED378F, 0x0AF508EA, 0xD38BBAF2}; uv.y = 1 - uv.y; uv = uv * (_FrameSize*2 + 1.) - _FrameSize; // Quiet zone/frame if (uv.x < 0 || uv.x > 1 || uv.y < 0 || uv.y > 1) return 1; uint px = uv.x * WIDTH; uint py = uv.y * WIDTH; // Finder patterns if (px < 8 && py < 8) return finder_pattern(px, py); if (px < 8 && py > WIDTH - 9) return finder_pattern(px, py - WIDTH + 7); if (px > WIDTH - 9 && py < 8) return finder_pattern(px - WIDTH + 7, py); // filler pixel if (px == 8 && py == WIDTH - 8) return 0; // Timing patterns if (px == 6) return py & 1; if (py == 6) return px & 1; // Aligners if (VERSION > 1 && (px > 12 || py > 12) // top left && (px < WIDTH - 13 || py > 12) // top right && (py < WIDTH - 13 || px > 12) // bottom left ) { uint x = px + ALIGNER_SPACING - 4; uint y = py + ALIGNER_SPACING - 4; if (px > 12) x -= MISALIGNMENT; if (py > 12) y -= MISALIGNMENT; x %= ALIGNER_SPACING; y %= ALIGNER_SPACING; if (x < 5 && y < 5) { return (x < 4 && x > 0 && y < 4 && y > 0 && !(x == 2 && y == 2)); } } // Format bits // left if (px < 6 && py == 8) return (FORMAT_BITS & (1 << (14 - px))) == 0; if (px == 7 && py == 8) return (FORMAT_BITS & (1 << 8)) == 0; // right if (px > WIDTH - 9 && py == 8) return (FORMAT_BITS & (1 << (WIDTH - 1 - px))) == 0; // top if (px == 8 && py < 6) return (FORMAT_BITS & (1 << py)) == 0; if (px == 8 && py < 9) return (FORMAT_BITS & (1 << (py-1))) == 0; // bottom if (px == 8 && py > WIDTH - 8) return (FORMAT_BITS & (1 << py + 15 - WIDTH)) == 0; // version information if (VERSION > 6) { if (px < 6 && py > WIDTH - 12) { uint b = px * 3 + py - (WIDTH - 11) ; return ((VERSION_BITS >> b) & 1) == 0; } if (px > WIDTH - 12 && py < 6) { uint b = px - (WIDTH - 11) + py * 3; return ((VERSION_BITS >> b) & 1) == 0; } } // data bit layout uint column = (WIDTH - px - (px < 6)-1)/2; uint direction_up = column % 2 == 0; uint full_columns = column > 4 ? column - 4 : 0; full_columns = min(full_columns, VERSION*2); // the 4 columns under the right finder uint short_columns = min(column, 4); // between the left two finder patterns uint tiny_columns = column > (VERSION*2 + 4) ? column - (VERSION*2 + 4) : 0; uint bit_index = full_columns * (WIDTH - 1) * 2; bit_index += short_columns * (WIDTH - 9) * 2; bit_index += tiny_columns * (WIDTH - 17) * 2; bit_index += (px + (px < 6)) % 2; uint column_progress; uint y_relative; if (direction_up) { y_relative = WIDTH - py - 1; column_progress = WIDTH - py - 1 - (py < 7); } else { y_relative = py; column_progress = py - ((full_columns == 0) ? 9 : (py > 6)); } bit_index += column_progress * 2; if (px < 9) bit_index -= 16; // rightmost tiny column // data bit layout - aligners if (VERSION > 1 && column > 1) { // aligners always cover a half column to the left and two full columns to the right // outer aligners are always 4 from all edges if (VERSION > 6) { // top version info section if (column == 5){ bit_index -= 12; bit_index -= min(py + 1, 6); } else if (column > 5) { bit_index -= 18; } } uint aligner_col = (column - 2) / (ALIGNER_SPACING/2); uint aligner_col_mod = (column - 2) % (ALIGNER_SPACING/2); if (aligner_col > (ALIGNERS - 2)){ aligner_col_mod = (column-2) - (aligner_col-1) * (ALIGNER_SPACING/2); aligner_col -= 1; } if (aligner_col > 0) { // full columns passed bit_index -= (ALIGNERS - 1) * aligner_col*25; if (aligner_col > 1) { // top row only obstructs 20 since it overlaps the timing stripe bit_index -= (aligner_col - 1)*20; } } uint passed_up = min((y_relative - 9 + ALIGNER_SPACING)/ALIGNER_SPACING, ALIGNERS-1); #define ALIGNER_GRID_Y_START (WIDTH - 9 - ALIGNER_SPACING * (ALIGNERS - 1)) uint passed_down = py > ALIGNER_GRID_Y_START ? (py - ALIGNER_GRID_Y_START) / ALIGNER_SPACING : 0; if (px < 9) { if (passed_up>0) passed_up -= 1; if (passed_down>(ALIGNERS-2)) passed_down-=1; if (tiny_columns == 0) { bit_index -= passed_up*10; } else if (tiny_columns == 1) { bit_index -= passed_down*10; bit_index -= (ALIGNERS-2)*10; } else if (tiny_columns == 2) { if (VERSION > 6) { bit_index -= 12; } bit_index -= (ALIGNERS-2) * 20; } else if (tiny_columns == 3) { if (VERSION > 6) { bit_index -= 12; bit_index -= (ALIGNERS-2)*20; } } } if (aligner_col_mod == 0) { if (direction_up) { bit_index -= passed_up * 10; if (py < 8) bit_index -= 8; } else { bit_index -= passed_down * 10; if (py > 8) bit_index -= 8; } } else if (aligner_col_mod == 1) { if (column > 3){ bit_index -= ALIGNERS*10 - 2; } else { bit_index -= ALIGNERS*10; bit_index += 26; // right finder } if (direction_up){ bit_index -= passed_up*10; if (py < 5) bit_index -= 8; } else { bit_index -= passed_down*10; if (py > 8) bit_index -= 8; if (column == 3) bit_index -= 8; } } else if (aligner_col_mod == 2 && px > 5) { bit_index -= (ALIGNERS - 1) * 20; if (aligner_col > 0) bit_index -= 16; // most of the current column top aligner, excluding the left 4 pixels if (direction_up) { bit_index -= passed_up*5; uint progress = (y_relative - 4) % ALIGNER_SPACING; if (progress < 5 && y_relative - 4 < ALIGNER_SPACING*(ALIGNERS-1)) { bit_index -= progress+1; } if (py < 9) { if (py < 4) bit_index -= 4; else bit_index -= (9-py) - (py<6); } } else { bit_index -= passed_down*5; if (py < 6 && py > 3) { bit_index -= py-3; } else if (py < 9 && py > 6) { bit_index -= py-4; } else if (py > 8){ bit_index -= 4; } uint progress = (py - ALIGNER_GRID_Y_START) % ALIGNER_SPACING; if (progress < 5 && py >= WIDTH-((ALIGNERS-1)*ALIGNER_SPACING+4)) { bit_index -= progress; bit_index += 4; } } } else if (aligner_col_mod > 2) { // include the full column of aligners bit_index -= (ALIGNERS - 1)*25; if (aligner_col > 0){ bit_index -= 20; } } } // mask uint mask; switch (MASK_TYPE){ case 0: mask = (px+py) % 2 == 0; break; case 1: mask = py % 2 == 0; break; case 2: mask = px % 3 == 0; break; case 3: mask = (px+py) % 3 == 0; break; case 4: mask = (py/2 + px/3) % 2 == 0; break; case 5: mask = (py*px)%2 + (px*py)%3 == 0; break; case 6: mask = ((py*px)%2 + (px*py)%3) % 2 == 0; break; case 7: mask = ((py+px)%2 + (px*py)%3) % 2 == 0; break; } uint bit = ((data[bit_index/32] >> (31-(bit_index % 32))) & 1); return bit == mask; } void s (Input IN, inout SurfaceOutputStandard o) { o.Albedo = main(IN.uv_); } ENDCG }}