How does opengl depth testing use the 24bit depth buffer? - opengl

I am using 32bit float values which I input into the vertex shader for the x,y,z positions of every vertex. But, I have read that opengl uses 24bit for depth buffer and 8 bit for stencil buffer.
Since, I am copying the same 32bit float in gl_position that I receive as input in vertex shader, I want to understand how does opengl convert this 32bit float to 24bit for depth testing.

The gl_Position in the vertex shader is a clip space coordinate. There will be a division by w to generate normalized device coordinates, where the visible range is [-1,1] in OpenGL (by default, can be changed nowadays). Those values will be transformed according to the currently set glDepthRange parameters to finally get the window space z value, which is in the range [0,1].
The depth buffer must just store these values, and - very similar to color values which often store only even 8 bit per channel values - an integer depth buffer is used to represent fixed point values in that range.
Quoting from setction 13.6 "coordinate transformations" of the OpenGL 4.5 core profile spec (emphasis mine):
z_w may be represented using either a fixed-point or floating-point representation.
However, a floating-point representation must be used if the draw framebuffer has a floating-point depth buffer. If an
m-bit fixed-point representation is used, we
assume that it represents each value k/(2^m-1),
where k in {0,1,...,2^m- 1}, as k (e.g. 1.0 is represented in binary as a string of all ones).
So, the window space z_w value (which is in [0,1]) is just multiplied by 2^m -1, and rounded to integer, and the result is stored in the buffer.

Related

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.

OpenGL/GLSL Color Attachment range

Is there a way, in GLSL/OpenGL textures, to store floats which are higher than 1 or lower than 0 ?
I'm working on a deferred rendering framework but when i try to store the positions as non-homogenious values (first shader) i get only values between 0-1 in my phong shader (second shader).
Same with normals, the light was displaying wrong.
The way to fix this was in the first shader :
gbuffer[x] =normal *0.5 +1. and in phong : normal * 2. - 1. //(non-)homogenious conversation
But I don't want to use this method.
So my current texture format is RGB. I tried RGB_16 but then I get a black window.
GL_RGB16 is still a normalized format, which means that the sampled value is between 0.0 and 1.0.
What you need to get a range outside of [0.0, 1.0] is a floating point texture. The formats with 3 components are GL_RGB16F for 16-bit float components and GL_RGB32F for 32-bit float components. Those two formats are not guaranteed to be supported for render targets, though (see table 8.12 in GL 4.5 spec). You will need to use their 4-component versions if you need to render to them: GL_RGBA16F or GL_RGBA32F.
If you still have a fixed range for your values, the approach you tried where you map your given range into a [0.0, 1.0] range by applying an offset/scale actually looks very valid to me. Using for example GL_RGBA16 gives you 16 bits of precision, while you get only 12 bits of precision from GL_RGBA16F with the same memory usage, since 4 bits are used for the exponent.

glTexImage2D When Exactly Does Clamping Happen

I have seen (several) (semi)-(related) (posts) (about) this, but none answers the following question directly:
When exactly does glTexImage.D clamp input values to [0,1]?
The documentation seems to imply that it always does (with the possible exception of GL_DEPTH_STENCIL, which I seem to remember being disallowed, although the documentation doesn't support this). This is how I'm currently handling it in my code.
Other of the linked sources seem to suggest that data is clamped exactly when it is converted to a normalized color format, or that it is only clamped when the image format is integral. To confirm this, I tested a GL_RGBAF32 internal format: negative float data are clamped to 0, while positive data aren't clamped at all.
Then there's (all combinations of) unsigned/signed normalized/unnormalized integer internal formats.
So, when does pixel unpack, specifically glTexImage.D, clamp data? I'm specifically interested in GL_((R|RG|RGB|RGBA)((8|16|32)(|I|UI|_SNORM)|(16F|32F))|DEPTH_COMPONENT(16|24|32)) (i.e., all the color floating-point and integer textures, plus all depth textures), but probably all internal formats should be addressed.
It does not have to clamp anything, technically.
One place this behavior is defined is by the pixel transfer operation. In other words, if the internal format and the format of the data you are transfering into the texture differ, then lots of interesting things can happen including scaling/clamping to a certain range. If you properly match internal format and pixel transfer format you can avoid implicit normalization and clamping of the image data since no conversion will be necessary.
Of course, pixel transfer is only one source of clamping. In GL4.3 you can share the underlying data store between multiple texture objects and change the internal format for compatible image classes (e.g. any 32-bit texture format) using texture views. That can be used to reinterpret UNORM image data as SNORM when sampled, for example.
I should probably explain that something like GL_RGB8 is fixed-point... more specifically, it is Unsigned Normalized (UNORM). If you were to transfer image data into a GL_RGBA32F image using GL_RGBA as the pixel transfer format and GL_UNSIGNED_BYTE as the data type GL will interpret that as conversion from GL_RGBA8 (source format) to GL_RGBA32F (internal format).
In GLES you must always match these formats because pixel transfer conversion is undefined. In desktop GL, however, this example is going to produce values only in the range [0.0, 1.0]. If you wanted the range to be [-1.0, 1.0] you would need to use an SNORM format.
It works the other way around too, if you try to read from a texture with a different internal format and destination format. If you want to get the proper values into / out of your texture, you need to avoid pixel transfer conversion.
OpenGL 4.4 Core Profile Specification - 8.5 Texture Image Specification - pp. 183
8.4.4.2 Conversion to floating-point
This step applies only to groups of floating-point components. It is not performed on indices or integer components. For groups containing both components and indices, such as GL_DEPTH_STENCIL, the indices are not converted.
Each element in a group is converted to a floating-point value. For unsigned or signed integer elements, equations 2.1 or 2.2, respectively, are used.
The specification goes on later to further clarify this behavior for all classes of image data:
OpenGL 4.4 Core Profile Specification - 8.5 Texture Image Specification - pp. 184
The selected groups are transferred to the GL as described in section 8.4.4 and then clamped to the representable range of the internal format. If the internalformat of the texture is signed or unsigned integer, components are clamped to [−2n−1, 2n−1 − 1] or [0, 2n − 1], respectively, where n is the number of bits per component. For color component groups, if the internalformat of the texture is signed or unsigned normalized fixed-point, components are clamped to [−1, 1] or [0, 1], respectively. For depth component groups, the depth value is clamped to [0, 1]. Otherwise, values are not modified. Stencil index values are masked by 2n − 1, where n is the number of stencil bits in the internal format resolution (see below). If the base internal format is GL_DEPTH_STENCIL and format is not GL_DEPTH_STENCIL, then the values of the stencil index texture components are undefined.

Fragment shader output values

I'm using my alpha channel as an 8 bit integer index for something unrelated to blending so I want to carefully control the bit values. In particular, I need for all of the pixels from one FBO-rendered texture with a particular alpha value to match all of the pixels with the same alpha value in the shader. Experience has taught me to be careful when comparing floating point values for equality...
While setting the color values using the floating point vec4 might not cause me issues, and my understanding is that even a half precision 16bit float will be able to differentiate all 8 bit integer (0-255) values. But I would prefer to perform integer operations in the fragment shader so I am certain of the values.
Am I likely to incur a performance hit by performing integer ops in the fragment shader?
How is the output scaled? I read somewhere that it is valid to send integer vectors as color output for a fragment. But how is it scaled? If I send a uvec4 with integers 0-255 will it scale that appropriately? I'd like for it to directly write the integer value into the pixel format, for integer formats I don't want it to do any scaling. Perhaps for RGBA8 sending in an int value above 255 would clamp it to 255, and clamp negative ints to zero, and so on.
This issue is made difficult by the fact that I cannot debug by printing out the color values unless I grab the rendered images and examine them carefully. Perhaps I can draw a bright color if something fails to match.
Here is a relevant thread I found on this topic. It has confused me even more than before.
I suggest not using the color attachment's alpha channel, but an additional render target with an explicit integer format. This is available since at least OpenGL-3.1 (the oldest spec I looked at, for this answer). See the OpenGL function glBindFragDataLocation, which binds a fragments shader out variable. In your case a int out $VARIABLENAME. For input into the next state use a integer sampler. I refer you to the specification of OpenGL-3.1 and GLSL-1.30 for the details.

What exactly is a floating point texture?

I tried reading the OpenGL ARB_texture_float spec, but I still cannot get it in my head..
And how is floating point data related to just normal 8-bit per channel RGBA or RGB data from an image that I am loading into a texture?
Here is a read a little bit here about it.
Basically floating point texture is a texture in which data is of floating point type :)
That is it is not clamped. So if you have 3.14f in your texture you will read the same value in the shader.
You may create them with different numbers of channels. Also you may crate 16 or 32 bit textures depending on the format. e.g.
// create 32bit 4 component texture, each component has type float
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 16, 16, 0, GL_RGBA, GL_FLOAT, data);
where data could be like this:
float data[16][16];
for(int i=0;i<16*16;++i) data[i] = sin(i*M_PI/180.0f); // whatever
then in shader you can get exactly same (if you use FLOAT32 texture) value.
e.g.
uniform sampler2D myFloatTex;
float value = texture2D(myFloatTex, texcoord.xy);
If you were using 16bit format, say GL_RGBA16F, then whenever you read in shader you will have a convertion. So, to avoid this you may use half4 type:
half4 value = texture2D(my16BitTex, texcoord.xy);
So, basically, difference between the normalized 8bit and floating point texture is that in the first case your values will be brought to [0..1] range and clamped, whereas in latter you will receive your values as is ( except for 16<->32 conversion, see my example above).
Not that you'd probably want to use them with FBO as a render target, in this case you need to know that not all of the formats may be attached as a render target. E.g. you cannot attach Luminance and intensity formats.
Also not all hardware supports filtering of floating point textures, so you need to check it first for your case if you need it.
Hope this helps.
FP textures have a special designated range of internal formats (RGBA_16F,RGBA_32F,etc).
Regular textures store fixed-point data, so reading from them gives you [0,1] range values. Contrary, FP textures give you [-inf,+inf] range as a result (not necessarily with a higher precision).
In many cases (like HDR rendering) you can easily proceed without FP textures, just by transforming the values to fit in [0,1] range. But there are cases like deferred rendering when you may want to store, for example, world-space coordinate without caring about their range.