I have a luminance map in LibGDX. I use a ByteBuffer to load my pixels, which works fine.
Gdx.graphics.getGL20().glPixelStorei(GL20.GL_UNPACK_ALIGNMENT, 1);
Gdx.graphics.getGL20().glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL20.GL_LUMINANCE, overviewBlock.getWidth(), overviewBlock.getHeight(), 0,
GL20.GL_LUMINANCE,
GL20.GL_UNSIGNED_BYTE, overviewBuffer);
However, I have trouble understanding how bytes are translated to the floats in GLSL. For a color based on an integer, writing to the ByteBuffer works like a charm. But I am not sure how to do this with a single byte color.
buffer.put((byte)255);
This does not result in a 1.0 value in GLSL/OpenGL, if I'm not mistaken. What is the correct way to get values in GLSL between 0 and 1.0, from say an integer ranged 0 - 255?
I'm using buffer.write((byte)(integer number & 0xff)) to write my bytes as recommended per the comments.
Related
Details: I have a glsl fragment shader with a uniform texture, "u_MapTexture" with several thousand colors on it (max of about 10k-15k unique rgb values). I also have a uniform palette texture ("u_paletteTexture") that is 16384 × 1 that I want to use to index the colors on u_MapTexture to. My problem is that no matter what I try mathematically, I can't seem to properly index the colors from the first texture to the palette texture using the RGB values of the passed color. Amy thoughts or ideas as to how I could do this?
Wasn't sure whether to post this here, on Gamedev SE, or on the Math SE.
Edit: I guess I might not have added enough information about the problem in, so here are some more details.
My current idea for the map is to keep an indexed palette of province colors, and to perform a palette-swap operation in my fragment shader (like the one outlined in this SO question: Simulating palette swaps with OpenGL Shaders (in LibGDX)). My shader is pretty much exactly copied from the linked article.
My problem: finding a way to uniquely index the province map (the original texture) -> province colors (the indexed palette texture).
At first, I decided that the palette texture would be configured as a (255+255)×(255+255) texture. This would give a large maximum number enough number of countries to choose from that would never in practice be reached.
I thought you could get the appropriate index of the palette texture of a country's color by getting its index in the texture as so: the index of each country would have been located at that palette texture's (x, y)->(r+g),(g+b)
I ran some example colors through this simple equation and came across a troubling scenario:
RGB (0, 0, 0) -> (0, 0);
RGB (1, 0, 1) -> (1, 1); ?
RGB (1, 3, 2) -> (4, 5);
RGB (0, 1, 0) -> (1, 1); ?
RGB (2, 5, 10) -> (7, 15);
RGB (255, 255, 255) -> (510, 510);
The question marks are by "recurring" colors in the algorithm, meaning that they would incorrectly map to the same country index.
Then I thought to add additional parameters and shrink the texture to a 1-dimensional array.
For example, the palette texture would have been of size (r+g+b),(r, g, b).
With this, with them same texture points:
RGB(0, 0, 0) -> (0);
RGB(1, 0, 1) -> (2); ?
RGB(0, 1, 1) -> (2); ?
RGB(1, 3, 2) -> (6); ?
RGB(3, 2, 1) -> (6); ?
RGB(0, 1, 0) -> (1);
RGB(2, 5, 10) -> (17);
RGB(255, 255, 255) -> (1020);
The recurrence problem is exacerbated. I did some quick calculations in my head (and thought about it more deeply in general) and I realized that no matter how many ways I add/multiply the color rgb variables, the same problem will occur due to the laws of mathematics. This leads to the actual problem: How can I uniquely and procedurally index country colors in the palette texture and access them via my shader? This seems like the most performant method, but its implementation is eluding me.
Also, for the record, I know that the UV coords and color values are floats, but I'm using the standard 0-255 format to logic the problem out.
TL;DR I need to extract a unique index from every RGB value, and that doesn't appear to be possible based on my test sets.
Basically the MCVE would be creating a 2D sprite and passing the fragment shader of the accepted answer of the linked SO question to sprite. The sprite would be comprised of about 10 unique RGB values, however whatever system used would have to support at least several thousand different unique colors. I don't have stable internet connection or I would upload my test textures.
Not sure if I get it right anyway let assume integer channels <0,255> so:
id = r + 256*g + 65536*b
that will give you id = <0,16777215>. Now just remap to your xs*ys texture:
x = id%xs
y = id/xs
where xs,ys is the resolution of the texture. Once you realize you can use powers of 2 for all of this you can use bit operations instead. For example let xs=4096,ys=4096 ...
id = r + g<<8 + b<<16
x = id&4095
y = id>>12
[Edit1]
So if I use this image you linked as input (txr_map):
And generate 4096x4096 texture all filed with 0x00404040 gray color except:
((DWORD*)(scr.txrs.txr.txr))[0x4A3020]=0x00FF0000;
((DWORD*)(scr.txrs.txr.txr))[0x49247E]=0x0000FF00;
((DWORD*)(scr.txrs.txr.txr))[0xCB3EAD]=0x000000FF;
((DWORD*)(scr.txrs.txr.txr))[0xC78A4F]=0x0000FFFF;
((DWORD*)(scr.txrs.txr.txr))[0x593D4E]=0x00FF00FF;
((DWORD*)(scr.txrs.txr.txr))[0x4B3C7E]=0x00FFFF00;
where scr.txrs.txr.txr is linearly allocated texture array so address is also your id... This selects few regions I picked up with color picker and set them with specific colors (red,green,blue,...).
Do not forget to set GL_LINEAR for min and mag filter. Then applying these shaders should do the trick:
//---------------------------------------------------------------------------
// Vertex
//---------------------------------------------------------------------------
#version 120
varying vec2 pos; // screen position <-1,+1>
varying vec2 txr; // texture position <0,1>
void main()
{
pos=gl_Vertex.xy;
txr=gl_MultiTexCoord0.st;
gl_Position=gl_Vertex;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Fragment
//---------------------------------------------------------------------------
#version 130
in vec2 pos; // screen position <-1,+1>
in vec2 txr; // texture position <0,1>
out vec4 col;
uniform sampler2D txr_map;
uniform sampler2D txr_pal;
//---------------------------------------------------------------------------
void main()
{
vec3 c;
int id,x,y;
c=texture2D(txr_map,txr).rgb;
x=int(float(c.b*255.0f)); id =x;
x=int(float(c.g*255.0f)); id|=x<<8;
x=int(float(c.r*255.0f)); id|=x<<16;
x= id &4095;
y=(id>>12)&4095;
c.s=(float(x)+0.5f)/4096.0f;
c.t=(float(y)+0.5f)/4096.0f;
col=texture2D(txr_pal,c.st);
}
//---------------------------------------------------------------------------
Sadly usampler2D does not work in my engine in the old API (that is why I use floats most likely some internal texture format problem). My CPU side GL code looks like this:
//---------------------------------------------------------------------------
OpenGLscreen scr; // my GL engine
GLSLprogram shd; // shaders
GLint txr_map=-1; // map
GLint txr_pal=-1; // palette
//---------------------------------------------------------------------------
void TForm1::draw()
{
scr.cls(); // glClear
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
shd.bind(); // use shader program
int unit=0;
scr.txrs.bind(txr_map,unit); shd.set1i("txr_map",unit); unit++; // bind textures and set uniforms
scr.txrs.bind(txr_pal,unit); shd.set1i("txr_pal",unit); unit++;
float a=5632.0/8192.0; // handle texture power of 2 size correction
glActiveTexture(GL_TEXTURE0);
glBegin(GL_QUADS);
glTexCoord2f(0.0,1.0); glVertex2f(-1.0,-1.0);
glTexCoord2f(0.0,0.0); glVertex2f(-1.0,+1.0);
glTexCoord2f( a ,0.0); glVertex2f(+1.0,+1.0);
glTexCoord2f( a ,1.0); glVertex2f(+1.0,-1.0);
glEnd();
for (unit--;unit>=0;unit--) scr.txrs.unbind(unit); // unbind textures
shd.unbind(); // unbind shaders
// just prints the GLSL logs for debug
scr.text_init_pix(1.0);
glColor4f(1.0,1.0,1.0,0.75);
scr.text(0.0,0.0,shd.log);
scr.text_exit_pixel();
scr.exe(); // glFlush
scr.rfs(); // swap buffers
}
//---------------------------------------------------------------------------
The result looks like this:
When I mix both result and input texture (for visual check) with:
col=(0.9*texture2D(txr_pal,c.st))+(0.1*texture2D(txr_map,txr));
The result looks like this:
So it clearly works as expected...
Not sure I understand exactly what you want to do.
First of all, the only way to uniquely map all 8-bit RGB colors to indices is to have 256^3 indices. You can shuffle the bits around to have a non-identity mapping (like here), but you still need that many destination indices.
If only a subset of all colors is used and you want fewer than 256^3 destination indices (as you seem to describe), some mechanism needs to be put in place to avoid collisions. Unless you have some special properties in the source colors that can be exploited mathematically, this mechanism will require some form of storage (like another texture or an SSBO).
Now what I don't understand is what you want to map to indices. Do you want to map all possible colors to a unique index? Does everything related to the mapping have to be done exclusively inside the shader? You mention countries and provinces, but I don't quite get how they relate exactly to the mapping you want.
When running the following code, "-2" is being traced and I am wrecking my head trying to understand why.
var bmd:BitmapData = new BitmapData(1,1,true,0xFFFFFFFF);
bmd.setPixel32(0,0, 0x32FF6B45);
trace(0x32FF6B45-bmd.getPixel32(0,0));
As far as I can tell, it should trace 0. 0x32FF6B45 is initially assigned to the pixel at coords 0,0. That value should be returned in bmd.getPixel32(0,0) and then, when it's subtracted from 0x32FF6B45, it should result in 0. Why the heck am I getting -2?
EDIT:
I've traced out the values individually and it makes sense that the operation in the trace above results in -2 because tracing out 0x32FF6B45 results in 855599941 and tracing out bmd.getPixel32(0,0) results in 855599943. The question now is why the heck are those values different? Whey doesn't bmd.getPixel32(0,0) also trace out 855599941?
I have the same problem, and I believe it is related to premultiplied alpha, as described here. In my code I was setting a pixel to 0xa08800ff and getting back 0xa08700ff. If you need alphas other than 0xff, then unfortunately it may be necessary to simultaneously store all your pixel values in a separate data structure too.
That is expected.
getPixel
This will return a value: #RRGGBB (rgb / red, green, blue)
getPixel32
This will return a value: #AARRGGBB (argb / alpha, red, green, blue)
Example:
trace('test 0x32FF6B45: '+0x32FF6B45);
var bmd:BitmapData = new BitmapData(1,1,true,0xFFFFFFFF);
trace('setting 0,0 to 0x32FF6B45');
bmd.setPixel32(0,0, 0x32FF6B45);
var color:* = bmd.getPixel32(0,0)
trace('0,0: '+color);
trace(color-bmd.getPixel32(0,0));
Results:
test 0x32FF6B45: 855599941
setting 0,0 to 0x32FF6B45
0,0: 855599943
0
From what I can tell, you're using a color that is out-of-bounds to Flash. I'm not sure of the color range, but I know in previous experiences when taking photoshop elements with many colors, sometimes objects failed to import because the color value was out of bounds.
#Jari is also correct about the transparency.
I'd like to keep a lot of images as bitmapData in memory. The images are monochromatic, so I actually don't need RGB or RGBA values. Is there any way to set the internal format of a bitmapData to monochromatic or another way to display images other than using BitmapData?
No, there's not monochromatic format for BitmapData.
No, you can't display images in anything other them BitmapData (ok, shaders and such, but it's really not the same).
However, you could use ByteArray to save the data not used currently to later use some BitmapData and setPixel to set pixels by splitting the single channel value into 3 values.
If you have up to 4 images the same size, you could re-use a single BitmapData object to store all of them in different channels, and use a ColorMatrixFilter to show just the channel you want.
This would be faster (and probably less code) than wvxvw's suggestion of storing the data in a ByteArray and using setPixel.
// store data in the red channel
bitmap.bitmapData.copyChannel( im1.bitmapData, im1.bitmapData.rect, new Point(), BitmapDataChannel.RED, BitmapDataChannel.RED );
// store data in the green channel
bitmap.bitmapData.copyChannel( im2.bitmapData, im2.bitmapData.rect, new Point(), BitmapDataChannel.GREEN, BitmapDataChannel.GREEN);
// e.g. filter the bitmap to just show the green channel
// (1's in first col for red, 3rd col for blue, 4th for alpha
var greenChannelFilter:ColorMatrixFilter = new ColorMatrixFilter(
[ 0,1,0,0,0,
0,1,0,0,0,
0,1,0,0,0,
0,0,0,0,255 ]);
bitmap.filters = [greenChannelFilter];
I have an (R, G, B) triplet, where each color is between 0.0 and 1.0 . Given a factor F (0.0 means the original color and 1.0 means white), I want to calculate a new triplet that is the “watermarked” version of the color.
I use the following expression (pseudo-code):
for each c in R, G, B:
new_c ← c + F × (1 - c)
This produces something that looks okayish, but I understand this introduces deviations to the hue of the color (checking the HSV equivalent before and after the transformation), and I don't know if this is to be expected.
Is there a “standard” (with or without quotes) algorithm to calculate the “watermarked” version of the color? If yes, which is it? If not, what other algorithms to the same effect can you tell me?
Actually this looks like it should give the correct hue, minus small variations for arithmetic rounding errors.
This is certainly a reasonable, simple was to achieve a watermark effect. I don't know of any other "standard" ones, there are a few ways you could do it.
Alternatives are:
Blend with white but do it non-linearly on F, e.g. new_c = c + sqrt(F)*(1-c), or you could use other non-linear functions - it might help the watermark look more or less "flat".
You could do it more efficiently by doing the following (where F takes the range 0..INF):
new_c = 1 - (1-c)/pow(2, F)
for real pixel values (0..255) this would convert into:
new_c = 255 - (255-c)>>F
Not only is that reasonably fast in integer arithmetic, but you may be able to do it in a 32b integer in parallel.
Why not just?
new_c = F*c
I think you should go first over watermarking pixels and figure out if it should be darker or lighter.
For lighter the formula might be
new_c=1-F*(c-1)
The program will show the student a line graph. The student will have to recreate that line graph by moving a character away from or toward a motion detector using the arrow keys, creating a distance-time plot. I can capture the data points that the program generates when drawing its graph. I can also capture the data points gnerated by the student. How can I compare the two graphs while allowing for some tolerance on the student's part? Should I try to detect incorrect graphs as they are being drawn or after all data points are recorded? While some of the graphs will be linear and easy to compare others will be piecewise functions with positive, negative, and zero slopes at random intervals.
Thanks!
Does the order in which the graph lines are drawn matter ?
You could record the points with a certain threshold into an Array/Vector and compare.
A quick'n'dirty way would be using 2 binary(monochrome, just black and white) images:
One image will be a 'print screen'(BitmapData.draw()) of the graph(e.g. black on white)
The other image will be a white(blank) BitmapData that you'll use to write black pixels
where the user/student draws(has the mouse while it's pressed).
e.g.
userBitmapData.setPixel(mouseX,mouseY,0x000000);
When the drawing is complete(either the mouse is released or whatever rule you set),
you run a function that checks how much black pixels from the source(original graph) image
are matched in the destination(user graph) image.
Either you create a BitmapData containing the other two bitmaps blended on Difference mode, so anything that isn't black is not a match, or just loop through all the pixels once and manually check if the pixels match. Note that this relies on the fact that dimensions(width,height) of the two images are the same.
Here's a bit of code to illustrate this:
function compare(source:BitmapData,destination:BitmapData,threshold:Number):Boolean{
var commonPixels:Number = 0, totalPixels:Number = 0;
for(var j:int = 0 ; j < source.height ; j++){
for(var i:int = 0 ; i < source.width; i++){
pixels++;
if(source.getPixel(i,j) == destination.getPixel(i,j)) commonPixels++;
}
}
trace('matching: ' + (commonPixels/pixels * 100) + ' % ');//delete this line,just testing
if(commonPixels/pixels >= threshold) return true;
else return false;
}
//usage:
trace('is the graph correct ?: ' + compare(graphBitmapData,userBitmapData,0.7));
The Vector/Array version would be similar, but there would be no visual cues. Depending on your setup, you might want to test which would work best for you: BitmapData takes more memory than Arrays, but you can easily create a Bitmap, add it to the display list and check if looks right, etc.
If speed is an issue:
using Vector. instead of Array might be faster
looping in reverse(highest number to 0, decrementing) also should speed up things a bit
you probably get away with one loop instead of two
e.g.
var pixels:int = source.width * source.height;
for(pixels; pixels >=0; pixels--)
HTH