300 lines
No EOL
12 KiB
GLSL
300 lines
No EOL
12 KiB
GLSL
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
|
|
}} |