Sign in to follow this  

HDR tone mapping once again

This topic is 4089 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I've implemented Reinhard's global tone mapping operator, but the results are different from his own implementation. I've even hardcoded average luminance value because it was twice higher in my app (maximum value was also different), but the results are still different. Reinhard's implementation, key = 0.18 key=0.18 My implementation, key = 0.18 key=0.18 My implementation, key = 5.84 key=5.84 Generally in my implementation colors look much more saturated. However I tried to make my code as similar to Reinhard's C code as possible. Here's the shader code:
float4 PS_Reinhard02( VSOutput input ) : COLOR
{
  // Get color of the pixel from the HDR texture
  float4 color = tex2D( hdrTextureSampler, input.TexCoords );
  
  // Convert RGB->ZXY->Yxy
  float3 Yxy = RGB2Yxy(color.rgb);
  
  //----------------------
  // Tone mapping
  //----------------------
  // (Lp) Map average luminance to the middlegrey zone by scaling pixel luminance
  float scaleFactor = 1.0f / (3.0f + 0.00001f); // 3.0f = average luminance
  float Lp = Yxy.r * scaleFactor * exposure;
  // (Ld) Scale all luminance within a displayable range  
  Yxy.r = (Lp * (1.0f + Lp / (whitePoint * whitePoint))) / (1.0f + Lp);  
  
  // Convert Yxy->ZXY->RGB
  color.rgb = Yxy2RGB(Yxy);  
  
  color.a = 1.0f;
  return color;
}

float3 RGB2Yxy(float3 color)
{
  //----------------------
  // RGB -> XYZ conversion
  //----------------------
  const float3x3 RGB2XYZ = {0.5141364f, 0.3238786f,  0.16036376f,
				                    0.265068f,  0.67023428f, 0.06409157f,
				                    0.0241188f, 0.1228178f,  0.84442666f};				                    
  float3 XYZ = mul(RGB2XYZ, color);
  
  //----------------------
  // XYZ -> Yxy conversion
  //----------------------
  float3 Yxy;
  Yxy.r = XYZ.g;                            // copy luminance Y
  Yxy.g = XYZ.r / (XYZ.r + XYZ.g + XYZ.b ); // x = X / (X + Y + Z)
  Yxy.b = XYZ.g / (XYZ.r + XYZ.g + XYZ.b ); // y = Y / (X + Y + Z)
  
  return Yxy;
}

float3 Yxy2RGB(float3 Yxy)
{
  //----------------------
  // Yxy -> XYZ conversion
  //----------------------
  float3 XYZ;
  XYZ.r = (Yxy.r * Yxy.g) / Yxy.b;              // X = Y * x / y
  XYZ.g = Yxy.r;                                // copy luminance Y  
  XYZ.b = (XYZ.r / Yxy.g) - XYZ.r - XYZ.g;      // Z = Y * (1-x-y) / y
    
  //----------------------
  // XYZ -> RGB conversion
  //----------------------
  const float3x3 XYZ2RGB  = { 2.5651f,   -1.1665f,   -0.3986f,
				                     -1.0217f,    1.9777f,    0.0439f,
				                      0.0753f,   -0.2543f,    1.1892f};
  return mul(XYZ2RGB, XYZ);
}

The matrices values are taken from Reinhard's source code. There are only 3 values that are "passed" to pixel shader: color of the pixel, exposure and white point, which are set using sliders in my app. I know that it's hard to tell what can be the problem, but maybe you can figure something out? Maybe it needs gamma correction or something? Thanks in advance

Share this post


Link to post
Share on other sites
I haven't really looked into your code, but I noticed that your CIE XYZ/RGB conversion matrices seem a little weird. They're neither Rec 601, Rec 709, nor the new ITU standard. Where did you get them from ? Or where they the one that Reinhardt himself used ? Been some time since I read his paper.

Regardless of weird matrices and/or a possible bug in your shader code, I have come to the (personal) conclusion that Rheinhardt's operator simply sucks, if the dodging and burning isn't implemented as well. Just as a comparison, you might want to try a simple gamma/exposure operator. It performs surprisingly well under many circumstances.

Share this post


Link to post
Share on other sites
Thanks.

The matrices are taken from Reinhard's source code, but maybe I'll try different ones. However I'm a bit confused with all these RGB versions, I'm not sure which one exactly I'm using.

I've tried converting RGB->Yxy->RGB without any luminance modifications in Yxy color space and the output image was similar to the input image, so (if I didn't mess up anything in setting up the test) the color space conversions should be ok. But it would mean that there's an error in these 3 lines of tone mapping operator...

Just to clarify one thing: by "simple gamma/exposure operator" do you mean something like this (it's from ATI HDR demo):

float4 color = tex2D(originalImageSampler, tc);
color *= fExposureLevel; // Apply simple exposure level
return pow(color, 0.55f); // Apply gamma and return

?

Have you tried implementing local version of Reinhard's operator? I'm trying to do it myself, but being quite new to DirectX and graphics programming in general I find it really hard (it would be really great if, while debugging, I could stop after rendering to a texture and see the result).

Share this post


Link to post
Share on other sites

This topic is 4089 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this