GL_UNSIGNED_BYTE - glsl

I've got colors in GL_UNSIGNED_BYTE r,g,b but I want to use the alpha channel to put a custom value that is going to be used inside the pixel shader to color the geometry differently.
There are two possible values 0 and 127 now my problem is that when I do this in the vertex shader :
[vertex]
varying float factor;
factor = gl_Color.w
it seems that the factor is always 1.0 because if I do this:
[fragment]
varying float factor;
factor = factor;
gl_FragColor = vec4(factor, 0.0, 0.0, 1.0)
The output is always red why I would expect two different colors, one when the factor is zero and one when the factor is 127.
So if I assign two values 0 and 127 I should get in the vertex shader 0/0.5? is this correct?
[Edit]
Ok I see now two different values but I don't know why I get them, there s any operation the GPU does in the gl_Colow.w component I am not aware of?
[Edit2]
As Nicholas has pointed out I am using glColorPointer(4...);

Since you are using the gl_Color input, and you make reference to GL_UNSIGNED_BYTE, I surmise that you are using glColorPointer to specify these color values. So in your code, you're calling something to the effect of:
glColorPointer(4, GL_UNSIGNED_BYTE, ...);
(BTW, in the future, it would be best if you actually provide this information, rather than forcing us to deduce it)
So, first issue: are you actually using 4 for the number of color components? You should be, but you don't say one way or the other.
Now that this has been corrected, let's get to the real issue (or at least it was in the original form of the question):
factor = factor/127.0;(to normalize)
OpenGL already normalized that for you. If you use any integer type with glColorPointer, the values you get in gl_Color will be normalized, either [0, 1] for UNSIGNED types, or [-1, 1] for non-unsigned types.

Related

Comparing two textures in openGL

I'm new to OpenGL and I'm looking forward to compare two textures to understand how much they are similar to each other. I know how to to this with two bitmap images but I really need to use a method to compare two textures.
Question is: Is there any way to compare two textures as we compare two images? Like comparing two images pixel by pixel?
Actually what you seem to be asking for is not possible or at least not as easy as it would seem to accomplish on the GPU. The problem is GPU is designed to accomplish as many small tasks as possible in the shortest amount of time. Iterating through an array of data such as pixels is not included so getting something like an integer or a floating value might be a bit hard.
There is one very interesting procedure you may try but I can not say the result will be appropriate for you:
You may first create a new texture that is a difference between the two input textures and then keep downsampling the result till 1x1 pixel texture and get the value of that pixel to see how different it is.
To achieve this it would be best to use a fixed size of the target buffer which is POT (power of two) for instance 256x256. If you didn't use a fixed size then the result could vary a lot depending on the image sizes.
So in first pass you would redraw the two textures to the 3rd one (using FBO - frame buffer object). The shader you would use is simply:
vec4 a = texture2D(iChannel0,uv);
vec4 b = texture2D(iChannel1,uv);
fragColor = abs(a-b);
So now you have a texture which represents the difference between the two images per pixel, per color component. If the two images will be the same, the result will be a totally black picture.
Now you will need to create a new FBO which is scaled by half in every dimension which comes to 128x128 in this example. To draw to this buffer you would need to use GL_NEAREST as a texture parameter so no interpolations on the texel fetching is done. Then for each new pixel sum the 4 nearest pixels of the source image:
vec4 originalTextCoord = varyingTextCoord;
vec4 textCoordRight = vec2(varyingTextCoord.x+1.0/256, varyingTextCoord.y);
vec4 textCoordBottom = vec2(varyingTextCoord.x, varyingTextCoord.y+1.0/256);
vec4 textCoordBottomRight = vec2(varyingTextCoord.x+1.0/256, varyingTextCoord.y+1.0/256);
fragColor = texture2D(iChannel0, originalTextCoord) +
texture2D(iChannel0, textCoordRight) +
texture2D(iChannel0, textCoordBottom) +
texture2D(iChannel0, textCoordBottomRight);
The 256 value is from the source texture so that should come as a uniform so you may reuse the same shader.
After this is drawn you need to drop down to 64, 32, 16... Then read the pixel back to the CPU and see the result.
Now unfortunately this procedure may produce very unwanted results. Since the colors are simply summed together this will produce an overflow for all the images which are not similar enough (results in a white pixel or rather (1,1,1,0) for non-transparent). This may be overcome first by using a scale on the first shader pass, to divide the output by a large enough value. Still this might not be enough and an average might need to be done in the second shader (multiply all the texture2D calls by .25).
In the end the result might still be a bit strange. You get 4 color components on the CPU which represent the sum or the average of an image differential. I guess you could sum them up and choose what you consider for the images to be much alike or not. But if you want to have a more sense in the result you are getting you might want to treat the whole pixel as a single 32-bit floating value (these are a bit tricky but you may find answers around the SO). This way you may compute the values without the overflows and get quite exact results from the algorithms. This means you would write the floating value as if it is a color which starts with the first shader output and continues for every other draw call (get texel, convert it to float, sum it, convert it back to vec4 and assign as output), GL_NEAREST is essential here.
If not then you may optimize the procedure and use GL_LINEAR instead of GL_NEAREST and simply keep redrawing the differential texture till it gets to a single pixel size (no need for 4 coordinates). This should produce a nice pixel which represents an average of all the pixels in the differential textures. So this is the average difference between pixels in the two images. Also this procedure should be quite fast.
Then if you want to do a bit smarter algorithm you may do some wonders on creating the differential texture. Simply subtracting the colors may not be the best approach. It would make more sense to blur one of the images and then comparing it to the other image. This will lose precision for those very similar images but for everything else it will give you a much better result. For instance you could say you are interested only if the pixel is 30% different then the weight of the other image (the blurred one) so you would discard and scale the 30% for every component such as result.r = clamp(abs(a.r-b.r)-30.0/100.0, .0, 1.0)/((100.0-30.0)/100.0);
You can bind both textures to a shader and visit each pixel by drawing a quad or something like this.
// Equal pixels are marked green. Different pixels are shown in red color.
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord.xy / iResolution.xy;
vec4 a = texture2D(iChannel0,uv);
vec4 b = texture2D(iChannel1,uv);
if(a != b)
fragColor = vec4(1,0,0,1);
else
fragColor = vec4(0,1,0,1);
}
You can test the shader on Shadertoy.
Or you can also bind both textures to a compute shader and visit every pixel by iteration.
You cannot compare vectors. You have to use
if( any(notEqual(a,b)))
Check the GLSL language spec

read and write integer 1-channel texture opengl

I want to:
create a read and writable 1-channel texture that contains integers.
using a shader, write integer "I" to the texture.
use the texture as a source, sample it and compare if the sample is equal to the integer I.
All this with core profile 3.3.
This is what I've got so far:
I create the texture like so:
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_INT, (java.nio.ByteBuffer) null);
I've Also tried GL_R8I and GL_RED_INTEGER, but that won't work.
I bind this texture as my FBO, set blendfunc to (GL_ONE, GL_ONE) (additive) and render 1 quad using a shader that simply does:
layout(location = 2) out float value;
main{ value = 1.0;}
Again, Integers won't work here!
Next, I bind another FBO and use the above as a source. I use a shader that samples the above with:
"if (texture2D(Tshadow, v_sampler_coo).r == 1.0){discard;}" + "\n"
The problem is that only way I've managed to get it to somehow work is by outputting:
out_diffuse = 1.0;
The compared:
if (texture2D(Tshadow, v_sampler_coo).r == 1.0){discard;}
Any other value, even a power of two (2^-2, 2^-3 etc) will not generate a true statement, nothing except 1.0.
Now what I'd ideally like is to be able to write an integer and sample that integer. But I can't manage. And regardless if I succeeded to write and integer, the sampler2D thing only returns normalized floats, right? And that's the only way to sample a color attachment?
UPDATE:
I found that if I write 0.5, then this work:
if (texture2D(Tshadow, v_sampler_coo).r == 0.4980392307){discard;}
The interal format GL_R8I is actually the correct format for your use case, and the spec explicitely lists that format as color-renderable.
layout(location = 2) out float value;
Why are you using a float output type if you intend to store integers? The OpenGL 3.3 core profile specification explicitely states the following:
Color values written by a fragment shader may be floating-point,
signed integer, or unsigned integer. If the color buffer has an signed
or unsigned normalized fixed-point format, color values are assumed to
be floating-point and are converted to fixed-point as described in
equations 2.6 or 2.4, respectively; otherwise no type conversion is
applied.
However, as you put it:
Now what I'd ideally like is to be able to write an integer and sample that integer.
You can do that, assuming you use the correct integer sampler type in the shader. What you can't do is use blending with integers. To quote the spec:
Blending applies only if the color buffer has a fixed-point or
floating-point format. If the color buffer has an integer format,
proceed to the next operation.
So if you need the blending, the best option is to work with GL_R8 normalized integer format, and with floating point outputs in the shader.
With more modern GL, you could simply emulate the additive blending by directly sampling the previous value in the shader which is updating the texture. Such feedback loops where a shader reads exactly the texel location it is later going to write to are well-defined and allowed in recent GL versions, but unfortunately not in GL3.x.
UPDATE
I found that if I write 0.5, then this work:
if (texture2D(Tshadow, v_sampler_coo).r == 0.4980392307){discard;}
Well, 0.5 is not representable by normalized integer formats (and actually, the bit depth does not even matter, it will never work). In the 8 bit case, the GL will convert
0.5 to 0.5*255 = 127.5. Now it will be implementation specific if it will round to 127 or 128, so you will end up with either 127.0/255.0 (which you got), or 128.0/255.0.
Note on the rounding rules: In the GL 3.3 spec, it is stated that the value after the multiplication
is then cast to an unsigned binary integer value with exactly b bits
With no rounding at all, so that 127 should always be the result. However, in the latest version, GL 4.5, it is stated as
returns one of the two unsigned binary integer values with exactly b
bits which are closest to the floating-point value r (where rounding
to nearest is preferred).
so actually, any rounding behavior is allowed...
As a final note: you should never compare floats for equality.

Shader - Color blending

I would like to know how to blend colors in a specific way.
Let's imagine that I have a color (A) and an other color (B).
I would like to blend them in such a way that if I choose white for the (B) then the output color is (A) but if have any other color for (B) it outputs a blending of (A) and (B).
I've tried the addition, but it doesn't give the expected result.
I've tried the multiplicative blending it's quite good for the (B) white value but it fail for a blue (B) and red (A) colors.
Any idea how to do that ?
With GLSL, the simplest approach is probably to use a branch. If colA and colB are the two vectors (of type vec4) holding your colors A and B:
if (any(lessThan(colB.xyz, vec3(1.0)))) {
outColor = colB;
} else {
outColor = colA;
}
Or, if you really want to avoid a branch, you could rely more on built-in functions. For example, using the observation that if all components are in the range [0.0, 1.0], the dot product of the vector with itself is 3.0 for the vector (1.0, 1.0, 1.0), and smaller for all other vectors:
outColor = mix(colB, colA, step(3.0, dot(colB.xyz, colB.xyz)))
You will have to benchmark to find out which of these is faster.
There may be some concern about floating point precision in the comparisons for both variations above. I believe it should be fine, since 1.0 can be represented as a float exactly. But if you do run into problems, you may want to allow for some imprecision by changing the constants that colB is compared against to slightly smaller values.
To avoid branching in a shader, one technique is to use lerp (linear interpolation). That is, use the would-be conditional variable as the lerp factor, so if it's 0 it's one color and if it's 1 it's the other color.
Be sure to reverse the logic since the second argument is what it blends to if cond=1. This also allows you to blend half way.
Example:
Instead of
Color result = (cond)? A:B;
use:
Color result=lerp(cond,B,A);

GL_FRAMEBUFFER_SRGB_EXT banding problems (gamma correction)

Consider the following code. imageDataf is a float*. In fact, as the code shows it consist of float4 values created by a ray tracer. Of course, the color values are in linear space and I need them gamma corrected for output on screen.
So what I can do is a simple for loop with a gamma correction of 2.2 (see for loop). Also, i can use GL_FRAMEBUFFER_SRGB_EXT, which works almost correclty but has "banding" problems.
Left is using GL_FRAMEBUFFER_SRGB_EXT, right is manual gamma correction. Right picture looks perfect. There may be some difficulties to spot it on some monitors. Does anyone have a clue how to fix this problem? I would like to do gamma correction for "free" as the CPU version makes the GUI a bit laggy. Note that the actual ray tracing is done in another thread using GPU(optix) so in fact its about as fast in rendering performance.
GLboolean sRGB = GL_FALSE;
glGetBooleanv( GL_FRAMEBUFFER_SRGB_CAPABLE_EXT, &sRGB );
if (sRGB) {
//glEnable(GL_FRAMEBUFFER_SRGB_EXT);
}
for(int i = 0; i < 768*768*4; i++)
{
imageDataf[i] = (float)powf(imageDataf[i], 1.0f/2.2f);
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 8);
glDrawPixels( static_cast<GLsizei>( buffer_width ), static_cast<GLsizei>( buffer_height ),
GL_RGBA, GL_FLOAT, (GLvoid*)imageDataf);
//glDisable(GL_FRAMEBUFFER_SRGB_EXT);
When GL_FRAMEBUFFER_SRGB is enabled, this means that OpenGL will assume that the colors for a fragment are in a linear colorspace. Therefore, when it writes them to an sRGB-format image, it will convert them internally from linear to sRGB. Except... your pixels are not linear. You already converted them to a non-linear colorspace.
However, I'll assume that you simply forgot an if statement in there. I'll assume that if the framebuffer is sRGB capable, you skip the loop and upload the data directly. So instead, I'll explain why you're getting banding.
You're getting banding because the OpenGL operation you asked for does the following. For each color you specify:
Clamp the floats to the [0, 1] range.
Convert the floats to unsigned, normalized, 8-bit integers.
Generate a fragment with that unsigned, normalized, 8-bit color.
Convert the unsigned, normalized, 8-bit fragment color from linear RGB space to sRGB space and store it.
Steps 1-3 all come from the use of glDrawPixels. Your problem is step 2. You want to keep your floating-point values as floats. Yet you insist on using the fixed-function pipeline (ie: glDrawPixels), which forces a conversion from float to unsigned normalized integers.
If you uploaded your data to a float texture and used a proper fragment shader to render this texture (even just a simple gl_FragColor = texture(tex, texCoord); shader), you'd be fine. The shader pipeline uses floating-point math, not integer math. So no such conversion would occur.
In short: stop using glDrawPixels.

Precision loss with mod in GLSL

I'm repeating a texture in the vertex shader (for storage, not for repeating at the spot). Is this the right way? I seem to lose precision somehwere.
varying vec2 texcoordC;
texcoordC = gl_MultiTexCoord0.xy;
texcoordC *= 10.0;
texcoordC.x = mod(texcoordC.x, 1.0);
texcoordC.y = mod(texcoordC.y, 1.0);
ADDED: I then save (storage) the texcoord in the color, print it to the texture and later use that texture again. When I retrieve the color from the texture, I find the texcoords and use them to apply a texture in postprocess. There's a reason I want it this way, that I won't go into. I get it that the texcoords will be limited by the color's precision, that is alright as my texture is 256 in width and height.
I know normally I would set the texcoords with glTexcoord2f to higher than 1.0 to repeat (and using GL_REPEAT), but I am using a modelloader which I am to lazy to edit, as I think it is not necessary/not the easiest way.
There are (at least) two ways in which this could go wrong:
Firstly yes, you will lose precision. You are essentially taking the fractional part of a floating point number, after scaling it up. This essentially throws some of the number away.
Secondly, this probably won't work anyway, not for most typical uses. You are trying to tile a texture per-vertex, but the texture is interpolated across a polygon. So this technique could tile the texture differently on different vertices of the same polygon, resulting in a bit of a mess.
i.e.
If vertex1 has a U of 1.5 (after scaling), and vertex2 has a U of 2.2, then you expect the interpolation to give increasing values between those points, with the half-way point having a U of 1.85.
If you take the modulo at each vertex, you will have a U of 0.5, and a U of 0.2 respectively, resulting in a decreasing U, and a half-way point with a U of 0.35...
Textures can be tiled just be enabling tiling on the texture/sampler, and using coordinates outside the range 0->1. If you really want to increase sampling accuracy and have a large amount of tiling, you need to wrap the UV coordinates uniformly across whole polygons, rather than per-vertex. i.e. do it in your data, not in the vertex shader.
For your case, where you're trying to output the UV coordinates into a buffer for some later purpose, you could clamp/wrap the UVs in the pixel shader. So multiply up the UV in the vertex shader, interpolate it across the polygon correctly, and then apply the modulo only when writing to the buffer.
However I still think you'll have precision issues as you're losing all the sub-pixel information. Whether or not that's a problem for the technique you're using, I don't know.