1

I'm writing a fragment shader for rendering a 1D texture containing an arbitrary byte array into a kind of barcode. my idea is to encode each byte into a square divided diagonally (so each of the 4 triangles represents 2 bit), like so:

 _____
|\ A /|  each byte encoded as binary is DDCCBBAA,
| \ / |  the colors are: Red   if 11
|D X B|                  Green if 10
| / \ |                  Blue  if 01
|/ C \|                  Black if 00
 ¯¯¯¯¯   so color can be calculated as: [(H & L), (H & !L), (!H & L)]

so for example: 198 == 11 00 01 10 would be:
 _____                 DD CC BB AA
|\ G /| 
| \ / | A=10=Green
|R X B| B=01=Blue
| / \ | C=00=Black
|/ b \| D=11=Red
 ¯¯¯¯¯  (B=Blue, b=Black)

what I got so far are a function for encoding 2 bools (H,L in the example notation) into a vec3 color and a function for encoding a byte and "corner index" (A/B/C/D in the example) into the color:

#version 400
out vec4 gl_FragColor; // the output fragment
in vec2 vf_texcoord; // normalized texture coords, 0/0=top/left
uniform isampler1D uf_texture; // the input data
uniform int uf_texLen; // the input data's byte count
vec3 encodeColor(bool H, bool L){
  return vec3(H&&L,H&&!L,!H&&L);
}
vec3 encodeByte(int data,int corner){
  int maskL = 1 << corner;
  int maskH = maskL << 1;
  int shiftL = corner/2;
  int shiftH = shiftL+1;
  bool H=bool((data&maskH)>>shiftH);
  bool L=bool((data&maskL)>>shiftL);
  return encodeColor(H,L);
}
void main(void) {

  // the part I can't figure out

  gl_FragColor.rgb=encodeByte(/* some stuff calculated by the part above*/);
  gl_FragColor.a=1;
}

the problem is I can't figure out how to calculate which byte to encode and in what "corner" the current fragment is.

nonchip
  • 1,084
  • 1
  • 18
  • 36
  • "Each of the 4 triangles represents 2 *bytes?*" Did you mean bits? – Wyzard Jul 22 '14 at 06:35
  • Assuming you are actually drawing all of the geometry, why not specify two uniform values per triangle with the "byte to encode" and "corner"? – Roger Allen Jul 22 '14 at 06:38
  • If this colored square represents one byte, but your raw data consists of multiple bytes, you'll need to display multiple squares somehow, and it's not clear how you intend to do that. Are you drawing each square as a separate GL primitive (e.g. triangle fan), or are you drawing a single rectangle with GL and relying on the fragment shader to display different colored squares within it? – Wyzard Jul 22 '14 at 06:41
  • @RogerAllen because the two "triangles" i mention aren't geometry `GL_TRIANGLES`, but the whole thing is rendered as one big rectangle (as a VBO of 2 tris). the triangles I mention in the question are just the upper/lower/left/right quarter of each byte representing square. – nonchip Jul 22 '14 at 06:41
  • @Wyzard I render the whole string as one big rectangle, so calculating which byte of the string to handle for each fragment is up to the fragment shader. – nonchip Jul 22 '14 at 06:43
  • so what I'm basically doing here is a color coded 2d barcode :D – nonchip Jul 22 '14 at 06:43
  • Then you need to provide more detail about how you want the shaded squares to be arranged within that single piece of geometry. If I want to display 7 bytes, should the 7 squares be arranged horizontally? Vertically? In a grid? what determines the number of rows/cols in the grid? – Wyzard Jul 22 '14 at 06:43
  • Sure would be way simpler if you used geometry. You'll need to use the frag x,y inputs and something like this http://stackoverflow.com/questions/2049582/how-to-determine-a-point-in-a-triangle – Roger Allen Jul 22 '14 at 06:45
  • @Wyzard: in the first step I think arranging them horizontally would suffice (so I see that it works at least), but in the future I'd like it to be "as square as possible with the lower right squares black", so your 7 bytes would be arranged as: `1st line=(0,1,2), 2nd=(3,4,5),3rd=(6,black,black)` – nonchip Jul 22 '14 at 06:46
  • @RogerAllen yes, it'll be simpler, but I'd like to do as much as possible inside the shader, because I'll be using this in a performance heavy app, trying to eliminate VRAM throughput and draw calls. I use this "unit square VBO" primitive for font rendering too, and I hope being able to use this for almost all GUI elements, so it'd be nice to do the whole "barcode drawing" as a matter of "bind gui square VBO, bind barcode shader, set data texture, DrawElements()" – nonchip Jul 22 '14 at 06:51
  • Consider using a geometry shader to generate the triangles dynamically in the GPU. Each one can just be a solid color (chosen by the geometry shader), so the fragment shader is trivial. – Wyzard Jul 22 '14 at 06:56
  • @Wyzard but then I would just move the problem towards the geometry shader. I think I have all required variables anyway (normalized coords of the current fragment inside the output quad, and size of the sampler1D), the only problem is I don't know how to calculate the required values. – nonchip Jul 22 '14 at 07:00
  • But the work done by the geometry shader is proportional to the number of triangles you're generating, instead of the number of pixels in the final image. Since each triangle probably covers more than one pixel (and certainly shouldn't cover less, or your barcode is unreadable), this may mean less overall work for the GPU. (I'm not sure about relative performance of geometry vs. fragment shaders, though.) – Wyzard Jul 22 '14 at 07:06
  • @Wyzard I agree that could be a nice optimization, but I'm afraid I also don't know the formulas for that situation. but of course if you could point me to the correct calculations for that, I'll be happy to use them :D – nonchip Jul 22 '14 at 07:11

1 Answers1

0

(Note, the code here is is off the top of my head and untested, and I haven't written a lot of GLSL. The variable names are sloppy and I've probably made some stupid syntax mistakes, but it should be enough to convey the idea.)

The first thing you need to do is translate the texture coordinates into a data index (which square to display colors for) and a modified set of texture coordinates that represent the position within that square.

For a horizontal arrangement, you could do something like:

float temp = vf_texcoord.x * uf_texLen;
float temp2 = floor(temp);
int dataIndex = temp2;
vec2 squareTexcoord = { temp2 - temp, vf_texcoord.y };

Then you'd use squareTexcoord to decide which quadrant of the square you're in:

int corner;
vec2 squareTexcoord2 = squareTexcoord - { 0.5, 0.5 }
if (abs(squareTexcoord2.x) > abs(squareTexcoord2.y)) {  // Left or right triangle
    if (squareTexcoord2.x > 0) {  // Right triangle
        corner = 0;
    }
    else {  // Left triangle
        corner = 1;
    }
}
else {  // Top or bottom triangle
    if (squareTexcoord2.y > 0) {  // Bottom triangle
        corner = 2;
    }
    else {  // Top triangle
        corner = 3;
    }
}

And now you have all you need for shading:

gl_FragColor = { encodeByte(int(texelFetch(uf_texture,dataIndex,0).r), corner), 1.0 };
Wyzard
  • 33,849
  • 3
  • 67
  • 87
  • I don't quite understand the `abs(squareTexcoord2.x) > abs(squareTexcoord2.y)` line. as far as I see that would check if we're in the lower right half of the sqare? – nonchip Jul 22 '14 at 07:23
  • It's checking whether the point is more to the left or right than it is to the top or bottom, which tells you whether it's in the "bow tie" or the "hourglass" part of the square. – Wyzard Jul 22 '14 at 07:26
  • btw, when using this code with a data texture of length=1, and only byte being 13, i get a 100% black output. – nonchip Jul 22 '14 at 07:26
  • ah, yes, sure, seems like my brain kinda ignored the `abs` call when parsing that line :D – nonchip Jul 22 '14 at 07:27
  • It's probably buggy; I haven't written any real GLSL in a year or so. Read it for the general idea, not the exact implementation. – Wyzard Jul 22 '14 at 07:27
  • yeah, i fixed some casting issues your code had, but as I'm catching all GLSL compiler errors, the only possible bug would be runtime. – nonchip Jul 22 '14 at 07:29
  • If you're testing with a single byte of data, take out the first part and just set `squareTexcoord = vf_texcoord` to eliminate one possible source of bugs. – Wyzard Jul 22 '14 at 07:31
  • did that, but still black. according to my manual calculations, 13 (=1101b) would be a square with upper triangle red and right triangle blue. – nonchip Jul 22 '14 at 07:34
  • I'd use a value where all the triangles should be non-black in order to make it obvious whether you're drawing anything at all. But SO comments aren't really a place for interactive debugging help; there's not much I can do for you. – Wyzard Jul 22 '14 at 07:37
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/57735/discussion-between-nonchip-and-wyzard). – nonchip Jul 22 '14 at 07:38