Jump to content
  • entries
    940
  • comments
    5,894
  • views
    864,015

High Dynamic Range Rendering


Josh

2,710 views

 Share

First, there's a lot of confusion about what HDR actually is, and I want to clear it up. HDR does not automatically mean "iris adjustment" although the two often go together. HDR, at its simplest, means that colors won't get washed out during the rendering pipeline.

 

Game graphics inputs are 8-bit (per channel textures). The output is 8-bit (unless you have a 10-bit monitor). So if we only perform one rendering step, it is impossible to lose color resolution. Even if our lighting step brightens the light beyond the brightest color possible (255,255,255), it doesn't matter, because your monitor can't display the difference anyways.

 

However, if we have post-processing effects that darken the screen again, we CAN lose color resolution. I made an extreme example to showcase this idea. Here's my "brighten" post-processing effect, which increases the brightness of each pixel by four times:

#version 400

uniform sampler2D texture1;
uniform bool isbackbuffer;
uniform vec2 buffersize;

out vec4 fragData0;

void main() 
{
   vec2 tcoord = vec2(gl_FragCoord.xy/buffersize);
   if (isbackbuffer) tcoord.y = 1.0 - tcoord.y;
   vec4 texcolor = texture(texture1, tcoord);
   fragData0 = texcolor * 4.0;
}

 

And here's my "darken" post-processing effect, which reverses this effect:

#version 400

uniform sampler2D texture1;
uniform bool isbackbuffer;
uniform vec2 buffersize;

out vec4 fragData0;

void main() 
{
   vec2 tcoord = vec2(gl_FragCoord.xy/buffersize);
   if (isbackbuffer) tcoord.y = 1.0 - tcoord.y;
   vec4 texcolor = texture(texture1, tcoord);
   fragData0 = texcolor / 4.0;
}

 

Here is a simple scene with no post-processing effect applied:

blogentry-1-0-82766500-1463435098_thumb.jpg

 

If I place the brighten effect first, and follow it by the darken effect, the color will get clamped at the max value of (255,255,255) and then darkened, resulting in a washed out image:

blogentry-1-0-95765200-1463433129_thumb.jpg

 

If we use the darken effect first, and then follow up with the brighten effect, the color gets compressed and inaccurate. I had to crank the multiplier up to 60 to show the effect, but you get the idea.

blogentry-1-0-49920400-1463433417_thumb.jpg

 

HDR solves this problem by using higher-accuracy float buffers in the middle stage processing. In this example, it results in an image that is identical to what we could see if neither of these post-processing effects were in use:

blogentry-1-0-31240200-1463433560_thumb.jpg

 

In real world usage, it can be used to prevent post-processing effects from washing out your colors. However, unless you are using a post-processing effect that darkens the whole screen, even bright pixels, you're not going to see any difference. This is where the iris adjustment shader comes in. Most effects actually brighten the screen, but iris adjustment is one that uniformly dims the screen, regardless of pixel brightness. That's why this effect is closely associated with HDR.

Texture Formats

When rendering to floating-point textures, you might think that would increase memory usage a lot. We do have the option to render to 32-bit floating point RGBA textures. However, we also have half-float 16-bit textures, which will probably produce the same results in most cases. But wait, we don't actually need the alpha channel at this stage in the rendering pipeline, and there's a compressed float format available called GL_R11F_G11F_B10F. This eliminates the alpha channel and uses the extra bits to add precision to the color, packing a floating point RGB image into the same space as an 8-bit RGBA image. I just tested, and it even works on Intel integrated graphics! It is likely that in the future this will be the default setting, and HDR will always be enabled by default, at no cost. To start with I am going to make it an explicit option until we figure out how well it works on all hardware, and whether we need a higher-resolution RGBA16F format.

 

You will have access to this feature in the next beta build.

  • Upvote 6
 Share

5 Comments


Recommended Comments

This works fairly well, your average lum detector is better than mine, but I had adjust the alpha adjustment down to avoid flickering scene.

 

I think this is perfect:

 

context:SetColor(1,1,1,0.02 / Time:GetSpeed())

 

to


context:SetColor(1,1,1,0.005 / Time:GetSpeed())

Link to comment

Also isris only work if it's the first pp-shader, ex. having ssao.shader before seems to break it, maybe alpha is more than 1 when starting iris adjustments writing to alpha channel?

Link to comment

One interesting thing is you have to cut off output in the lighting shaders that are less than zero, because the outputted floating point value can darken the screen.

Link to comment

Ooooooh, that's one feature I've been eagerly waiting for :) Really happy that you're improving the graphics of Leadwerks again. Question: I'd be interested to play around with several tone mapping operators (I guess similar to what the "iris adjustment" does) - will the iris adjustment be a simple post processing effect attachment to the camera (in which case it could be easily replaced)?

Link to comment

It's a script file you can attach to the camera. It creates some buffers to downsample the screen down to a 2x2 texture, then does a texture lookup right in the middle with linear filtering, and uses that brightness to adjust the coloring. Honestly I am amazed I was able to develop that in Leadwerks 2 back when testing a shader meant restarting the editor.

Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...