Jump to content
  • Advertisement
Sign in to follow this  
Medo Mex

HDR Rendering (Average Luminance)

This topic is 989 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

Hey Guys,
 
I'm trying to implement HDR.
 
The first step I'm doing now is to calculate the average luminance (which I read it should be done by down sampling the scene texture to 1x1)
 
I have a function in Pixel Shader "Downscale2x2Luminence" which take the full texture and downsample it 2x2 then return the luminance greyscale: return float4(lum, lum, lum 1.0f).
 
Now, what is the next step in order to get 1x1 texture with the average luminance?
Edited by Medo3337

Share this post


Link to post
Share on other sites
Advertisement

Take the output of your first pass that calculated luminance and wrote a half-resolution luminance image and continue reducing that by 1/2 or 1/4 at a time until the texture reaches 1x1. If you're reducing the luminance texture by half, do a bilinear sample in the middle of a 2x2 quad of pixels and the hardware will average them for you as long as you get the texture coordinate right, so only one texture fetch is required (per pixel) each time you reduce.

 

This isn't the most efficient way of doing it but it'll do the job.

Share this post


Link to post
Share on other sites

do a bilinear sample in the middle of a 2x2 quad of pixels and the hardware will average them for you as long as you get the texture coordinate right

Emphasis is mine. I shall note it gets really tricky to get perfect right. Took me days of RenderDoc debugging. It was always off by some small amount.

Share this post


Link to post
Share on other sites

do a bilinear sample in the middle of a 2x2 quad of pixels and the hardware will average them for you as long as you get the texture coordinate right

Emphasis is mine. I shall note it gets really tricky to get perfect right. Took me days of RenderDoc debugging. It was always off by some small amount.

On D3D9 that was definitely the case as it had the annoying "half pixel offset" built in, but on 10 and 11 it's easy isn't it?

 

Draw a full screen quad with UVs [0,0] -> [1,1] and the centre of the pixel at half resolution falls exactly in the middle of the 2x2 quad in the source texture, no offsets necessary.

Edited by Adam Miles

Share this post


Link to post
Share on other sites

@Adam Miles: I have two questions:

 

In C++, I have one texture 1x1 and the other one is (ScreenWidth / 2 x ScreenHeight / 2)

 

Downscale2x2Luminence will output to (ScreenWidth / 2 x ScreenHeight / 2) texture

 

1. But after that when I keep downsampling over and over again, what texture size should I output to (since the output every time will have different texture size)?

 

2. Do you mean I should do something like the following?

int currrentWidth = ScreenWidth;
int currrentHeight = ScreenHeight;
while(...)
{
       // Code here to downsample to half...

       currrentWidth /= 2;
       currentHeight /= 2;
}

Share this post


Link to post
Share on other sites

Personally (and there's other approaches here) I would downsample not by half in the first step but from whatever your resolution is to something "approximately" half, but also a power of two in each dimension. For example:

 

1920 x 1080
1024 x 512 // Approximately half

512 x 256

256 x 128

128 x 64

64 x 32

32 x 16

16 x 8

8 x 4

4 x 2

2 x 1

1 x 1

You'll need to create either separate textures for each of the resolutions listed, or one texture with a full mip chain generated from 1024 x 512 down to 1x1. You then just iterate through that list writing a 2x2 downsample of each image into the next one until you reach 1x1.

 

It's best to try and avoid hitting an odd number of pixels in the lower resolutions, you don't want to end up at 240 x 135, then 120 x 67 otherwise deciding where to put your texture lookups becomes a lot trickier, so that's why I suggest writing to a 2:1 resolution image in your first step. Your code snippet is essentially correct, yes.

Share this post


Link to post
Share on other sites

@Adam Miles: Here is what I think about (correct me if I'm wrong):

 

- No matter what resolution we have, create a texture with the size 1024 x 512

- Set the view port size to 1024 x 512

- Render to texture size (1024 x 512)

 

Now, the GPU should handle downsampling to 1024x512 for me without even having to do anything in the pixel shader.

 

Take the 1024 x 512 texture and use for() statement in c++ to keep downsampling to half over and over again.

 

Is that correct?

 

If that's correct, that might be a problem if the screen resolution is less than 1024 x 512 (because that will lead to upsample instead of downsample in the first pass), in that case how do I calculate the approximate half from any resolution?

Share this post


Link to post
Share on other sites

- No matter what resolution we have, create a texture with the size 1024 x 512

No.
He said clearly:

1024 x 512 // Approximately half

That means:
1920 ÷ 2 = 960
960 Rounded to Nearest Power-of-2 = 1024.

1080 ÷ 2 = 540
540 Rounded to Nearest Power-of-2 = 512.
 
 

If that's correct, that might be a problem if the screen resolution is less than 1024 x 512 (because that will lead to upsample instead of downsample in the first pass), in that case how do I calculate the approximate half from any resolution?

See above. You never upsample.
 
 

Take the 1024 x 512 texture and use for() statement in c++ to keep downsampling to half over and over again.

Except that you can’t read and write from the same texture, so you either need 2 textures to do this (advanced) or 1 texture for each resolution (simple).


L. Spiro

Share this post


Link to post
Share on other sites

@L. Spiro:

@Adam Miles:

 

Here is what I'm going to do:

 

ScreenWidth = 1920;

ScreenHeight = 1080;

 

1. GetNearestPower2(ScreenWidth / 2, ScreenWidth / 2); // Should return 1024 x 512 in that case

2. Set view port size to the result of step 1, render scene to texture of size from step 1, the GPU will handle downsampling in that case, the pixel shader should just return the luminance of each pixel in greyscale

3. Now we have 1024x512 greyscale luminance texture, Use while() statement in C++ to render to full screen quad (each render should down sample to half), the while statement should only exit when we have 1x1 texture

4. That's it, now we we have 1x1 texture which contains the entire scene luminance

 

Is that correct?

Edited by Medo3337

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!