Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement adaptive exposure #1559

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

VReaperV
Copy link
Contributor

Requires #1550 and #1558
Implements #29

This builds up on #1550 by adding automatic exposure based on scene brightness.

When r_tonemapAdaptiveExposure is enabled, a compute shader gets the geometric mean of luminance before tonemapping. Then in the tonemapper this value is mapped to a curve with coefficients chosen to try to avoid sudden changes and too high/too low exposure values, and multiplied with the HDR colour.

An example of this in practice: https://users.unvanquished.net/~reaper/adaptiveLighting/adaptiveLightingGlobal.mp4

Also moved the material stuff from common_cp to material_cp. This wasn't an issue prior because only the material system used compute shaders, but now this would result in shaders failing to compile if r_materialSystem is disabled (and using macros would result in unnecessary shader re-compilations).

@VReaperV VReaperV added T-Improvement Improvement for an existing feature A-Renderer T-Cleanup T-Feature-Request Proposed new feature labels Feb 21, 2025
@VReaperV
Copy link
Contributor Author

I'll see if I can add a non-compute path later. The compute one can also easily give histogram information for Unvanquished/Unvanquished#221.

@VReaperV VReaperV linked an issue Feb 21, 2025 that may be closed by this pull request
@VReaperV VReaperV force-pushed the adaptive-exposure branch 2 times, most recently from b1ef5f8 to b7277fe Compare February 23, 2025 13:05
@VReaperV
Copy link
Contributor Author

I've put some of these changes into #1563 to make this pr lighter.

@slipher
Copy link
Member

slipher commented Mar 3, 2025

That's a good idea. I just tested and it doesn't seem to work as expected: there are large, sudden exposure changes despite very little change in the scene. For example:

unvanquished_2025-03-03_020134_000

unvanquished_2025-03-03_020132_000

@VReaperV
Copy link
Contributor Author

VReaperV commented Mar 3, 2025

Hmm, I might've broken something with the more recent push, the changes were pretty mild when I was testing it.

@VReaperV VReaperV force-pushed the adaptive-exposure branch from b7277fe to 01ab8df Compare March 10, 2025 18:44
@VReaperV
Copy link
Contributor Author

Rebased + fixed some issues. That should hopefully fix the drastic exposure changes as seen on the above screenshot too.

@VReaperV VReaperV force-pushed the adaptive-exposure branch from 01ab8df to d7b2a06 Compare March 11, 2025 05:58
@sweet235
Copy link
Contributor

LGTM

@VReaperV VReaperV force-pushed the adaptive-exposure branch from d7b2a06 to 90becba Compare March 15, 2025 06:05
@slipher
Copy link
Member

slipher commented Mar 15, 2025

I found another buggy place with huge exposure changes between spots that look about the same in mxl-school.

unvanquished_2025-03-15_013605_000
unvanquished_2025-03-15_013607_000

Copy link
Member

@slipher slipher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With my laptop's Nvidia card, taking a screenshot goes from no noticeable delay to a 2-second delay when adaptive exposure is enabled.

Any plans to smooth out the exposure changes over time, like how the eye adapts to light maybe? For now the exposure variation seems too large and rapid to be really usable; all world surfaces are flickering when you move.

Doors feel kind of buggy in general. There is often a bright flash when the door is opening. But maybe that's just the same thing as the preceding paragraph.

#if defined(HAVE_ARB_explicit_uniform_location) && defined(HAVE_ARB_shader_atomic_counters)
if( u_TonemapAdaptiveExposure ) {
const float l = GetAverageLuminance( luminanceU ) - 8;
color.rgb *= clamp( 0.18f / exp2( l * 0.8f + 0.1f ), 0.0f, 2.0f );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably the lower limit shouldn't be allowed to go 0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It won't, the lowest it can go is ~0.0016.

@VReaperV
Copy link
Contributor Author

I found another buggy place with huge exposure changes between spots that look about the same in mxl-school.

I miscalculated the max value for luminosity sum, so it does overflow sometimes. Will add a fix later.

@slipher
Copy link
Member

slipher commented Mar 15, 2025

Also the instant exposure adjustments mess up other effects that are supposed to cause rapid changes in brightness. The flickering of the firelight in metro-b1-2 alien base is almost not visible at all, being canceled out by exposure changes. When a human weapon is fired, the whole scene perversely darkens due to the luminance of the muzzle flash.

@VReaperV
Copy link
Contributor Author

With my laptop's Nvidia card, taking a screenshot goes from no noticeable delay to a 2-second delay when adaptive exposure is enabled.

Hmm, it might be best to improve the screenshotting in some way. I don't think there's much that can be done with this from the adaptive exposure side.

Any plans to smooth out the exposure changes over time, like how the eye adapts to light maybe? For now the exposure variation seems too large and rapid to be really usable; all world surfaces are flickering when you move.

Yeah, I plan to add some exponential smoothing there.

Doors feel kind of buggy in general. There is often a bright flash when the door is opening. But maybe that's just the same thing as the preceding paragraph.

It might be due to the overflow too.

Also the instant exposure adjustments mess up other effects that are supposed to cause rapid changes in brightness. The flickering of the firelight in metro-b1-2 alien base is almost not visible at all, being canceled out by exposure changes. When a human weapon is fired, the whole scene perversely darkens due to the luminance of the muzzle flash.

Yeah, smoothing it over time should make it better, maybe some value tweaking too.

@VReaperV VReaperV force-pushed the adaptive-exposure branch 2 times, most recently from c5cb774 to fb62668 Compare March 16, 2025 19:38
@VReaperV
Copy link
Contributor Author

I found another buggy place with huge exposure changes between spots that look about the same in mxl-school.

unvanquished_2025-03-15_013605_000 unvanquished_2025-03-15_013607_000

This should be fixed now.

@VReaperV VReaperV force-pushed the adaptive-exposure branch 2 times, most recently from 41564ef to f251a5e Compare March 16, 2025 20:07
@illwieckz
Copy link
Member

That looks nice but yes the feature should remain disabled by default until some delay / average on some time is implemented, because of the flicking when moving.

@VReaperV VReaperV force-pushed the adaptive-exposure branch from f251a5e to d1f2c9c Compare March 17, 2025 23:28
GLUniform4f( shader, "u_TonemapParms2", true ) {
}

void SetUniform_TonemapParms2( vec4_t tonemapParms2 ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could use a more readable interface. Arguments with named float(s) instead of vec4

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps so. I'd just made it as an "extension" to u_TonemapParms that was already there.

gl_luminanceReductionShader->SetUniform_ViewWidth( width );
gl_luminanceReductionShader->SetUniform_ViewHeight( height );
vec4_t parms { log2f( r_toneMappingHDRMax.Get() ) };
parms[1] = UINT32_MAX / ( width * height * ( uint32_t( parms[0] ) + 8 ) );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is UINT32_MAX used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is the maximum value that can be held in an atomic_uint, so it ensures that there will be no overflow.

@VReaperV VReaperV force-pushed the adaptive-exposure branch from 71adf84 to 8e25a56 Compare March 22, 2025 07:19
Add a compute shader that will compute the geometric mean of scene luminance, then map it to an exposure curve in the `cameraEffects` shader. This is controlled by `r_tonemapAdaptiveExposure`.
@VReaperV VReaperV force-pushed the adaptive-exposure branch from 682037b to 86e7478 Compare March 30, 2025 20:09
Copy link
Member

@slipher slipher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get a GLSL compile error if I do set r_arb_shader_atomic_counter_ops 0.

#if defined(SUBGROUP_ATOMIC)
const float luminanceSum = subgroupInclusiveAdd( luminance );

if( subgroupElect() ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mixed spaces and tabs here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Renderer T-Cleanup T-Feature-Request Proposed new feature T-Improvement Improvement for an existing feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

adaptative lighting
4 participants