Fix: USD import: Improve texture color space choice

Many assets with UsdPreviewSurface materials exist in which the textures
do not have authored color space settings.  Because of this, assets with
normal maps and other non-color data look incorrect on import because
these textures are marked as being sRGB instead of Non-Color.

This patch fixes the color space choices for these textures based on
connections to the PBR shader; if no color space is specified, non-Albedo
maps get an automatic Non-Color assignment.

Co-authored-by: Charles Wardlaw <kattkieru@users.noreply.github.com>
Pull Request: https://projects.blender.org/blender/blender/pulls/113374
This commit is contained in:
Charles Wardlaw
2023-12-06 18:17:53 +01:00
committed by Michael Kowalski
parent d24a49da84
commit b7fa45b68d
2 changed files with 74 additions and 28 deletions
@@ -62,6 +62,8 @@ static const pxr::TfToken st("st", pxr::TfToken::Immortal);
static const pxr::TfToken varname("varname", pxr::TfToken::Immortal);
/* Color space names. */
static const pxr::TfToken auto_("auto", pxr::TfToken::Immortal);
static const pxr::TfToken sRGB("sRGB", pxr::TfToken::Immortal);
static const pxr::TfToken raw("raw", pxr::TfToken::Immortal);
static const pxr::TfToken RAW("RAW", pxr::TfToken::Immortal);
@@ -548,12 +550,13 @@ void USDMaterialReader::set_principled_node_inputs(bNode *principled,
/* Recursively set the principled shader inputs. */
if (pxr::UsdShadeInput diffuse_input = usd_shader.GetInput(usdtokens::diffuseColor)) {
set_node_input(diffuse_input, principled, "Base Color", ntree, column, &context);
set_node_input(diffuse_input, principled, "Base Color", ntree, column, &context, true);
}
float emission_strength = 0.0f;
if (pxr::UsdShadeInput emissive_input = usd_shader.GetInput(usdtokens::emissiveColor)) {
if (set_node_input(emissive_input, principled, "Emission Color", ntree, column, &context)) {
if (set_node_input(
emissive_input, principled, "Emission Color", ntree, column, &context, true)) {
emission_strength = 1.0f;
}
}
@@ -562,37 +565,38 @@ void USDMaterialReader::set_principled_node_inputs(bNode *principled,
((bNodeSocketValueFloat *)emission_strength_sock->default_value)->value = emission_strength;
if (pxr::UsdShadeInput specular_input = usd_shader.GetInput(usdtokens::specularColor)) {
set_node_input(specular_input, principled, "Specular Tint", ntree, column, &context);
set_node_input(specular_input, principled, "Specular Tint", ntree, column, &context, false);
}
if (pxr::UsdShadeInput metallic_input = usd_shader.GetInput(usdtokens::metallic)) {
;
set_node_input(metallic_input, principled, "Metallic", ntree, column, &context);
set_node_input(metallic_input, principled, "Metallic", ntree, column, &context, false);
}
if (pxr::UsdShadeInput roughness_input = usd_shader.GetInput(usdtokens::roughness)) {
set_node_input(roughness_input, principled, "Roughness", ntree, column, &context);
set_node_input(roughness_input, principled, "Roughness", ntree, column, &context, false);
}
if (pxr::UsdShadeInput coat_input = usd_shader.GetInput(usdtokens::clearcoat)) {
set_node_input(coat_input, principled, "Coat Weight", ntree, column, &context);
set_node_input(coat_input, principled, "Coat Weight", ntree, column, &context, false);
}
if (pxr::UsdShadeInput coat_roughness_input = usd_shader.GetInput(usdtokens::clearcoatRoughness))
{
set_node_input(coat_roughness_input, principled, "Coat Roughness", ntree, column, &context);
set_node_input(
coat_roughness_input, principled, "Coat Roughness", ntree, column, &context, false);
}
if (pxr::UsdShadeInput opacity_input = usd_shader.GetInput(usdtokens::opacity)) {
set_node_input(opacity_input, principled, "Alpha", ntree, column, &context);
set_node_input(opacity_input, principled, "Alpha", ntree, column, &context, false);
}
if (pxr::UsdShadeInput ior_input = usd_shader.GetInput(usdtokens::ior)) {
set_node_input(ior_input, principled, "IOR", ntree, column, &context);
set_node_input(ior_input, principled, "IOR", ntree, column, &context, false);
}
if (pxr::UsdShadeInput normal_input = usd_shader.GetInput(usdtokens::normal)) {
set_node_input(normal_input, principled, "Normal", ntree, column, &context);
set_node_input(normal_input, principled, "Normal", ntree, column, &context, false);
}
}
@@ -601,7 +605,8 @@ bool USDMaterialReader::set_node_input(const pxr::UsdShadeInput &usd_input,
const char *dest_socket_name,
bNodeTree *ntree,
const int column,
NodePlacementContext *r_ctx) const
NodePlacementContext *r_ctx,
bool is_color_corrected) const
{
if (!(usd_input && dest_node && r_ctx)) {
return false;
@@ -610,7 +615,8 @@ bool USDMaterialReader::set_node_input(const pxr::UsdShadeInput &usd_input,
if (usd_input.HasConnectedSource()) {
/* The USD shader input has a connected source shader. Follow the connection
* and attempt to convert the connected USD shader to a Blender node. */
return follow_connection(usd_input, dest_node, dest_socket_name, ntree, column, r_ctx);
return follow_connection(
usd_input, dest_node, dest_socket_name, ntree, column, r_ctx, is_color_corrected);
}
else {
/* Set the destination node socket value from the USD shader input value. */
@@ -675,7 +681,8 @@ bool USDMaterialReader::follow_connection(const pxr::UsdShadeInput &usd_input,
const char *dest_socket_name,
bNodeTree *ntree,
int column,
NodePlacementContext *r_ctx) const
NodePlacementContext *r_ctx,
bool is_color_corrected) const
{
if (!(usd_input && dest_node && dest_socket_name && ntree && r_ctx)) {
return false;
@@ -721,12 +728,24 @@ bool USDMaterialReader::follow_connection(const pxr::UsdShadeInput &usd_input,
link_nodes(ntree, normal_map, "Normal", dest_node, "Normal");
/* Now, create the Texture Image node input to the Normal Map "Color" input. */
convert_usd_uv_texture(
source_shader, source_name, normal_map, "Color", ntree, column + 2, r_ctx);
convert_usd_uv_texture(source_shader,
source_name,
normal_map,
"Color",
ntree,
column + 2,
r_ctx,
is_color_corrected);
}
else {
convert_usd_uv_texture(
source_shader, source_name, dest_node, dest_socket_name, ntree, column + 1, r_ctx);
convert_usd_uv_texture(source_shader,
source_name,
dest_node,
dest_socket_name,
ntree,
column + 1,
r_ctx,
is_color_corrected);
}
}
else if (shader_id == usdtokens::UsdPrimvarReader_float2) {
@@ -746,7 +765,8 @@ void USDMaterialReader::convert_usd_uv_texture(const pxr::UsdShadeShader &usd_sh
const char *dest_socket_name,
bNodeTree *ntree,
const int column,
NodePlacementContext *r_ctx) const
NodePlacementContext *r_ctx,
bool is_color_corrected) const
{
if (!usd_shader || !dest_node || !ntree || !dest_socket_name || !bmain_ || !r_ctx) {
return;
@@ -772,7 +792,7 @@ void USDMaterialReader::convert_usd_uv_texture(const pxr::UsdShadeShader &usd_sh
cache_node(r_ctx->node_cache, usd_shader, tex_image);
/* Load the texture image. */
load_tex_image(usd_shader, tex_image);
load_tex_image(usd_shader, tex_image, is_color_corrected);
}
/* Connect to destination node input. */
@@ -784,7 +804,7 @@ void USDMaterialReader::convert_usd_uv_texture(const pxr::UsdShadeShader &usd_sh
/* Connect the texture image node "Vector" input. */
if (pxr::UsdShadeInput st_input = usd_shader.GetInput(usdtokens::st)) {
set_node_input(st_input, tex_image, "Vector", ntree, column, r_ctx);
set_node_input(st_input, tex_image, "Vector", ntree, column, r_ctx, false);
}
}
@@ -862,12 +882,13 @@ void USDMaterialReader::convert_usd_transform_2d(const pxr::UsdShadeShader &usd_
/* Connect the mapping node "Vector" input. */
if (pxr::UsdShadeInput in_input = usd_shader.GetInput(usdtokens::in)) {
set_node_input(in_input, mapping, "Vector", ntree, column, r_ctx);
set_node_input(in_input, mapping, "Vector", ntree, column, r_ctx, false);
}
}
void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader,
bNode *tex_image) const
bNode *tex_image,
bool is_color_corrected) const
{
if (!(usd_shader && tex_image && tex_image->type == SH_NODE_TEX_IMAGE)) {
return;
@@ -962,8 +983,28 @@ void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader,
color_space = file_input.GetAttr().GetColorSpace();
}
if (ELEM(color_space, usdtokens::RAW, usdtokens::raw)) {
STRNCPY(image->colorspace_settings.name, "Raw");
if (color_space.IsEmpty()) {
/* At this point, assume the "auto" space and translate accordingly. */
color_space = usdtokens::auto_;
}
if (color_space == usdtokens::auto_) {
/* If it's auto, determine whether to apply color correction based
* on incoming connection (passed in from outer functions). */
STRNCPY(image->colorspace_settings.name, is_color_corrected ? "sRGB" : "Non-Color");
}
else if (color_space == usdtokens::sRGB) {
STRNCPY(image->colorspace_settings.name, "sRGB");
}
/*
* Due to there being a lot of non-compliant USD assets out there, this is
* a special case where we need to check for different spellings here.
* On write, we are *only* using the correct, lower-case "raw" token.
*/
else if (ELEM(color_space, usdtokens::RAW, usdtokens::raw)) {
STRNCPY(image->colorspace_settings.name, "Non-Color");
}
NodeTexImage *storage = static_cast<NodeTexImage *>(tex_image->storage);
@@ -108,7 +108,8 @@ class USDMaterialReader {
const char *dest_socket_name,
bNodeTree *ntree,
int column,
NodePlacementContext *r_ctx) const;
NodePlacementContext *r_ctx,
bool is_color_corrected) const;
/**
* Follow the connected source of the USD input to create corresponding inputs
@@ -119,7 +120,8 @@ class USDMaterialReader {
const char *dest_socket_name,
bNodeTree *ntree,
int column,
NodePlacementContext *r_ctx) const;
NodePlacementContext *r_ctx,
bool is_color_corrected = false) const;
void convert_usd_uv_texture(const pxr::UsdShadeShader &usd_shader,
const pxr::TfToken &usd_source_name,
@@ -127,7 +129,8 @@ class USDMaterialReader {
const char *dest_socket_name,
bNodeTree *ntree,
int column,
NodePlacementContext *r_ctx) const;
NodePlacementContext *r_ctx,
bool is_color_corrected = false) const;
void convert_usd_transform_2d(const pxr::UsdShadeShader &usd_shader,
bNode *dest_node,
@@ -140,7 +143,9 @@ class USDMaterialReader {
* Load the texture image node's texture from the path given by the USD shader's
* file input value.
*/
void load_tex_image(const pxr::UsdShadeShader &usd_shader, bNode *tex_image) const;
void load_tex_image(const pxr::UsdShadeShader &usd_shader,
bNode *tex_image,
bool is_color_corrected = false) const;
/**
* This function creates a Blender UV Map node, under the simplifying assumption that