ACES and Substance Painter

I’d like to make a LUT to use in Substance Painter to work with ACES. I would build the LUT in Nuke using the linear_to_linear EXR file provided by Substance (which is a CMS test pattern). Could someone talk be through the steps involved in doing this in Nuke?

The simplistic version could be as follows:

set cut_paste_input [stack 0]
version 10.5 v2
Read {
inputs 0
file /Users/kelsolaar/Downloads/linear_to_linear.exr
format "2048 128 0 0 2048 128 1 "
origset true
name Read1
selected true
xpos -33
ypos -103
OCIOColorSpace {
in_colorspace "Utility - Linear - sRGB"
out_colorspace "Output - sRGB"
name OCIOColorSpace1
selected true
xpos -33
ypos -13
Write {
file /Users/kelsolaar/Downloads/sRGB_Output_sRGB.exr
file_type exr
name Write1
selected true
xpos -33
ypos 11

I’m at home so cannot test but this should work, keep in mind that this is naive and will clamp highlights, depending how much time you spend in Substance Painter, it might become an issue. Brian Leleux made a version that is based on Substance Painter Log and should preserve them better: and then there is another variant by Jose Linares:



I made my LUT with OCIO in Nuke’s node graph based on a suggestion in an older thread by Nick(I think?) to pre-transform the LUT. Otherwise it will end up clipped as Thomas stated. It requires Painter’s Log tonemapper to be enabled for the LUT to function correctly.

Mine was geared towards improving consistency between texturing in Painter and viewing in real-time engines, but I believe Jose’s will be better suited for VFX work where the source images need to be converted.

Hello guys,

I think it was this thread : ACEScg for Animation feature and further questions

The Nuke code Thomas has provided should do the trick. We have been using a similar process at ENSI for the last year. Last news I got from Allegorithmic was OCIO for Substance Designer in January 2020 and Substance Painter a bit later in the year. :wink:


For your Nuke script should the color space for the Read and Write nodes be set to ACEScg or raw?


I tried your LUT in Substance, and it seems to work great. The only issue I have is that the material view has the LUT applied, but the Base Color does not. Wondering if there’s a way to get that working? Would Jose’s solution which uses a combination of a view LUT and a filter to transform the image address that? I guess it would need to have two filters applied, one to bring the image into ACEScg and another to bring it into sRGB(ACES) for viewing the Base Color.

Really do wish Substance worked with OCIO (and also could paint across UDIMS).

Whether it is ACEScg or Raw is the same in that case, it results in the image being read without performing any conversions because the working space of the ACES OCIO config is ACEScg. The OCIOColorSpace node assumes that you were using sRGB textures in Substance Painter, if you are using ACEScg one, then you must change the input accordingly!



Unfortunately, the channels being shown in Linear is just Painter’s default behavior at the moment. It would be nice to toggle gamma correction or tonemapping for each channel though. Jose’s wouldn’t necessarily fix that particular issue since I believe his Filter is only converting the textures to ACEScg without the final ODT/RRT, which would be done through the LUT. So you’d see a difference, but probably not what you’re looking for

It can be done with the ACEScg filter from PBR+ACES. It has an ODT (Preview) setting at texture level. The problem is that the viewer applies an sRGB gamma so you have to counteract that with a prelinearization. I think this pretransform deviates colors a bit so a slight saturation fix gives a practical match to a proper OCIO workflow in Nuke for instance.
I didn’t want to bake this “fix” process into the filter so everything is visible out there and not assumed.

I recorded a short video:

Yes, OCIO and UDIMs are the most anticipated features, hope they implement them soon.

By the way I had issues logging in with the Google account, had to link my Github.

Thanks Jose,

What is the process for creating a filter in Substance? Can the LUT files from OCIO be used in the process as they can in Nuke when making a LUT exr for the view transform in Substance? Can Painter read in a universal format (like .cube or .exr) for a filter or does it need to be in a Substance internal format made in Designer?

You have to publish the filter from Designer. I think it might be possible to make a LUT parser in Designer but it’s not the workflow I followed, I used an approximation that works good enough.
Probably since OCIO support has already been announced for Substance I don’t think it’s worth the trouble. Still my workaround implementation for the “Output - sRGB” as IDT is and will be valid if you don’t want to mess with gamma, camera response curves or HDR values, despite not everybody agreeing with it.

Was OCIO support officially announced? It’s been “coming” since 2015, so unless it’s been officially announced for a near-future update, I wouldn’t bank on it if your workflow needs something now.

@bleleux you’re definitely right, nothing official. But we are supposed to beta test in December… Fingers crossed!

I made the Substance EXR LUT using Thomas’ Nuke script above. I tried adding in log conversions to this as others have discussed, and also tried it without converting to log. My experience was that none of the log flavors (I tried a ton) quite matched, including the one from Brian (which has a very slight greenish tint to it). However the one without log seems to match perfectly.

I know that typically one needs to do a LUT in LOG space because it needs to work in the 0-1 range. However in this case I am not writing out a typical LUT file like .cube but rather writing to an EXR file which can go above a 0-1 range.

So it would appear that in the particular case of Substance Painter, which uses an EXR file for a LUT, it works better to have no LOG conversion, and indeed none of the (exr) LUTs that come with Substance Painter appear to be in LOG.

I just wanted to confirm the validity of this approach with those more scientifically minded than I just in case I’m missing something.

How bout it science?

I don’t think I’ve ever noticed a green tint. Is this noticeable in Painter directly or when comparing to another renderer? Just to be sure, your Painter camera settings are clear of everything but the Log tonemapper, since the default setup has some things enabled iirc. The biggest issues I’ve seen in my primary renderer(UE4), aside from shading differences, is UE4 also includes a few adjustments to fake a wide gamut and to fix the “electric blue” issue. No obvious color tint that I’m seeing though

The stock linear EXR provided by Allegorithmic is already clamped between 0-1. You can see this with the stock Log LUTs too. There’s this older thread where my original LUT was shared, before I did the pre-transform and Nick provides a lot of helpful information ACEScg for Animation feature and further questions

Thanks @bleleux I’ll give that a read. FYI I was comparing Substance with a render in Maya and noticed blues in Substance had a slight teal hue compared to Maya.

Out of curiosity, what linLog function are you doing in Nuke for your LUT?

It was a Log2Lin function with inverted default values. The Painter tonemapper converts the linear image before the LUT, so I had to do the inverse in the LUT. This is something I think could be done better in my LUT for accuracy’s sake.

I’m a Nuke noob and used the OCIODisplay node which looked like it did the color transform and ODT, but I don’t know if it does the chromatic adaptation as well? That could be where some of the colors differ, or the fact that I used the ACEScg colorspace.

So to clarify, you did:
Lin to log
Utility-linear-sRGB to Output-sRGB
log to Lin

Hey everyone,

just wondering if anyone who actually made the log tone mapper workflow work in Substance Painter could share a screenshot of their Nuke setup ?

I am not able to invert the log tonemapper in my LUT generation. It is either too dark or washed out for some reason. @bleleux @Derek @Dogway1

As you guys are well aware, without the log tone mapper inverted, highlights will clamp. I’d like to get rid of that… I have tried lots of combination of log2lin/lin2log in Nuke but none did the trick…

I’ll try again tomorrow with a fresh head.
Many thanks !

Hey @ChrisBrejon!
It’s a bit un-intuitive: to create a log shaper pre-lut on your lut image, you need to first apply the inverse log transform, then apply the color transform (your OCIODisplay in this case).

So your tree might look something like this:

I usually think about it like this: How do you get from the colorspace you will be applying the lut image in (Painter Log) to the colorspace that the color transform is applied in (ACEScg Linear)? You need a Painter Log to ACEScg Linear colorspace transformation. There are no more transformations to be done because once the Output Transform is applied you are where you need to be.

Hope that makes sense.
Anyway, I was poking around curiously at this same problem a few months ago. I’ll share what I came up with in the hopes it might help you, though I’m not convinced it is a perfect solution!

As far as I can tell, the Substance Painter tonemapping log curve is undocumented. I wasn’t able to get a perfect match with the usual suspects (simple log function, log2lin, cineon, acescct etc).

Without the math of that curve, or a good approximation of it, you obviously can’t do a forward or an inverse transform to get into or out of painter log. So I tried to reverse engineer the painter log curve by putting a linear ramp on a sphere, applying the log tonemapping in Painter with a Mapping factor of 64, and analyzing what happened to the displayed pixels. Not exactly elegant but I did manage to get a log curve out of it that at least seems to resemble the proper thing.

The log curve is here as an spi1d, and there is a lut image for the rec709 and p3d65 aces output transforms there as well if you want to test it out.

I’m pretty sure someone on here who is way smarter than me is going to come along with a better solution and an exact match for the log curve, but hey maybe this will help someone.

Let me know how it works!