3 color_operations::{hsv_to_rgb, rgb_to_hsv}, 4 maths::{PI_2, powsafe}, 7import package::tonemapping::lut_bindings::{ 15const LEVEL_MARGIN: f32 = 0.1; 19const LEVEL_MARGIN_DIV: f32 = 0.5 / LEVEL_MARGIN; 21fn sample_current_lut(p: vec3<f32>) -> vec3<f32> { 24@if(TONEMAP_METHOD_AGX) 25 return textureSampleLevel(dt_lut_texture, dt_lut_sampler, p, 0.0).rgb; 26@elif(TONEMAP_METHOD_TONY_MC_MAPFACE) 27 return textureSampleLevel(dt_lut_texture, dt_lut_sampler, p, 0.0).rgb; 28@elif(TONEMAP_METHOD_BLENDER_FILMIC) 29 return textureSampleLevel(dt_lut_texture, dt_lut_sampler, p, 0.0).rgb; 31 return vec3(1.0, 0.0, 1.0); 39fn rgb_to_ycbcr(col: vec3<f32>) -> vec3<f32> { 41 0.2126, 0.7152, 0.0722, 42 -0.1146, -0.3854, 0.5, 48fn tonemap_curve(v: f32) -> f32 { 51 let c = v + v * v + 0.5 * v * v * v; 58fn tonemap_curve3_(v: vec3<f32>) -> vec3<f32> { 59 return vec3(tonemap_curve(v.r), tonemap_curve(v.g), tonemap_curve(v.b)); 62fn somewhat_boring_display_transform(col: vec3<f32>) -> vec3<f32> { 63 var boring_color = col; 64 let ycbcr = rgb_to_ycbcr(boring_color); 66 let bt = tonemap_curve(length(ycbcr.yz) * 2.4); 67 var desat = max((bt - 0.7) * 0.8, 0.0); 70 let desat_col = mix(boring_color.rgb, ycbcr.xxx, desat); 72 let tm_luma = tonemap_curve(ycbcr.x); 73 let tm0 = boring_color.rgb * max(0.0, tm_luma / max(1e-5, tonemapping_luminance(boring_color.rgb))); 74 let final_mult = 0.97; 75 let tm1 = tonemap_curve3_(desat_col); 77 boring_color = mix(tm0, tm1, bt * bt); 79 return boring_color * final_mult; 88const TONY_MC_MAPFACE_LUT_DIMS: f32 = 48.0; 90fn sample_tony_mc_mapface_lut(stimulus: vec3<f32>) -> vec3<f32> { 91 var uv = (stimulus / (stimulus + 1.0)) * (f32(TONY_MC_MAPFACE_LUT_DIMS - 1.0) / f32(TONY_MC_MAPFACE_LUT_DIMS)) + 0.5 / f32(TONY_MC_MAPFACE_LUT_DIMS); 92 return sample_current_lut(saturate(uv)).rgb; 106fn RRTAndODTFit(v: vec3<f32>) -> vec3<f32> { 107 let a = v * (v + 0.0245786) - 0.000090537; 108 let b = v * (0.983729 * v + 0.4329510) + 0.238081; 112fn ACESFitted(color: vec3<f32>) -> vec3<f32> { 113 var fitted_color = color; 116 let rgb_to_rrt = mat3x3<f32>( 117 vec3(0.59719, 0.35458, 0.04823), 118 vec3(0.07600, 0.90834, 0.01566), 119 vec3(0.02840, 0.13383, 0.83777) 123 let odt_to_rgb = mat3x3<f32>( 124 vec3(1.60475, -0.53108, -0.07367), 125 vec3(-0.10208, 1.10813, -0.00605), 126 vec3(-0.00327, -0.07276, 1.07602) 129 fitted_color *= rgb_to_rrt; 132 fitted_color = RRTAndODTFit(fitted_color); 134 fitted_color *= odt_to_rgb; 137 fitted_color = saturate(fitted_color); 155fn saturation(color: vec3<f32>, saturationAmount: f32) -> vec3<f32> { 156 let luma = tonemapping_luminance(color); 157 return mix(vec3(luma), color, vec3(saturationAmount)); 165fn convertOpenDomainToNormalizedLog2_(color: vec3<f32>, minimum_ev: f32, maximum_ev: f32) -> vec3<f32> { 166 let in_midgray = 0.18; 169 var normalized_color = max(vec3(0.0), color); 171 normalized_color = select(normalized_color, 0.00001525878 + normalized_color, normalized_color < vec3<f32>(0.00003051757)); 172 normalized_color = clamp( 173 log2(normalized_color / in_midgray), 177 let total_exposure = maximum_ev - minimum_ev; 179 return (normalized_color - minimum_ev) / total_exposure; 183fn convertNormalizedLog2ToOpenDomain(color: vec3<f32>, minimum_ev: f32, maximum_ev: f32) -> vec3<f32> { 184 var open_color = color; 185 let in_midgray = 0.18; 186 let total_exposure = maximum_ev - minimum_ev; 188 open_color = (open_color * total_exposure) + minimum_ev; 189 open_color = pow(vec3(2.0), open_color); 190 open_color = open_color * in_midgray; 201fn applyAgXLog(Image: vec3<f32>) -> vec3<f32> { 202 var prepared_image = max(vec3(0.0), Image); 203 let r = dot(prepared_image, vec3(0.84247906, 0.0784336, 0.07922375)); 204 let g = dot(prepared_image, vec3(0.04232824, 0.87846864, 0.07916613)); 205 let b = dot(prepared_image, vec3(0.04237565, 0.0784336, 0.87914297)); 206 prepared_image = vec3(r, g, b); 208 prepared_image = convertOpenDomainToNormalizedLog2_(prepared_image, -10.0, 6.5); 210 prepared_image = clamp(prepared_image, vec3(0.0), vec3(1.0)); 211 return prepared_image; 214fn applyLUT3D(Image: vec3<f32>, block_size: f32) -> vec3<f32> { 215 return sample_current_lut(Image * ((block_size - 1.0) / block_size) + 0.5 / block_size).rgb; 222fn sample_blender_filmic_lut(stimulus: vec3<f32>) -> vec3<f32> { 223 let block_size = 64.0; 224 let normalized = saturate(convertOpenDomainToNormalizedLog2_(stimulus, -11.0, 12.0)); 225 return applyLUT3D(normalized, block_size); 230fn tonemapping_reinhard(color: vec3<f32>) -> vec3<f32> { 231 return color / (1.0 + color); 236fn tonemapping_luminance(v: vec3<f32>) -> f32 { 237 return dot(v, vec3<f32>(0.2126, 0.7152, 0.0722)); 240fn tonemapping_change_luminance(c_in: vec3<f32>, l_out: f32) -> vec3<f32> { 241 let l_in = tonemapping_luminance(c_in); 242 return c_in * (l_out / l_in); 245fn tonemapping_reinhard_luminance(color: vec3<f32>) -> vec3<f32> { 246 let l_old = tonemapping_luminance(color); 247 let l_new = l_old / (1.0 + l_old); 248 return tonemapping_change_luminance(color, l_new); 251fn rgb_to_srgb_simple(color: vec3<f32>) -> vec3<f32> { 252 return pow(color, vec3<f32>(1.0 / 2.2)); 258fn tonemapping_pbr_neutral(color_in: vec3<f32>) -> vec3<f32> { 261 const start_compression: f32 = 0.8 - 0.04; 265 const desaturation: f32 = 0.15; 268 let min_channel = min(color_in.r, min(color_in.g, color_in.b)); 272 let offset = select(0.04, min_channel - 6.25 * min_channel * min_channel, min_channel < 0.08); 276 let offset_color = color_in - offset; 280 let max_channel = max(offset_color.r, max(offset_color.g, offset_color.b)); 281 if max_channel < start_compression { 288 let d = 1.0 - start_compression; 291 let new_max_channel = 1.0 - d * d / (max_channel + d - start_compression); 295 let color = offset_color * (new_max_channel / max_channel); 299 let g = 1.0 - 1.0 / (desaturation * (max_channel - new_max_channel) + 1.0); 300 return mix(color, vec3(new_max_channel), g); 305fn screen_space_dither(frag_coord: vec2<f32>) -> vec3<f32> { 306 var dither = vec3<f32>(dot(vec2<f32>(171.0, 231.0), frag_coord)).xxx; 307 dither = fract(dither.rgb / vec3<f32>(103.0, 71.0, 97.0)); 308 return (dither - 0.5) / 255.0; 313fn sectional_color_grading( 315 color_grading: ptr<function, ColorGrading>, 323 let level = (color.r + color.g + color.b) / 3.0; 328 var levels = vec3(0.0); 329 let midtone_range = (*color_grading).midtone_range; 330 if (level < midtone_range.x - LEVEL_MARGIN) { 332 } else if (level < midtone_range.x + LEVEL_MARGIN) { 333 levels.y = ((level - midtone_range.x) * LEVEL_MARGIN_DIV) + 0.5; 334 levels.z = 1.0 - levels.y; 335 } else if (level < midtone_range.y - LEVEL_MARGIN) { 337 } else if (level < midtone_range.y + LEVEL_MARGIN) { 338 levels.z = ((level - midtone_range.y) * LEVEL_MARGIN_DIV) + 0.5; 339 levels.y = 1.0 - levels.z; 345 let contrast = dot(levels, (*color_grading).contrast); 346 let saturation = dot(levels, (*color_grading).saturation); 347 let gamma = dot(levels, (*color_grading).gamma); 348 let gain = dot(levels, (*color_grading).gain); 349 let lift = dot(levels, (*color_grading).lift); 352 let luma = tonemapping_luminance(color); 353 color = luma + saturation * (color - luma); 354 color = 0.5 + (color - 0.5) * contrast; 366 color = powsafe(color * gain + lift, 1.0 / gamma); 369 color = color * powsafe(vec3(2.0), (*color_grading).exposure); 370 return max(color, vec3(0.0)); 373fn tone_mapping(in: vec4<f32>, in_color_grading: ColorGrading) -> vec4<f32> { 374 var color = max(in.rgb, vec3(0.0)); 375 var color_grading = in_color_grading; 380 var hsv = rgb_to_hsv(color); 381 hsv.r = (hsv.r + color_grading.hue) % PI_2; 382 color = hsv_to_rgb(hsv); 389 color = max(color_grading.balance * color, vec3(0.0)); 394@if(SECTIONAL_COLOR_GRADING) { 395 color = sectional_color_grading(color, &color_grading); 399 color = color * powsafe(vec3(2.0), color_grading.exposure); 403@if(TONEMAP_METHOD_LINEAR) { 405} @elif(TONEMAP_METHOD_REINHARD) { 406 color = tonemapping_reinhard(color.rgb); 407} @elif(TONEMAP_METHOD_REINHARD_LUMINANCE) { 408 color = tonemapping_reinhard_luminance(color.rgb); 409} @elif(TONEMAP_METHOD_ACES_FITTED) { 410 color = ACESFitted(color.rgb); 411} @elif(TONEMAP_METHOD_AGX) { 412 color = applyAgXLog(color); 413 color = applyLUT3D(color, 32.0); 414} @elif(TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM) { 415 color = somewhat_boring_display_transform(color.rgb); 416} @elif(TONEMAP_METHOD_TONY_MC_MAPFACE) { 417 color = sample_tony_mc_mapface_lut(color); 418} @elif(TONEMAP_METHOD_BLENDER_FILMIC) { 419 color = sample_blender_filmic_lut(color.rgb); 420} @elif(TONEMAP_METHOD_PBR_NEUTRAL) { 421 color = tonemapping_pbr_neutral(color.rgb); 425 color = saturation(color, color_grading.post_saturation); 427 return vec4(color, in.a); 434fn approximate_inverse_tone_mapping(in: vec4<f32>, color_grading: ColorGrading) -> vec4<f32> { 435 let out = tone_mapping(in, color_grading); 436 let approximate_ratio = length(in.rgb) / length(out.rgb); 437 return vec4(in.rgb * approximate_ratio, in.a);