The long-awaited OCIO feature is here, now we gotta find how it works.
It’s there ! After so much time, Substance-Painter finally saw itself getting
a shiny new color-management system with OCIO support. We’re going to dive
deeper inside and see how it works.
The article is divided into two parts.
You got first a theoretical part that will try to explain how
color-management works. This can help you debug issues and just not tweak
settings like a blind machine. This part is not mandatory though. You can
skip straight to the Substance Setup & Workflow section if desired.
We can break the workflow into 4 sections : Input, Workspace,
Display and Output
You transfer data between each of these section. Data that must be
potentially decoded and then encoded, depending on what the section required.
In Substance Painter this data is most of the time pixels, encoded
using the RGB color model.
All of these data-transfers allow me to introduce the most important rule:
you always need to know where you start to know where you are
going.
As an example, in the above diagram, to convert the Input data to the
Workspace data, we need to know how the Input data is encoded (in our
case, which colorspace).
“Where you start” means first, what type of data are you manipulating ?
There are only two types : color and scalar.
It is important to know which one your data belongs to because the scalar
one doesn’t require your data to be color-managed and as such skip a
complex part.
Scalar data has no means to be displayed directly, the data store numbers
that can be used to drive other types of data. We are only interested in the
original value of these numbers and as such this kind of data must never
be altered by color-transformations.
To get to more concrete examples scalar data include but is not limited to:
roughness, normals, masks, displacement, vectors, …
Everything that is not scalar. Values stored are intended to be displayed
directly. These values are always encoded in some colorspace and require to be
decoded properly.
This include but is not limited to : diffuse/albedo/base-color, subsurface
color, specular color, refraction color, every image displayed on the web, …
As Substance is aware if the channel needs to be color-managed, some operations
will be adjusted/skipped. An application of this is the view transform
that will be disabled when viewing a scalar channel.
This notion will be also applied by yourself when needed to specify the
colorspace encoding of a resource (images, alphas, materials, …).
If you import a roughness texture, as it is scalar data you will have to
specify the “colorspace” as “raw”, so no special decoding is applied.
Data that need to be processed, can be anything but in our case it is
pixel data, like an image texture, a brushstroke, a procedural noise, …
If it is scalar, we don’t need to decode it. We must specify that we don’t
want color-transformations by specifying for example the colorspace=”raw”.
If it is color this means that the data has been mandatorily encoded in a given
colorspace. You can hope that this encoding is specified somewhere, like in
the name, in the metadata, … But as color-management is a big mess still in
2021 most of the time we will assume that it’s in sRGB colorspace with
a transfer-function depending on the file format used.
In sp the Input section can be found on the image slot of each layer.
See Input Setup in Sp ..
Everything you create, modify go through it. We define how it is configured
so we can always know “where to go” when transforming an Input.
In sp this is the “Working color space”. In OCIO term it corresponds the
scene_linear role (also the reference one).
Even though sp doesn’t support OCIO role, it read the
scene_linear one to use it as the Working Colorspace.
Once the data has been processed through the Workspace you might want to
preview it. To do so, everybody will be using some kind of hardware display,
usually a computer monitor. This might sound dumb but it is a critical step.
So here we need to convert the Workspace data to Display data, and there is a
lot to do here.
We can see the Workspace as an “open-domain”, where data can
be stored in some fancy colorspaces, reach some very high values, … (it
can also be closed with data being already closer to the Display domain).
We can then see the Display as a “closed-domain”, it except a kind of
particularly encoded signal and has limits clearly defined. Knowing the
source (Workspace) we can define the transformations required to convert it
to the target (Display). This involved at his core, colorspace primaries
conversion (if Workspace colorspace is different from the Display ones),
transfer-function encoding/re-encoding (to convert a linear Workspace to
a Display requiring the sRGB transfer-function), and at a more advanced stage,
a dynamic-range conversion (still if required). This last step is usually
called “tonemapping” where we try to make the open-domain that is the Workspace
fit into the Display closed-domain.
Damn that’s a complicated one, but anyway, what you have to keep in mind is
we are encoding data for a delimited domain defined by the display you are
using.
In sp the Display section is handled by the view-transform dropdown, that
you can find at the top-right of your viewport.
See Display Setup in Sp .
But isn’t the Display the Output ? Yes, it can be, if you are at the end of the
chain. But here in sp, the end of the chain is our exported texture files. The
Display only allow us to have a preview of how they could look.
So here, we will encode the Workspace data, has it is required for the next
pipeline step. Encoding will depend on what you need in the next step and what
container (image format) you chose.
In sp the Output section happens during the textures export process.
See Output Setup in Sp .
To illustrate the theory here is a diagram representing a section of a
potential VFX-pipeline. I hope this will not confuse you more than this topic
already is.
Example of a color-managed pipeline with various colorspace configuration
used for each section.
I voluntary chose different colorspace across departments and sections to
accentuate potential transformations.
It has been chose to work with ACES for the color-management.
Let’s assume all the users working on this pipeline have access to the
same display which is a DCI-P3 calibrated monitor.
~
You made it yay ! Color-science is a complex topic, so don’t worry if you
don’t get everything the first time. You will find additional resources to
continue your exploration at the end of this article.
Color-management is not application-dependent but project-dependant.
As such, you will not find any options in the applications settings but in
project ones.
The first occurrence of these new options can be found on the New project
window. You will find a Color Management sub-menu at the bottom.
You will be offered between two modes Legacy and OpenColorIO.
Legacy corresponds to the pre-release way sp was working with. We will
skip this mode as it is not useful anymore.
But wait, wait … what is OCIO ? Why should I use it ?
OCIO
is a color-management solution developed originally by Sony Picture Imageworks
aiming at enforcing color-management consistency between DCCs.
I recommend having a read at the documentation .
OCIO itself only define standards of utilisation and give you the tools to work
but the core of the system is the OCIO config (a .ocio file).
This is where all the color-transforms and options are defined.
For example, ACES is a color-management system on his own but ship a version
through OCIO.
The main advantage is that OCIO is supported by most software (even if the
implementation wildly differs between each 😬 ) so you could get the same look
through all of your DCCs (in theory).
~
For our convenience sp already ships with 3 OCIO configs :
Substance
ACES 1.0.3
ACES 1.2
You can find them in the sp installation folder like this one :
C:\Program Files\Allegorithmic\Adobe Substance 3D Painter\resources\ocio
Honestly, I don’t know why did they include two ACES versions, only the last
one was needed, but it is awesome to have a default “Substance” config.
Lot of flexibility here. First option is to use the shipped configs.
In my opinion only the Substance config is interesting here.
The 2 ACES ones are the “default” dev configs with the hundred of
colorspaces you will never need. It is better to use a lightweight ACES
config like the one from CAVE academy. (see ACES Workflow
section)
The Substance config will be a good fit if you are using the traditional
sRGB linear workflow and do not wish to use an OCIO config in every DCC.
You will still have enough control to have a proper color-managed workflow.
Let’s now see how you could load a custom OCIO config.
You create a new variable named OCIO with the path to the config.
This variable will be used by ALL software that can read it. (unless
overridden).
This is not a recommended solution as you pollute your environment variable
+ if you decide to switch the config for another one all your previous project
will be broken.
You defined the environment variable in a start-up script.
This is the cleanest way to do it but means you can’t use the Windows shortcut
to start your software :
We use a .bat to configure and launch the software. Here is a basic .bat
that will set the OCIO variable and then launch sp.
set"OCIO=C:\aces_1.1\config.ocio"
start """C:\Program Files\Allegorithmic\Adobe Substance 3D Painter\AdobeSubstance 3D Painter.exe"
This means that to launch Substance you will have to always use this .bat.
No “double-clicking” on file to open them either.
You can have a look on internet at how to pin a .bat to the taskbar .
But this guarantees a very robust software configuration per project.
It corresponds to all the sections below the color-management mode. It allows
to configure how inputs react with the OCIO config, i.e which colorspace is
being assigned by default.
Usually, in other software, this section is configured using the OCIO roles defined in the OCIO configuration.
But currently sp support only its own OCIO roles. Which mean
you might have to manually setup this section to get a correct
result with the auto settings OR make sure the OCIO config you are using
have the OCIO roles supported by Sp.
If you look at the above image, this is how it is supposed to look when picking
the Substance config. As the Substance config have the supported OCIO roles
defined, the parameters are properly configured.
But if you are loading a custom config, make sure these options are properly
configured with the intended colorspaces for each format if you want all the
auto options to work properly. Most of them (except Export ones) can be
changed in context in last resort.
Visit the ACES Workflow section to find how this should be considered if you
are using the ACES config.
~
Since 7.4.2 you can also find a new Standard sRGB color space parameter
which is a very nice add for having UI elements properly managed. If the name
doesn’t looks clear, it correspond to the sRGB - Display encoded
colorspace. From the documentation, it is used :
To convert color set in the hexadecimal field of the color picker.
To save and load color swatches within the color picker.
To be listed as a Display in the color picker list.
This update also adds support for specific OCIO role which are the following :
Check the Substance’s OCIO documentation for more details but make sure your OCIO config have these
fellas set with the corresponding colorspace if you want it to be properly
read in Sp.
Alright, to recap’ everything for a new project you need :
Change the color-management mode to OCIO
Choose the OCIO config (already chosen if env variable set)
Check the OCIO options to have the correct default colorspaces working.
And of course, setting the other parameters related to your texturing.
Now you are good to start the texturing workflow. The workflow will be
divided into the same sections explained in the theoretical part of this
article (see Color-managed Workflow).
The Workspace, in software is actually an “abstract” section. It just
represents the colorspace used as a reference, target or source for every color
transformation. It is defined in the OCIO config and cannot be changed outside
of it.
Visible in the Project’s Color-management section (Using the ACES 1.2 OCIO
config here)
It is just good to know what is the colorspace being used here.
A good first step before working is to make sure the Display part is
properly configured so you don’t start texturing while viewing the wrong
colors. This Display part can be configured using what we usually called a
view-transform menu. In sp, you can find it at the top-right of your
viewport.
What you have to remember is that you need to choose the option
that corresponds to your display. If your display is calibrated to the
Display P3 colorspace (Apple displays), choose the Display P3 option.
But what if I don’t know what my display is calibrated to ?
A safe choice would be to assume you are using an sRGB-like display.
I see some people using Rec.709 instead of sRGB, why ?
sRGB and Rec.709 share the same primaries, so you can use both without
seeing color-shift due to different primaries. What does change is the
transfer function being used. But fasten your seat-belt, here comes the
mess : Rec.709 only defined an OETF
which is intended for camera signal encoding, not data display encoding !
For display encoding with the Rec.709 colorspace, one should use the
BT.1886
standard which can be resumed as a simple 2.4
power-function.
So how to know which one of these two is being used ? Simple, if when
compared to sRGB, the image looks darker, it’s the OETF, if it’s looking
less contrasty, it’s BT.1886.
If you do the test, the Substance config use the OETF (which should not
be used), while the ACES config uses BT.1886.
You didn’t answer my question !? I’m just more confused now !
As written previously, you need to choose the option that corresponds to
your display, so if your display is not calibrated to Rec.709+BT.1886
don’t use it. But some people like the look of it, being less contrasty,
that’s why it’s being chosen. But the display should not be a creative
choice. If you like a less contrasty look, you should apply it in the Look
(see under).
Just to add more confusion, the BT.1886 difference with sRGB can
actually be used as a viewing environment compensation. So it can actually
justify why using Rec709+BT.1886 instead of sRGB.
Anyways, I’m going too far from the subject, and someone already
wrote about this topic, I let you read this mind-blowing article from
Chris Brejon OCIO, Display Transforms and Misconceptions.
If you are using a custom User channel, you will have to manually
specify if the channel is color-managed. (By default they are not)
This is achieved by clicking on the gear icon > Color channel.
Texturing is all about mixing already existing images, with some carefully
crafted paint stroke, and funky procedural resources. All of these, if they
are color-data, have been created and saved with a specific colorspace.
We will need to know and then specify this colorspace to sp so the OCIO
processor can know if it needs conversion to the Workspace colorspace.
In Sp this manipulation takes place, weirdly, on the images slots of each
layer. You will not find any option to specify the colorspace in the shelf.
Updated menu design on versions 7.4.2 +If the menu is not visible, you can toggle it just above.
By default, it is set to auto, which will use the settings specified in
the project color-management menu explained above.
(Substance parameters for OCIO configs).
I recommend always modifying this option to the proper colorspace to be sure
the resource is properly color-managed.
Another option is to have the source colorspace specified in the file name.
That’s in my opinion a bit messy because the colorspace has to be the exact
name used in the config. If 2 configs used a different name, your image will
only work for one. If I take for example a colorspace name used in the ACES
config this could give: bricks_wall_albedo_Utility - Linear - sRGB.exr.
As used as feared by artists. It never react how the artist wants
and looks to be made out of dark magic (at least in Mari 🙃 ).
Did the sp implementation bring any good news ? Let’s see.
Abbreviations used:
tcd : top colorspace dropdown
eds : editable sliders, where you can manually enter your color
components.
A good feature is the little info icon, giving explicit
info on how the widget works. If we have a look at the info message
next to the tcd we can read :
Color mixing space, this is the space in which a color is edited
before being converted into the working color space. It usually
matches the monitor to be easily viewed.
Which means the values in the eds are in the colorspace
specified by the tcd, then they are converted to the working
colorspace. (you can check the result of this conversion just under.)
This is a valid workflow, which allow you to get the same color seen
in the UI on the final textures.
But keep in mind that you have to be careful on the color
you are chosing, the fact that it looks as you want (perceptual)
doesn’t mean the scene-refered values (working colorspace) have a
physically plausible value. For exemple, in an ACES workflow you might want
to make sure that no channel’s value goes up to 1.0, which mean you are
reaching the boundary of the ACEScg gamut which is comparable to a laser’s
color.
What about the actual picker ?
The picker will pick the value at display, so with the view-transform
applied (if not disabled). Consider this as the input, and will convert
from the tcd colorspace to the working colorspace. You then get
back “nearly” the same value (with some math precisions issue ).
What I recommend though, is to change the view-transform to a
“no-operation” colorspace (usually called raw), pick the color, and
re-apply the previous view-transform. This will avoid the
potential colorspace invertibility and imprecisions issues.
Using Filmic config, original value is RGB(1,0.5,0).
Small infographic to resume colorspace transformations.
So I must say that the latest update have greatly improved the color-picking
experience which behave a bit more as you would except. Cool stuff !
Looking forward to next updates to reach the perfect color-picking
experience haha.
There is no direct option to modify the environment image colorspace.
From 7.4.2 + you can find an option in the Display Settings Menu, to change
the environment colorspace being used.
For the other versions you still have the following options:
Modify the default Linear colorspace in the project settings. The
environments maps will use it.
Include the source colorspace in the name of the HDRI. It has to be the
exact same name as defined in the config. Example :
myhdri_ACES - ACEScg.exr. (you can find an example in ACES -
Environment)
When you right-click on a mask you have the option to Export mask as File.
Don’t worry, mask seems to always be considered as scalar and exported
without any treatment.
The Export Textures window didn’t got much new. We doesn’t have any options
to apply a color-transformation at export time in the Window. The only options
are the one available into the project settings.
Basically, integer format should be sRGB display encoded. Floating point format
should use the same working colorspace.
What’s new though is the $colorspace token in the Output Templates tab.
Which is simply replaced by the colorspace defined in the project settings.
(You can have a preview of the file name in the LIST OF EXPORT tab).
I’m personaly not fan of this option as this might introduce special characters
in the file’s name, depending on how the colorspace is named. It is, I think,
a better option to have the texture name without the colorspace, but exported
in a directory with the colorspace name.
For scalar channels, sp will not apply any color-transformation
and consider them using the colorspace raw (no matter the config).
Interstingly, this colorspace raw doesn’t get written into the
$colorspace token as it should. (fixed in 7.4.1)
You could use the one shipped with Substance but I wouldn’t recommend so.
They are the ones with the hundred colorspaces that will just slow you down
when you need to choose one.
Instead, it would be smarter to use a config with only what you need like
the one from CAVE academy.
Then you will need to configure the default colorspaces. Using the Cave config
(which have the same nomenclature as the official ACES ones) here is what I
recommend :
Import settings are the usual stuff, most of the 8bit texture, if not all
are sRGB display encoded files so Utility - sRGB - Texture correspond.
Floating point images like EXRs should always be linear so the alternative
version Utility - Linear - sRGB is the right choice. Remember these options
are just applied by default (with the auto colorspace) but can be
changed anytime.
I choose Utility - sRGB - Texture for Substance materials because
it seems the output is always sRGB display encoded as the screenshot under
show. (colorspace options can be modified on the material anyway).
Now for the output my choice is not the only option. To me, you shouldn’t
export ACEScg 8bit files, that why I re-encode them back to sRGB by using
Utility - sRGB - Texture. The right option is to export EXRs (floating
point images) in the same working colorspace: ACEScg. And don’t worry for
scalar channel they will be handled automatically at export. These options
are the only ones that can’t be modified per-case though; this is the only
place you can change them.
I’m only going to give detailed explanations when something is specific to
ACES. Meanwhile the explanations given in Substance Setup & Workflow still
apply so make sure you properly understood this section.
For every external resource you import, you need to assign the correct
input colorspace if the automatic one doesn’t correspond. The usual rules
for the ACES workflow apply.
There is unfortunately no direct options to change environment’s
colorspace.
From 7.4.2+ you can find an option in the Display Settings Menu, to change
the environment colorspace being used.
Environment follow default colorspace rules. Being floating point image
, they will use the pre-defined Utility - Linear - sRGB colorspace.
So as long as they are sRGB - linear-like encoded, they will be properly
displayed.
If that not the case you can use the mentioned override option, but
in the case you are using an older version here is my previous solution :
But what if I want to import an already converted ACEScg HDRI ?
There is a way to have it working. You can specify the colorspace in the
file name. The colorspace has to be the exact same name as the one
defined in the config. An example would be: myhdri_ACES - ACEScg.exr.
Left one is sRGB encoded, middle and right ACEScg encoded. Right one
doesn’t get properly converted and looks shifted.
Everything in The color-picker section applies here. You can just except
more odd behavior as a wider-gamut and more complex view-transform will not
play well with it. The first potential issue that came to my mind is the
combinaison of 0-1 clamping + ACES ODT : If you are picking a pure value of
RGB(1,1,1) considered encoded as Output - sRGB, you would usually get back
a scene-referred value of RGB(16.2,16.2,16.2). But the color-picker is
clamped between the 0-1 range so you will end up with just RGB(1,1,1).
Well … the less brain-damaging solution would be to just eyeball the
color.
Luckily, the 7.4.2 update made thing easier for us. The hexadecimal color
will assume to be encoded in the sRGB standard colorspace and converted
to the working colorspace. So just copy/paste your color in the hexadecimal
field and you should have your color converted.
Even with this, you might notice that the color is still not similar to
the sRGB one. This is normal and inherent to the ACES ODT which apply a
“creative” and “technical” transformation on display.
So keep this in mind: you will never be able to match the look
of the sRGB workflow with the ACES workflow. (unless cheating).
Do yourself a favour here and only care about EXR. You don’t need anything
else and this is the file format recommended by the Academy for ACES
data encoding.
If you choose EXR, you have nothing to care about. Color channel will be
exported in ACEScg while scalar channel will bypass any
color-transform encoding. Simple as that.
Reminder that you can choose to remove the $colorspace token in your
export template map name to avoid unwanted special characters in your file
name. (and instead export the textures in a folder named ACEScg)
OCIO divide the Display section into 3 components :
Display : the physical hardware you are using (monitor, TV, phone, …).
View : a way to encode the data for a specific viewing purpose.
Look : a creative layer of modification on the data. ex: a grade.
Why do I explain you this ? Because these components are often mismatched
or forgotten. Unfortunately, Substance makes no exception here.
If you look at the view-transform screenshot above, you can see that each
option has the Default prefix.
If we have a look at the config.ocio file from the Substance config,
we can see why :
What should be a display or a separate view is actually all merged
into a single view component !
But even with this fix, it’s not very friendly to have a long list of merged
(display + view) while you would only need one Display most of the time. The
best solution here would be to have 2 dropdowns :
One to choose the Display, and one to choose the corresponding available View.
We should even get a third one for looks as we are going to see in the next
section :
In above explanations where I mention OCIO Display is build with
3 components, we now see that I didn’t mention the last one yet: Looks.
Looks is a color-tansformation performed in any colorspace aimed at
modifying the data in a creative way. This would allow for example the
artist to have a first look at how its renders could looks like after the
di pass.
Usually, Looks are defined similar to colorspaces, as a list, but you can also
make a Look available in a display’s view:
displays:sRGB:-!<View>{name:Display, colorspace:sRGB-Display}-!<View>{name:Display Grade A, colorspace:sRGB-Display, looks:gradeA}looks:-!<Look>name:gradeAprocess_space:rclg16transform:!<FileTransform>{src:look_A.cc, interpolation:linear}
In the best case, we should have a dropdown menu that would allow us to combine
the current view-transform with any Look defined. A good example of this
is Blender :
Notice how it respects the 3 components of an OCIO display.
Unfortunately, sp didn’t implement this feature yet. So we can only rely
on merging the look in a display view for now.
A good way to test this is using the Filmic OCIO config by Troy Sobotka.
The filmic encoding is correctly available in a View but require an
extra step to be correctly displayed. By default it is a flat log
representation, and require choosing a Look with the desired contrast amount.
To have it working in sp, it is required to merge the Look in a new View.
displays:sRGB:-!<View>{name:sRGB OETF, colorspace:sRGB OETF}...-!<View>{name:Filmic Very High Contrast, colorspace:Filmic Log Encoding, look:+Very High Contrast}...
Which in sp, if we kept all the contrast amount, give us a very long list of
cropped name 😬 But at least it’s working.
The Substance OCIO config is an OCIO v1 configuration. I don’t know
what is the reason they decided to not use the v2 for their config because
it could really helped having a cleaner and better config (even if the artist
wouldn’t see that much of a difference).
By curiosity I tried to put my hand on OCIO v2 and create a config that could
be a substitution of the Substance config. Documentation was pretty straight
forward and I manage to build a nice config using python. You can find the
result here :
This list aim at helping the potential Substance dev team members reading
this, addressing the issues. Keep in mind that this is my personal opinion, i’m not a color-scientist
nor a profesional developer.
could overall, benefits from using OCIO v2 features.
OCIO roles are not supported, as such default configuration for
projects is wrong and can confuse artists.
(implemented on 7.4.2 !)(see Substance parameters for OCIO configs)
The view-transform dropdown is too small in width. When selecting long
display names, they got cropped.
(see sp-odt-name-cropped)
The view-transform dropdown could be split into 2 dropdowns. One for
Displays and one for Views.
(see substance-config-displays-fixed )
Colorspace on resources (images, …) should be performable from the shelf
and not from a layer’s slot. A resource doesn’t have its original
colorspace changing depending on where it’s used !
There is no direct option to change the environment image colorspace.
Having the above suggestion implemented would solve this one too.
(implemented on 7.4.2 !)(see Environment )
Color-picker : modifying the top colorspace should affect the
editable values. Where the top colorspace represents the colorspace used
to enter values so they can be converted to the working colorspace
behind the scene.
(implemented on 7.4.2 !)
With the above, add a way to see what values are being used in
the workspace.
(implemented on 7.4.2 !)
Color-picker is broken. It react differently depending of the OCIO
config version use.
(see picker section for details).
Update: it is less broken in 7.4.2 but there is still no some issues.
No options to set a specific colorspace for textures at export time.
When use the following OCIO v2 colorspace name token, an error is logged:
[ColorManagement] Error while creating OpenColorIO colorspace transform: Color space '<USE_DISPLAY_NAME>' could not be found
But displays still manage to work without issues.
EDIT: This actually affect the color-picker who doesn’t seems to supports
shared-views.
custom user channels doesn’t behave as color-managed in the interface (see
Custom User Channels)
Damn that was a long one. Congrats if you stick to the end, I hope you
now have an idea of how you could use OCIO in SubstancePainter. If not,
don’t hesitate to contact me to suggest how this
article could be improved. (you can also join the discord, click on the purple
button at the bottom of this page)
If you like this post and wish to support me you could buy some of my
scripts on my Gumroad or check my ko-fi at
the bottom of this page.
I see you in the next one that would probably be on the same topic but on
Mari. 👋