KhronosGroup / OpenGL-Registry

OpenGL, OpenGL ES, and OpenGL ES-SC API and Extension Registry
678 stars 274 forks source link

Out-of-range floating-point color values #549

Open SunSerega opened 1 year ago

SunSerega commented 1 year ago

This comment has a file that mentions all the cases I found but with links to related spec/extension.


void ClearColor( float r, float g, float b, float a );

sets the clear value for fixed-point and floating-point color buffers. The specified components are stored as floating-point values. [1]

So nothing is said about values, not in the [0; 1] range.

Description

glClearColor specifies the red, green, blue, and alpha values used by glClear to clear the color buffers. Values specified by glClearColor are clamped to the range [0,1]. [2]

But in HTML pages for the same spec version, they are clamped?


Even more confusing in the case of glColor4f:

Versions of the Color and SecondaryColor commands that take floating-point values accept values nominally between 0.0 and 1.0. 0.0 corresponds to the minimum while 1.0 corresponds to the maximum (machine dependent) value that a component may take on in the framebuffer (see section 2.14 on colors and coloring). Values outside [0, 1] are not clamped. [3]

Now it's explicitly not clamped. But then Microsoft walks in and:

Neither floating-point nor signed integer values are clamped to the range [0,1] before the current color is updated. However, color components are clamped to this range before they are interpolated or written into a color buffer. [4]

Well, isn't this an implementation detail? (I mean when the clamping is done, as long as before interpolation) And OpenGL API is supposed to only care about the apparent behavior...


Generally, from a few experiences with old OpenGL I remember - all the floating point color values seemed to be clamped. So it was quite a revelation to learn, for some functions none of the original .pdf specs define how out-of-range color values should behave.

I would understand if glClearColor with out-of-range values was implementation defined... But since glColor4f is explicitly said to not be clamped, I expect a line between two points with colors (2.0; 2.0; 2.0) and (0.0; 0.0; 0.0) to have a fully white point in the middle and descend to full black towards the second point. However, what I observe is a gradient starting at the first point - as if values passed to glColor4f are in fact clamped to be (1.0; 1.0; 1.0).

NogginBops commented 1 year ago

This is from page 524 (546 in the pdf):

Unsigned normalized fixed-point and signed normalized fixed-point RGBA
color buffers are cleared to color values derived by clamping each component of the
clear color to the range [0, 1] or [−1, 1] respectively, then converting the (possibly
sRGB converted and/or dithered) color to fixed-point using equations 2.3 or 2.4,
respectively. The result of clearing integer color buffers is undefined.

Doesn't say anything about non-normalized color buffers, which would imply they are not clamped. This is also the behavior I observe in my applications, normalized formats get clamped to [0, 1] while non-normalized formats use the floats without clamping.

gnl21 commented 1 year ago

I agree with @NogginBops, this is how I would expect my implementation to behave. Values are clamped only when they reach the framebuffer and only if the framebuffer is in a normalised format (so can't represent the input colours). If you have a floating-point colour buffer then clears and rendering results will just write out-of-range values without clamping them.

I expect a line between two points with colors (2.0; 2.0; 2.0) and (0.0; 0.0; 0.0) to have a fully white point in the middle and descend to full black towards the second point. However, what I observe is a gradient starting at the first point

I agree with your expectation here, although if you're using floating-point framebuffers then how they are interpreted by the underlying window system is not defined by GL.

SunSerega commented 1 year ago

Ok, so basically clamping I see is a behavior of the default windows framebuffer implementation. In other words [4] is correct because that's what Microsoft says about its own code, but [2] is wrong because it's different from original PDF?

And, as I understand, [1] and [3] behave the same, but are described differently in PDF: [3] promises the values will not be clamped, but in the end, it is effectively implementation-defined; meanwhile [1] doesn't say anything, which at least isn't wrong, but... No less confusing.

gnl21 commented 1 year ago

Ok, so basically clamping I see is a behavior of the default windows framebuffer implementation.

Not necessarily the default windows framebuffer, but rather a behaviour of all GL framebuffers. If the framebuffer is stored in a normalised format then it can only represent values in the range [0, 1], so all values that are stored into that kind of buffer are in the range [0, 1] by definition, so GL clamps those writes. This isn't implementation dependent but it is format dependent. I think this is what MS are trying to describe in [4], but since it's information about some other command, not the one being specified, is imprecise and has ended up confusing.

[1] and [3] are consistent, but in [1] it is assumed that if no clamp is mentioned then no clamp is done. Given the profusion of different descriptions here (and the fact that I think the behaviour may have changed from older GL versions, maybe), this does seem quite confusing. [2] seems just to be wrong, I think.

pdaniell-nv commented 1 year ago

Yeah [2] is definitely wrong, and should be updated to match the OpenGL specs. Clamping at the glClearColor time was removed in OpenGL 3.0.

pdaniell-nv commented 1 year ago

@SunSerega would you want to create a PR to fix the refpage at https://github.com/KhronosGroup/OpenGL-Refpages/blob/main/gl4/glClearColor.xml?