Advertisement

sRGB to Linear with values greater than 1.0?

Started by May 30, 2024 01:50 PM
4 comments, last by Josh Klint 6 months, 3 weeks ago

In our engine we allow brightness greater than 1.0. How should these values be handled when converting sRGB colors to linear and back?

10x Faster Performance for VR: www.ultraengine.com

sRGB by definition can't be greater than 1 or less than 0. If you have an sRGB texture (e.g. for diffuse albedo), it should be converted to linear by the hardware automatically when sampling if you use the right texture format. This produces a linear value in the range [0,1]. Then you can use this linear value for lighting calculations in a shader. When the final HDR pixel value is calculated, which can be in the range [0,infinity], then you can apply tone mapping to bring it back to the [0,1] linear range for display. If your default framebuffer is sRGB, then you can get the hardware to automatically convert the [0,1] linear to [0,1] sRGB.

It's important to understand the point of sRGB. It only exists to fit a wider dynamic range into a limited 8-bit precision, which is why I think you have an HDR image import bug in your other thread. HDR should not have an sRGB curve.

Advertisement

The albedo textures are always between 0 and 1, but light colors can be greater than 1.0, in order to “overdrive” the lights.

I'm pretty sure a lot of HDRIs contain linear values that are much greater than 1.0 as well.

It sounds like perhaps light color and cubemaps should always be stored in linear space.

10x Faster Performance for VR: www.ultraengine.com

Josh Klint said:
light color and cubemaps should always be stored in linear space.

That's true. I use 16F formats for reflection and ambient diffuse cube maps, as well as for the whole post-processing pipeline and any intermediate results. Light color is expressed in linear lux or W/m^2 and can be in range [0,infinity). The only place I use sRGB is for diffuse albedo texture and the final framebuffer displayed to the user. Everything else is linear, even if only 8 bits (normals, ambient occlusion, roughness, metalness, displacement, etc.).

Yeah, same.

I thought about it and for values over 1.0, linear and sRGB should just be treated the same: 2.0 linear is also 2.0 sRGB. It doesn't really matter what conversion you use above 1.0 as long as it consistently transforms a linear value of, say, 1.1 to sRGB and back to the same value.

Like you pointed out, I should probably keep everything in linear color space throughout all the post-processing chain, and then only convert to sRGB on the final pass, to retain the best precision.

10x Faster Performance for VR: www.ultraengine.com

This topic is closed to new replies.

Advertisement