Search code examples
c++image-processinggraphicsraytracing

How to convert raw RGB luminance using OCIO


I'm currently working on a raytracer in C++, and am struggling to understand color transforms/spaces. I'm trying to use AGX via OCIO, but I cant find any solid explanations as to how I'm to apply the transform starting from RGB luminance. I am specifically working with raw light outputs from my raytracer. What is the exact process I need to follow here? Relevant code below.

Initialization:

OCIO::ConstConfigRcPtr config = OCIO::Config::CreateFromFile("C:\\...\\AgX-main\\config.ocio");
// The correct arguments for the next line are a mystery to me.
OCIO::ConstProcessorRcPtr processor = config->getProcessor(OCIO::ROLE_RENDERING, OCIO::ROLE_RENDERING);
auto compute = processor->getDefaultCPUProcessor();    

Color processing

XYZ post_process_pixel(XYZ lums) {            //lums is raw light data via RGB
    float w_luminance = luminance(lums);      //get pixel overall luminence
    float log_lum = pow(w_luminance,1.0/2.2); //curb it via gamma 2.2
    lums = lums * log_lum / lums;             //apply curb to color

    float pixel[3] = { lums[0],lums[1],lums[2] };
    compute->applyRGB(pixel);

    //scale to 24 bit rgb
    return XYZ::clamp(XYZ(pixel[0],pixel[1],pixel[2]), 0, 1) * 255;
}

Thanks!


Solution

  • Ok so I've figured out my issue here for anyone else who gets stuck on this. Still not entirely sure what I'm doing, but this gives me decent results using AGX.

    Check the config.ocio file in notepad and look for a section labeled "displays:", under which there's a few display spaces (sRGB, P3, etc.). Under each there's a list of views, which are your transform options (I think). You want to use the "colorspace", In my case "AGX base sRGB".

    You want to use ROLE_SCENE_LINEAR as the origin space. That is the space for raw luminance (I think). The end result is this processor initialization:

    config->getProcessor(OCIO::ROLE_SCENE_LINEAR,"AgX Base sRGB");
    

    From there do not preprocess your luminance.

    XYZ post_process_pixel(XYZ lums) {  //lums is raw light data via RGB
    
        float pixel[3] = { lums[0],lums[1],lums[2] };
        compute->applyRGB(pixel);
    
        return XYZ::clamp(XYZ(pixel[0],pixel[1],pixel[2]), 0, 1) * 255;//scale to 24 bit rgb
    }
    

    This brings me far closer to my expected outputs (comparing against cycles). My result is notably less saturated, so I likely still have a misconfiguration somewhere, but its otherwise very good. I will update this if I find any further improvements.

    Edit: So beyond that, for me at least, to bring my output in line with blender's I needed to do a gamma correction on the output. Simple as:

    float pixel[3] = { lums[0],lums[1],lums[2] };
    compute->applyRGB(pixel);
    lums = XYZ(pixel[0], pixel[1], pixel[2]);
    
    float post_lum = pow(luminance(lums),1/2.2);
    lums = lums * post_lum / luminance(lums);