Skip to content

Commit c07817c

Browse files
committed
Fix(Tonemap): Correct blend logic to preserve HDR detail
The blending operation for the `tonemap_mix` uniform in `postDeferredTonemap.glsl` incorrectly used a prematurely clamped color value as the source for the linear mix target. Specifically, the exposed HDR input color was clamped to the [0, 1] LDR range before being used in the `mix()` function when `tonemap_mix < 1.0`. This premature clamping resulted in the loss of High Dynamic Range (HDR) detail in highlights during the blend operation. As `tonemap_mix` was reduced, instead of smoothly blending towards the linear scene representation, clipped highlights were incorrectly reintroduced. This commit modifies the `toneMap` and `toneMapNoExposure` functions to correct this logic: 1. The original linear input color is preserved before exposure/processing. 2. The appropriate exposure factor is calculated and applied separately. 3. The chosen tone mapping operator is applied to the exposed color, storing the result. 4. The `mix()` function now correctly blends between the appropriately scaled, *unclamped* linear input color and the fully tone-mapped result. 5. The final clamp to the [0, 1] LDR range is applied *after* the blend operation. This change ensures that HDR information is preserved throughout the blending process, resulting in a smoother, more perceptually correct visual transition as `tonemap_mix` is adjusted. While the effect is nuanced, it is noticeable in bright highlights; with the legacy code, these highlights appeared visibly clipped and less intense during the blend, whereas the corrected code allows them to retain their peak brightness and detail more accurately. This makes the `tonemap_mix` control more intuitive, behaving as a true intensity blend for the tone mapping effect without introducing clipping artifacts. The computational cost is negligible.
1 parent be2e7e9 commit c07817c

File tree

1 file changed

+24
-13
lines changed

1 file changed

+24
-13
lines changed

indra/newview/app_settings/shaders/class1/deferred/tonemapUtilF.glsl

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -117,27 +117,34 @@ uniform float exposure;
117117
uniform float tonemap_mix;
118118
uniform int tonemap_type;
119119

120+
120121
vec3 toneMap(vec3 color)
121122
{
122123
#ifndef NO_POST
123-
float exp_scale = texture(exposureMap, vec2(0.5,0.5)).r;
124-
125-
color *= exposure * exp_scale;
124+
vec3 linear_input_color = color;
126125

127-
vec3 clamped_color = clamp(color.rgb, vec3(0.0), vec3(1.0));
126+
float exp_scale = texture(exposureMap, vec2(0.5,0.5)).r;
127+
float final_exposure = exposure * exp_scale;
128+
vec3 exposed_color = color * final_exposure;
128129

130+
vec3 tonemapped_color = exposed_color;
129131
switch(tonemap_type)
130132
{
131133
case 0:
132-
color = PBRNeutralToneMapping(color);
134+
tonemapped_color = PBRNeutralToneMapping(exposed_color);
133135
break;
134136
case 1:
135-
color = toneMapACES_Hill(color);
137+
tonemapped_color = toneMapACES_Hill(exposed_color);
136138
break;
137139
}
138140

139-
// mix tonemapped and linear here to provide adjustment
140-
color = mix(clamped_color, color, tonemap_mix);
141+
vec3 exposed_linear_input = linear_input_color * final_exposure;
142+
color = mix(exposed_linear_input, tonemapped_color, tonemap_mix);
143+
144+
color = clamp(color, 0.0, 1.0);
145+
#else
146+
color *= exposure * texture(exposureMap, vec2(0.5,0.5)).r;
147+
color = clamp(color, 0.0, 1.0);
141148
#endif
142149

143150
return color;
@@ -147,20 +154,24 @@ vec3 toneMap(vec3 color)
147154
vec3 toneMapNoExposure(vec3 color)
148155
{
149156
#ifndef NO_POST
150-
vec3 clamped_color = clamp(color.rgb, vec3(0.0), vec3(1.0));
157+
vec3 linear_input_color = color;
151158

159+
vec3 tonemapped_color = color;
152160
switch(tonemap_type)
153161
{
154162
case 0:
155-
color = PBRNeutralToneMapping(color);
163+
tonemapped_color = PBRNeutralToneMapping(color);
156164
break;
157165
case 1:
158-
color = toneMapACES_Hill(color);
166+
tonemapped_color = toneMapACES_Hill(color);
159167
break;
160168
}
161169

162-
// mix tonemapped and linear here to provide adjustment
163-
color = mix(clamped_color, color, tonemap_mix);
170+
color = mix(linear_input_color, tonemapped_color, tonemap_mix);
171+
172+
color = clamp(color, 0.0, 1.0);
173+
#else
174+
color = clamp(color, 0.0, 1.0);
164175
#endif
165176

166177
return color;

0 commit comments

Comments
 (0)