Mike Day: An efficient and user-friendly tone mapping operator

At Insomniac we’ve been using the well-known Reinhard tone mapping operator for a while now, but have found its parameterization unintuitive, and its curve shape to be lacking the useful ‘toe’ feature. Presented here is an efficiently computed alternative model which has been designed to qualitatively capture the toe and shoulder features while at the same time using intuitive parameters.

Read more: an efficient and user-friendly tone mapping operator.pdf

  • dom

    hey i was wondering if ya could do me a favor please. you know resistance fall of man. your comunity shut down the ranked online servers. think about all those people who played for a great deal of days and cant contiue playing ranked. the game isnt the same without ranked please bring that up to your boss.

  • Ali Ma

    Hi

  • Mike Day

    Benn Herrera of Telltale Games informs me that they have adapted this technique for their own purposes. He writes:

    “Thank you for sharing your work. We needed the ability to specify inverted S curves (toe bulges up, shoulder bulges down) and followed your process but with the original toe/shoulder functions reversed.

    The use case is a cross-process filter which we implemented as tone mapping with separate curves for each channel. Part of the requirement was that the orientation of the toe/shoulder bulges needed to be reversible (usually for the green component)

    In the spirit of sharing back, for inverted S curve:

    // tonemapB = black point
    // tonemapC = input value at which to cross over from toe to shoulder
    // tonemapW = white point
    // tonemapB < tonemapC < tonemapW
    // tonemapS = shoulder strength
    // tonemapT = toe strength
    // tonemapK = ToeFunc( input=tonemapC ) == K == ShoulderFunc( input=tonemapC ) && ToeFunc'( input=tonemapC ) == ShoulderFunc'( input == tonemapC )

    float tonemapK = ((1.0f – tonemapS)*(tonemapC – tonemapB)) /

    ( ((1.0f – tonemapT)*(tonemapW – tonemapC)) + ((1.0f – tonemapS)*(tonemapC – tonemapB)) );

    float toeOut = (tonemapK * (input – tonemapB)) /

    ((tonemapT * input) + ((1 – tonemapT)*tonemapC) – tonemapB);

    float shoulderOut = tonemapK + (( (input – tonemapC) * (1.0f – tonemapK) * (1.0f – tonemapS) ) /

    (tonemapW – ((1 – tonemapS)*tonemapC) – tonemapS*input));

    float out = input < tonemapC ? toeOut : shoulderOut;

    Thanks again,

    -Benn"