/* trw.h: Tumblin/Rushmeier/Ward/Ferwerda tone maps (Jan Prikryl) */ #include #include #include "trwf.h" #include "statistics.h" #include "error.h" #include "rgb.h" #include "color.h" #include "cie.h" #include "camera.h" /* REFERENCES: J. Tumblin, H.E. Rushmeier. Tone Reproduction for Realistic Images, IEEE Computer Graphics and Applications, 13:6, 1993, pp. 42-48. G. Ward. A Contrast-Based Scalefactor for Luminance Display, Graphics Gems IV, Academic Press, 1994, pp. 415-421. J. Tumblin, J.K. Hodgins, B.K. Guenter. Two Methods for Display of High Contrast Images, ACM Transactions on Graphics, 18:1, 1999, pp. 56-94. J.A. Ferwerda, S.N. Pattanaik, P. Shirley, D. Greenberg. A Model of Visual Adaptation for Realistic Image Synthesis, SIGGRAPH 1996, pp. 249-258. */ /* #define VERBOSE */ /* Precomputed parameters for different tone mapping methods */ static RGB f_sf = {0.062, 0.608, 0.330}; static float f_msf, f_pm_comp, f_pm_disp, f_sm_comp, f_sm_disp; static float invcmax, lrwm_comp, lrwm_disp, lrwexponent; static float m_comp, m_disp; static float g, r_comp, r_disp; /* Environment parameters */ static float _lwa; static float _ldaTumb, _ldaWard; /* --------------------------------------------------------------------------- PRIVATE FUNCTIONS ------------------------------------------------------------------------- */ static float _stevensGamma(float lum) { if (lum > 100.0) return 2.655; else return 1.855 + 0.4 * log10(lum + 2.3e-5); } static float _photopicOperator(float logLa) { float r; if (logLa <= -2.6) r = -0.72; else if (logLa >= 1.9) r = logLa - 1.255; else r = pow(0.249*logLa+0.65, 2.7) - 0.72; return pow(10.0, r); } static float _scotopicOperator(float logLa) { float r; if (logLa <= -3.94) r = -2.86; else if (logLa >= -1.44) r = logLa - 0.395; else r = pow(0.405*logLa+1.6, 2.18) - 2.86; return pow(10.0, r); } static float _mesopicScaleFactor(float logLwa) { if (logLwa < -2.5) return 1.0; else if (logLwa > 0.8) return 0.0; else return (0.8-logLwa)/3.3; } static void Defaults(void) { } static void Init(void) { float lwa = _lwa = tmopts.lwa; float ldmax = tmopts.ldm; float cmax = tmopts.cmax; float alpharw,betarw,alphad,betad; float gwd; /* Tumblin & Rushmeier */ _ldaTumb = ldmax/sqrt(cmax); #ifdef VERBOSE printf(" lda = %f (TMO_TMBLRUSH)\n",_ldaTumb); #endif { float l10 = log10(TMO_CANDELA_LAMBERT(lwa)); alpharw = 0.4 *l10 + 2.92; betarw = -0.4*(l10*l10) - 2.584*l10 + 2.0208; } { float l10 = log10(TMO_CANDELA_LAMBERT(_ldaTumb)); alphad = 0.4 *l10 + 2.92; betad = -0.4*(l10*l10) - 2.584*l10 + 2.0208; } lrwexponent = alpharw/alphad; lrwm_comp = pow(10.0, (betarw-betad)/alphad); lrwm_disp = lrwm_comp/(TMO_CANDELA_LAMBERT(ldmax)); invcmax = 1.0/cmax; #ifdef VERBOSE printf(" (Brw = %f)\n", pow(10.0, betarw) * pow(TMO_CANDELA_LAMBERT(lwa), alpharw)); printf(" (Bd = %f)\n", pow(10.0, betad) * pow(TMO_CANDELA_LAMBERT(_ldaTumb), alphad)); #endif /* Ward */ _ldaWard = ldmax/2.0; { double p1 = pow(_ldaWard,0.4); double p2 = pow(lwa,0.4); double p3 = (1.219+p1)/(1.219+p2); m_comp = pow(p3, 2.5); } m_disp = m_comp/ldmax; #ifdef VERBOSE printf(" lda = %f (TMO_WARD)\n",_ldaWard); printf(" m_d = %g (TMO_WARD)\n",m_disp); printf(" lwa = %f\n ldmax = %f\n cmax = %f\n", lwa,ldmax,cmax); printf(" m_comp = %g, m_disp = %g (Ward)\n", m_comp, m_disp); #endif /* Ferwerda */ f_msf = _mesopicScaleFactor(log10(lwa)); f_sm_comp = _scotopicOperator(log10(_ldaWard)) / _scotopicOperator(log10(lwa)); f_pm_comp = _photopicOperator(log10(_ldaWard)) / _photopicOperator(log10(lwa)); f_sm_disp = f_sm_comp/ldmax; f_pm_disp = f_pm_comp/ldmax; /* Revised Tumblin & Rushmeier */ g = _stevensGamma(lwa) / _stevensGamma(_ldaTumb); gwd = _stevensGamma(lwa) / (1.855 + 0.4*log(_ldaTumb)); r_comp = pow(sqrt(cmax), gwd-1) * _ldaTumb; r_disp = r_comp/ldmax; } static void Terminate(void) { } static COLOR TR_ScaleForComputations(COLOR radiance) { float rwl, scale; rwl = ColorLuminance(radiance); if (rwl > 0.0) { float m = TMO_LAMBERT_CANDELA( (pow(TMO_CANDELA_LAMBERT(rwl),lrwexponent)*lrwm_comp)); scale = m > 0.0 ? m/rwl : 0.0; } else scale = 0.0; COLORSCALE(scale, radiance, radiance); return radiance; } static COLOR TR_ScaleForDisplay(COLOR radiance) { float rwl, scale, eff; rwl = M_PI * ColorLuminance(radiance); GetLuminousEfficacy(&eff); COLORSCALE(eff * M_PI, radiance, radiance); if (rwl > 0.0) { float m = (pow(TMO_CANDELA_LAMBERT(rwl),lrwexponent)*lrwm_disp-invcmax); scale = m > 0.0 ? m/rwl : 0.0; } else scale = 0.0; COLORSCALE(scale, radiance, radiance); return radiance; } static float TR_ReverseScaleForComputations(float dl) { if (dl > 0.0) return pow(dl*3.14e-4/lrwm_comp,1.0/lrwexponent) / (3.14e-4 * dl); else return 0.0; } static float TR_ReverseScaleForDisplay(float dl) { Fatal(-1, "TR_ReverseScaleForDisplay", "Not yet implemented"); return -1.0; } TONEMAP TM_TumblinRushmeier = { "Tumblin/Rushmeier's Mapping", "TumblinRushmeier", "tmoTmblRushButton", 3, Defaults, (void (*)(int *, char **))NULL, (void (*)(FILE *))NULL, Init, Terminate, TR_ScaleForComputations, TR_ScaleForDisplay, TR_ReverseScaleForComputations, (void (*)(void *))NULL, (void (*)(void *))NULL, (void (*)(void))NULL, (void (*)(void))NULL }; static COLOR Ward_ScaleForComputations(COLOR radiance) { COLORSCALE(m_comp, radiance, radiance); return radiance; } static COLOR Ward_ScaleForDisplay(COLOR radiance) { float eff; GetLuminousEfficacy(&eff); COLORSCALE(eff * m_disp, radiance, radiance); return radiance; } static float Ward_ReverseScaleForComputations(float dl) { return 1.0/m_comp; } static float Ward_ReverseScaleForDisplay(float dl) { Fatal(-1, "Ward_ReverseScaleForDisplay", "Not yet implemented"); return -1.0; } TONEMAP TM_Ward = { "Ward's Mapping", "Ward", "tmoWardButton", 3, Defaults, (void (*)(int *, char **))NULL, (void (*)(FILE *))NULL, Init, Terminate, Ward_ScaleForComputations, Ward_ScaleForDisplay, Ward_ReverseScaleForComputations, (void (*)(void *))NULL, (void (*)(void *))NULL, (void (*)(void))NULL, (void (*)(void))NULL }; static COLOR RevisedTR_ScaleForComputations(COLOR radiance) { float rwl, scale; rwl = ColorLuminance(radiance); if (rwl > 0.0) scale = r_comp * pow(rwl/_lwa, g) / rwl; else scale = 0.0; COLORSCALE(scale, radiance, radiance); return radiance; } static COLOR RevisedTR_ScaleForDisplay(COLOR radiance) { float rwl, scale, eff; rwl = M_PI * ColorLuminance(radiance); GetLuminousEfficacy(&eff); COLORSCALE(eff * M_PI, radiance, radiance); if (rwl > 0.0) scale = r_disp * pow(rwl/_lwa, g) / rwl; else scale = 0.0; COLORSCALE(scale, radiance, radiance); return radiance; } static float RevisedTR_ReverseScaleForComputations(float dl) { if (dl > 0.0) return _lwa * pow(dl/r_comp, 1.0/g) / dl; else return 0.0; } static float RevisedTR_ReverseScaleForDisplay(float dl) { Fatal(-1, "RevisedTR_ReverseScaleForDisplay", "Not yet implemented"); return -1.0; } TONEMAP TM_RevisedTumblinRushmeier = { "Revised Tumblin/Rushmeier's Mapping", "RevisedTR", "tmoRevisedTmblRushButton", 3, Defaults, (void (*)(int *, char **))NULL, (void (*)(FILE *))NULL, Init, Terminate, RevisedTR_ScaleForComputations, RevisedTR_ScaleForDisplay, RevisedTR_ReverseScaleForComputations, (void (*)(void *))NULL, (void (*)(void *))NULL, (void (*)(void))NULL, (void (*)(void))NULL }; static COLOR Ferwerda_ScaleForComputations(COLOR radiance) { RGB p; float sl, eff; /* Convert to photometric values */ GetLuminousEfficacy(&eff); COLORSCALE(eff, radiance, radiance); /* Compute the scotopic grayscale shift */ ColorToRGB(radiance,&p); sl = f_sm_comp * f_msf * (p.r*f_sf.r + p.g*f_sf.g + p.b*f_sf.b); /* Scale the photopic luminance */ COLORSCALE(f_pm_comp, radiance, radiance); /* Eventually, offset by the scotopic luminance */ if (sl > 0.0) COLORADDCONSTANT(radiance, sl, radiance); return radiance; } static COLOR Ferwerda_ScaleForDisplay(COLOR radiance) { RGB p; float sl, eff; /* Convert to photometric values */ GetLuminousEfficacy(&eff); COLORSCALE(eff, radiance, radiance); /* Compute the scotopic grayscale shift */ ColorToRGB(radiance,&p); sl = f_sm_disp * f_msf * (p.r*f_sf.r + p.g*f_sf.g + p.b*f_sf.b); /* Scale the photopic luminance */ COLORSCALE(f_pm_disp, radiance, radiance); /* Eventually, offset by the scotopic luminance */ if (sl > 0.0) COLORADDCONSTANT(radiance, sl, radiance); return radiance; } static float Ferwerda_ReverseScaleForComputations(float dl) { Fatal(-1, "Ferwerda_ReverseScaleForComputations", "Not yet implemented"); return -1.0; } static float Ferwerda_ReverseScaleForDisplay(float dl) { Fatal(-1, "Ferwerda_ReverseScaleForDisplay", "Not yet implemented"); return -1.0; } TONEMAP TM_Ferwerda = { "Partial Ferwerda's Mapping", "Ferwerda", "tmoFerwerdaButton", 3, Defaults, (void (*)(int *, char **))NULL, (void (*)(FILE *))NULL, Init, Terminate, Ferwerda_ScaleForComputations, Ferwerda_ScaleForDisplay, Ferwerda_ReverseScaleForComputations, (void (*)(void *))NULL, (void (*)(void *))NULL, (void (*)(void))NULL, (void (*)(void))NULL };