2
votes

I want to:

  1. create a read and writable 1-channel texture that contains integers.
  2. using a shader, write integer "I" to the texture.
  3. use the texture as a source, sample it and compare if the sample is equal to the integer I.
  4. All this with core profile 3.3.

This is what I've got so far:

  1. 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.

  1. 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;}
1
floating-point and integer data are almost mutually exclusive throughout the GL pipeline. Conversion between the two are undefined in most cases. The only integer values that do generally work as floating-point are actually fixed-point ((un)signed normalized).Andon M. Coleman

1 Answers

2
votes

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.