Automatic exposure control

i’d like to add automatic exposure control to my renderer. afaik i need the average brightness of the scene. so i render the scene to a texture and then blit it to a texture half the size of the preceding one until i arrive at one which is 1x1.

how can i compute the exposure now? in a paper on this topic i found this:

Automatic exposure control aims to adjust the average brightness of the scene. The idea is that the average brightness of the scene should be around 0.5 because the displayable intensity range is from zero to one. If the average brightness is known, the exposure value can be determined with
Exposure = 0.5 / Average_Brightness;
Keep in mind that you wish the exposure to slowly adapt and not change instantaneously. To do this, you could use the following code:
Exposure = lerp(Exposure, 0.5 / Average_Brightness, Exposure_Adjust_Speed);

does this make sense? it gives chaotic results for me, with the screen sometimes being plain white or black. any ideas?

thanks! :slight_smile:

Try to clamp your Average_Brightness within a reasonable range, ie. 0.3-0.7, to avoid extreme results.

yeah that sounds like a good idea. and then? i mean i do not want the exposure to change immediately, but to adjust slowly. do i have to call

Exposure = lerp(Exposure, 0.5 / Average_Brightness, Exposure_Adjust_Speed);

several times, in a loop or so? i’m not really sure how this lerp function is supposed to do that.

No, you need to call it once per frame.

Before this, calculate Average_Brightness for the frame to be rendered. “Exposure” has to be the exposure of the last frame. The result is the exposure of the current frame, that you use to adjust the brightness when rendering the current frame. And you will need it for the next frame, of course.

Exposure_Adjust_Speed has to be in range 0…1, where 1 results in immediate changes and lower values add the delay.


k does this make sense?
i still have to setup a 1x1 texture to save the computed exposure for use in the next frame…

// get scene
vec4 Scene = texture2D( u_Texture0, v_Coordinates );

// average brightness of this frame
vec3 Color = texture2D( u_Texture4, vec2(0.5, 0.5) ).rgb;
float AverageBrightness = clamp( max(max(Color.r, Color.g), Color.b), 0.3, 0.7 );

// exposure of previous frame
float PreviousExposure = texture2D( ... );

// compute exposure
float Exposure = lerp( PreviousExposure , 0.5/AverageBrightness, 0.5 );

// perform tone-mapping
Scene.rgb *= Exposure*( Exposure/u_MaximumBrightness+1.0 )/( Exposure+1.0 );

you could also use the geometry shader to quickly generate a historgram of your scene, extracting min, max and average and then doing a complete color level adjustment. However, maybe this is much more as you need…

ok i tried something else…
i compute the exposure from 9 samples of the current scene:

#define SAMPLE_COUNT 9

static unsigned int Pixels[SAMPLE_COUNT][2] =
	iWidth*0.50, iHeight*0.50,

	iWidth*0.25, iHeight*0.50,
	iWidth*0.75, iHeight*0.50,
	iWidth*0.50, iHeight*0.25,
	iWidth*0.50, iHeight*0.75,

	iWidth*0.25, iHeight*0.25,
	iWidth*0.25, iHeight*0.75,
	iWidth*0.75, iHeight*0.25,
	iWidth*0.75, iHeight*0.75

Vector3D kAveragedSamples;
unsigned char Samples[SAMPLE_COUNT][4];
for( unsigned int i = 0; i < SAMPLE_COUNT; i++ )
	glReadPixels( Pixels[i][0], Pixels[i][1], 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &Samples[i][0] );
	kAveragedSamples += ( Vector3D(Samples[i][0], Samples[i][1], Samples[i][2])/255.0f )/SAMPLE_COUNT;

AverageBrightness = max( max(kAveragedSamples.x, kAveragedSamples.y), kAveragedSamples.z );

fExposure = 0.5/AverageBrightness;
fExposure = fPreviousExposure+( fExposure-fPreviousExposure )*0.02;

fPreviousExposure = fExposure;

then, in the final stage, i multiply the scene by this value:

Scene.rgb *= u_Exposure*( u_Exposure/u_MaximumBrightness+1.0 )/( u_Exposure+1.0 );

it works, but i’m not sure if the average brightness is computed correctly… even for very bright scenes, the value is pretty low (< 0.4) and sometimes a little shift of the camera causes a very different value. this results bright scenes being even brigther, but if i set an upper limit for the exposure, then dark scenes will only be lightened up a bit. any ideas?

Try with:

TargetExposure = 0.5 / Average_Brightness;
Exposure = Exposure + (TargetExposure - Exposure) * 0.1;

Calculate TargetExposure every frame based on histogram or 1x1 mipmap. Then calculate Exposure using above math.

In short, using above formula, Exposure will slowly get close to TargetExposure, but never reach that value. If you have static frame, it will change exposure by 10% every frame.

thanks, but isn’t that exactly what i’m doing already?

yes… right… I didnt read this thread carefully.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.