HDR Rendering (Average Luminance)

Started by
30 comments, last by Medo Mex 8 years, 3 months ago
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?
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.

Adam Miles - Principal Software Development Engineer - Microsoft Xbox Advanced Technology Group

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.

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.

Adam Miles - Principal Software Development Engineer - Microsoft Xbox Advanced Technology Group

@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;
}

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.

Adam Miles - Principal Software Development Engineer - Microsoft Xbox Advanced Technology Group

@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?

- 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

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

@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?

That's right, yes.

Adam Miles - Principal Software Development Engineer - Microsoft Xbox Advanced Technology Group

This topic is closed to new replies.

Advertisement