cvr-props/Assets/test/QRCode.shader

220 lines
6.5 KiB
Text
Raw Normal View History

2024-08-16 10:06:39 +02:00
Shader "CrispyPin/QRCode" {
Properties {
[HideInInspector]
2024-08-17 12:24:04 +02:00
_("",2D)=""{}
_Version("Version", Range(1, 40)) = 1
_Mask("Mask type", Range(0, 7)) = 1
[Toggle]
_DisableMask("Hide mask", Integer) = 0
// _TimeSlider("Time", Range(150, 300)) = 0
_TimeSlider("Time", Range(0, 300)) = 0
2024-08-16 10:06:39 +02:00
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 100
CGPROGRAM
#pragma surface s Standard
struct Input{float2 uv_;};
uint _Version;
uint _Mask;
bool _DisableMask;
float _TimeSlider;
2024-08-16 10:06:39 +02:00
#define VERSION _Version
#define WIDTH (17 + VERSION * 4)
2024-08-17 12:24:04 +02:00
#define PIXEL_WIDTH WIDTH
// #define PIXEL_WIDTH 177
2024-08-16 10:06:39 +02:00
#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 BIT_COUNT (WIDTH * WIDTH - 225 - (WIDTH - 17)*2 - 25 * (ALIGNERS * ALIGNERS - 3) * (VERSION > 1))
#define TIME (_Time.y*24)
#define TIME (uint)(_TimeSlider)
2024-08-16 10:06:39 +02:00
2024-08-16 14:46:24 +02:00
#define EC_LEVEL_L 1
#define EC_LEVEL_M 0
#define EC_LEVEL_Q 3
#define EC_LEVEL_H 2
#define EC_LEVEL EC_LEVEL_L
#define MASK_TYPE _Mask
// ECI_MODE 0100 = byte
#define ECI_MODE 4
2024-08-16 14:46:24 +02:00
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]
2024-08-16 10:06:39 +02:00
#define WHITE 1
#define BLACK 0
2024-08-17 12:24:04 +02:00
#define PINK float3(1, .3, .5)
#define BLUE float3(0, .4, .7)
2024-08-16 10:06:39 +02:00
2024-08-17 12:24:04 +02:00
float3 finder_pattern(uint x, uint y) {
2024-08-16 10:06:39 +02:00
if (x < 6 && x > 0 && y < 6 && y > 0) return !(x < 5 && x > 1 && y < 5 && y > 1);
return (x > 6 || y > 6);
}
2024-08-17 12:24:04 +02:00
float3 main (float2 uv){
// "hell", "o wo", "rld!"
const uint data[] = {0x68656c6c,0x6f20776f,0x726c6421,
// 4 bits 0 padding to align to byte (4 bit ECI_MODE is inserted before data and length)
// then ec11ec11... until some version-specific length reached?
// then EC data
0x0ec11ec1, 0x1ec2d631, 0x95423722, 0xe0000000
};
const uint data_len = 3*4;
const uint total_len = 7*4;
2024-08-16 10:06:39 +02:00
uv.y = 1 - uv.y;
uv = uv * 1.5 - 0.25;
// Quiet zone/frame
if (uv.x < 0 || uv.x > 1 || uv.y < 0 || uv.y > 1) return 1.2;
2024-08-16 10:06:39 +02:00
2024-08-17 12:24:04 +02:00
uint px = uv.x * PIXEL_WIDTH;
uint py = uv.y * PIXEL_WIDTH;
2024-08-16 10:06:39 +02:00
// 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;
2024-08-16 10:06:39 +02:00
// 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
2024-08-16 10:06:39 +02:00
) {
2024-08-17 12:24:04 +02:00
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));
2024-08-16 10:06:39 +02:00
}
}
// Format bits
2024-08-16 14:46:24 +02:00
// 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;
2024-08-16 10:06:39 +02:00
// 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;
if (direction_up)
column_progress = WIDTH - py - 1 - (py < 7);
else
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
// rightmost column of aligners
uint passed_up = (column_progress - 9 + ALIGNER_SPACING) / ALIGNER_SPACING;
if (column == 2) {
bit_index -= passed_up * 10;
} else if (column == 3) {
uint passed_down = (column_progress) / ALIGNER_SPACING;
bit_index -= (passed_down + ALIGNERS - 1) * 10;
} else if (column == 4) {
bit_index -= (ALIGNERS - 1) * 20;
uint offset = (column_progress + ALIGNER_SPACING - 3) % ALIGNER_SPACING;
if (offset < 6) {
bit_index -= offset;
}
bit_index -= passed_up * 5;
} else {
bit_index -= (ALIGNERS-1)*25;
}
}
// data
uint bit = 0;
if (bit_index < 4){
bit = (ECI_MODE >> (3-bit_index))&1;
} else if (bit_index < 12) {
bit = (data_len >> (7 - (bit_index - 4))) & 1;
} else if (bit_index < total_len*8 + 12){
uint data_bit_index = bit_index - 12;
bit = ((data[data_bit_index/32] >> (31-(data_bit_index % 32))) & 1);
}
// mask
uint mask;
switch (_Mask){
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;
}
if (!_DisableMask) {
bit ^= mask;
}
// return !bit;
// bit index debugging worm
uint worm = abs(TIME % BIT_COUNT - bit_index);
const uint length = 4;
if (worm < length) return BLUE * (worm/(float)length);
return (float)(bit_index%BIT_COUNT)/(float)BIT_COUNT;
2024-08-16 10:06:39 +02:00
}
void s (Input IN, inout SurfaceOutputStandard o) { o.Albedo = main(IN.uv_); }
2024-08-16 10:06:39 +02:00
ENDCG
}}