Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MAYA-133932 - Implement OpenPBR import/export #3990

Merged
merged 3 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions cmake/modules/FindUSD.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,22 @@ if (USD_LIBRARY_DIR AND EXISTS "${USD_LIBRARY_DIR}/${USD_LIB_PREFIX}usdMtlx${CMA
endif()
endif()

# See if we are getting OpenPBR Surface shader from USD:
pierrebai-adsk marked this conversation as resolved.
Show resolved Hide resolved
set(USD_HAS_MX_OPENPBR_SURFACE FALSE CACHE INTERNAL "USD.MaterialX.OpenPBRSurface")
if (PXR_USD_LOCATION AND
(EXISTS "${PXR_USD_LOCATION}/libraries/bxdf/mx39_open_pbr_surface.mtlx" OR
EXISTS "${PXR_USD_LOCATION}/libraries/bxdf/open_pbr_surface.mtlx"))
set(USD_HAS_MX_OPENPBR_SURFACE TRUE CACHE INTERNAL "USD.MaterialX.OpenPBRSurface")
message(STATUS "USD has OpenPBR Surface")
endif()

# See if we are using the backported OpenPBR Surface shader, which needs special handling of Mx39FresnelData:
set(USD_HAS_BACKPORTED_MX39_OPENPBR FALSE CACHE INTERNAL "USD.MaterialX.Mx39OpenPBRSurface")
if (PXR_USD_LOCATION AND EXISTS "${PXR_USD_LOCATION}/libraries/pbrlib/genglsl/lib/mx39_microfacet_specular.glsl")
set(USD_HAS_BACKPORTED_MX39_OPENPBR TRUE CACHE INTERNAL "USD.MaterialX.Mx39OpenPBRSurface")
message(STATUS "USD has backported MaterialX 1.39 OpenPBR Surface code")
endif()

include(FindPackageHandleStandardArgs)

find_package_handle_standard_args(USD
Expand Down
13 changes: 13 additions & 0 deletions lib/mayaUsd/fileio/shading/shadingModeRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,28 @@ TF_DECLARE_PUBLIC_TOKENS(
MAYAUSD_CORE_PUBLIC,
PXRUSDMAYA_SHADINGMODE_TOKENS);

#if MAYA_API_VERSION >= 20250300
// clang-format off
#define PXRUSDMAYA_SHADINGCONVERSION_TOKENS \
(none) \
(lambert) \
(openPBRSurface) \
(standardSurface) \
(usdPreviewSurface) \
(blinn) \
(phong)
// clang-format on
#else
// clang-format off
#define PXRUSDMAYA_SHADINGCONVERSION_TOKENS \
(none) \
(lambert) \
(standardSurface) \
(usdPreviewSurface) \
(blinn) \
(phong)
// clang-format on
#endif

TF_DECLARE_PUBLIC_TOKENS(
UsdMayaPreferredMaterialTokens,
Expand Down
20 changes: 20 additions & 0 deletions lib/mayaUsd/fileio/shading/shadingModeUseRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,26 @@ class UseRegistryShadingModeImporter
}

if (!mayaAttr.isNull()) {
// Connecting the R component of a color to a color should be possible. Check
// if there is a compatible parent in case of type mismatch.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My test case has a few monochrome textures connected to float inputs. Added support for that.

MFnAttribute srcFnAttr(srcAttr.attribute());
MFnAttribute mayaFnAttr(mayaAttr.attribute());
if (!srcFnAttr.acceptsAttribute(mayaFnAttr)) {
if (srcAttr.isChild()) {
const auto srcParentPlug = srcAttr.parent();
const auto srcParentAttr = MFnAttribute(srcParentPlug);
if (srcParentAttr.acceptsAttribute(mayaFnAttr)) {
srcAttr = srcParentPlug;
}
} else if (mayaAttr.isChild()) {
const auto mayaParentPlug = mayaAttr.parent();
const auto mayaParentAttr = MFnAttribute(mayaParentPlug);
if (srcFnAttr.acceptsAttribute(mayaParentAttr)) {
mayaAttr = mayaParentPlug;
}
}
}

UsdMayaUtil::Connect(srcAttr, mayaAttr, false);
}
}
Expand Down
17 changes: 17 additions & 0 deletions lib/mayaUsd/render/MaterialXGenOgsXml/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ target_sources(${PROJECT_NAME}
)
endif()

if(USD_HAS_BACKPORTED_MX39_OPENPBR)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The backported OpenPBR code requires injecting Maya lighting code at the exact right moment. This is done by creating a special closure node to handle to handle the two MaterialX nodes that require updated lighting.

target_compile_definitions(${PROJECT_NAME}
PRIVATE
USD_HAS_BACKPORTED_MX39_OPENPBR
)
target_sources(${PROJECT_NAME}
PRIVATE
Nodes/MayaClosureSourceCodeNode.cpp
)
endif()

set(HEADERS
CombinedMaterialXVersion.h
GlslFragmentGenerator.h
Expand All @@ -56,6 +67,12 @@ list(APPEND LIGHT_IMPLEMENTATIONS
libraries/mx_lighting_maya_v3.glsl
)

if(USD_HAS_BACKPORTED_MX39_OPENPBR)
list(APPEND LIGHT_IMPLEMENTATIONS
libraries/mx39_lighting_maya_all.glsl
)
endif()

list(APPEND NODE_DECLARATIONS
libraries/maya_surfaces.mtlx
libraries/maya_utilities.mtlx
Expand Down
28 changes: 28 additions & 0 deletions lib/mayaUsd/render/MaterialXGenOgsXml/GlslFragmentGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
#include "Nodes/MayaHwImageNode.h"
#include "Nodes/MayaSourceCodeNode.h"
#endif
#ifdef USD_HAS_BACKPORTED_MX39_OPENPBR
#include "Nodes/MayaClosureSourceCodeNode.h"
#endif

#include <mayaUsd/render/MaterialXGenOgsXml/CombinedMaterialXVersion.h>
#include <mayaUsd/render/MaterialXGenOgsXml/GlslOcioNodeImpl.h>
Expand Down Expand Up @@ -559,21 +562,33 @@ ShaderPtr GlslFragmentGenerator::generate(
emitLineBreak(pixelStage);
MX_EMIT_INCLUDE(
libRoot + "pbrlib/genglsl/ogsxml/mx_lighting_maya_v3.glsl", context, pixelStage);
#ifdef USD_HAS_BACKPORTED_MX39_OPENPBR
emitLine("#define MAYA_MX39_USING_ENVIRONMENT_FIS", pixelStage, false);
#endif
} else if (specularMethod == SPECULAR_ENVIRONMENT_PREFILTER) {
if (OgsXmlGenerator::useLightAPI() < 2) {
MX_EMIT_INCLUDE(
libRoot + "pbrlib/genglsl/ogsxml/mx_lighting_maya_v1.glsl",
context,
pixelStage);
#ifdef USD_HAS_BACKPORTED_MX39_OPENPBR
emitLine("#define MAYA_MX39_USING_ENVIRONMENT_PREFILTER_V1", pixelStage, false);
#endif
} else {
MX_EMIT_INCLUDE(
libRoot + "pbrlib/genglsl/ogsxml/mx_lighting_maya_v2.glsl",
context,
pixelStage);
#ifdef USD_HAS_BACKPORTED_MX39_OPENPBR
emitLine("#define MAYA_MX39_USING_ENVIRONMENT_PREFILTER_V2", pixelStage, false);
#endif
}
} else if (specularMethod == SPECULAR_ENVIRONMENT_NONE) {
MX_EMIT_INCLUDE(
libRoot + "pbrlib/genglsl/ogsxml/mx_lighting_maya_none.glsl", context, pixelStage);
#ifdef USD_HAS_BACKPORTED_MX39_OPENPBR
emitLine("#define MAYA_MX39_USING_ENVIRONMENT_NONE", pixelStage, false);
#endif
} else {
throw ExceptionShaderGenError(
"Invalid hardware specular environment method specified: '"
Expand Down Expand Up @@ -954,6 +969,19 @@ GlslFragmentGenerator::getImplementation(const NodeDef& nodedef, GenContext& con
context.addNodeImplementation(name, impl);

return impl;
#ifdef USD_HAS_BACKPORTED_MX39_OPENPBR
} else if (
implElement->getName() == "IM_dielectric_tf_bsdf_genglsl"
|| implElement->getName() == "IM_generalized_schlick_tf_82_bsdf_genglsl") {
// We need to inject lighting code into the backported OpenPBR:
impl = MayaClosureSourceCodeNode::create();
impl->initialize(*implElement, context);

// Cache it.
context.addNodeImplementation(name, impl);

return impl;
#endif
}
return GlslShaderGenerator::getImplementation(nodedef, context);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// Copyright Contributors to the MaterialX Project
// SPDX-License-Identifier: Apache-2.0
//

#include "MayaClosureSourceCodeNode.h"

#include <MaterialXGenShader/HwShaderGenerator.h>

MATERIALX_NAMESPACE_BEGIN

ShaderNodeImplPtr MayaClosureSourceCodeNode::create()
{
return std::make_shared<MayaClosureSourceCodeNode>();
}

void MayaClosureSourceCodeNode::emitFunctionDefinition(
const ShaderNode& node,
GenContext& context,
ShaderStage& stage) const
{
// Pre-inject backported OpenPBR lighting code:
if (_name == "IM_dielectric_tf_bsdf_genglsl"
|| _name == "IM_generalized_schlick_tf_82_bsdf_genglsl") {
static const auto allIncludes
= std::array<FilePath, 2> { "pbrlib/genglsl/lib/mx39_microfacet_specular.glsl",
"pbrlib/genglsl/ogsxml/mx39_lighting_maya_all.glsl" };
for (auto const& toInclude : allIncludes) {
// Update source code to inject our mx39 lighting functions:
FilePath libraryPrefix = context.getOptions().libraryPrefix;
FilePath fullFilename = libraryPrefix.isEmpty() ? toInclude : libraryPrefix / toInclude;
FilePath resolvedFilename = context.resolveSourceFile(fullFilename, FilePath());
stage.addInclude(fullFilename, resolvedFilename, context);
}
}
return ClosureSourceCodeNode::emitFunctionDefinition(node, context, stage);
}

MATERIALX_NAMESPACE_END
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// Copyright Contributors to the MaterialX Project
// SPDX-License-Identifier: Apache-2.0
//

#ifndef MAYA_MATERIALX_CLOSURESOURCECODENODE_H
#define MAYA_MATERIALX_CLOSURESOURCECODENODE_H

#include <MaterialXGenShader/Nodes/ClosureSourceCodeNode.h>

MATERIALX_NAMESPACE_BEGIN

/// Source code node that supports the backported OpenPBR Surface node from MaterialX 1.39
class MayaClosureSourceCodeNode : public ClosureSourceCodeNode
{
public:
static ShaderNodeImplPtr create();

void emitFunctionDefinition(const ShaderNode& node, GenContext& context, ShaderStage& stage)
const override;
};

MATERIALX_NAMESPACE_END

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#ifdef MAYA_MX39_USING_ENVIRONMENT_FIS
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same code as found in the regular Maya lighting files, but adapted to use Mx39FresnelData and mx39compute_fresnel.


vec3 mx39_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distribution, Mx39FresnelData fd)
{
if (mayaGetSpecularEnvironmentNumLOD() == 0) {
return vec3(0);
}

// Generate tangent frame.
X = normalize(X - dot(X, N) * N);
vec3 Y = cross(N, X);
mat3 tangentToWorld = mat3(X, Y, N);

// Transform the view vector to tangent space.
V = vec3(dot(V, X), dot(V, Y), dot(V, N));

// Compute derived properties.
float NdotV = clamp(V.z, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(alpha);
float G1V = mx_ggx_smith_G1(NdotV, avgAlpha);

// Integrate outgoing radiance using filtered importance sampling.
// http://cgg.mff.cuni.cz/~jaroslav/papers/2008-egsr-fis/2008-egsr-fis-final-embedded.pdf
vec3 radiance = vec3(0.0);
for (int i = 0; i < MX_NUM_FIS_SAMPLES; i++)
{
vec2 Xi = mx_spherical_fibonacci(i, MX_NUM_FIS_SAMPLES);

// Compute the half vector and incoming light direction.
vec3 H = mx_ggx_importance_sample_VNDF(Xi, V, alpha);
vec3 L = fd.refraction ? mx_refraction_solid_sphere(-V, H, fd.ior.x) : -reflect(V, H);

// Compute dot products for this sample.
float NdotL = clamp(L.z, M_FLOAT_EPS, 1.0);
float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);

// Sample the environment light from the given direction.
vec3 Lw = tangentToWorld * L;
float pdf = mx_ggx_NDF(H, alpha) * G1V / (4.0 * NdotV);
float lod = mx_latlong_compute_lod_adsk(Lw, pdf, float(mayaGetSpecularEnvironmentNumLOD() - 1), MX_NUM_FIS_SAMPLES);
vec3 sampleColor = mayaSampleSpecularEnvironmentAtLOD(Lw, lod);

// Compute the Fresnel term.
vec3 F = mx39_compute_fresnel(VdotH, fd);

// Compute the geometric term.
float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);

// Compute the combined FG term, which is inverted for refraction.
vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : F * G;

// Add the radiance contribution of this sample.
// From https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
// incidentLight = sampleColor * NdotL
// microfacetSpecular = D * F * G / (4 * NdotL * NdotV)
// pdf = D * G1V / (4 * NdotV);
// radiance = incidentLight * microfacetSpecular / pdf
radiance += sampleColor * FG;
}

// Apply the global component of the geometric term and normalize.
radiance /= G1V * float(MX_NUM_FIS_SAMPLES);

// Return the final radiance.
return radiance;
}

#endif

#ifdef MAYA_MX39_USING_ENVIRONMENT_PREFILTER_V1

vec3 mx39_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distribution, Mx39FresnelData fd)
{
N = mx_forward_facing_normal(N, V);
vec3 L = fd.refraction ? mx_refraction_solid_sphere(-V, N, fd.ior.x) : -reflect(V, N);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(alpha);
vec3 F = mx39_compute_fresnel(NdotV, fd);
float G = mx_ggx_smith_G2(NdotV, NdotV, avgAlpha);
vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : F * G;
vec3 Li = mix(g_specularI, g_diffuseI, avgAlpha);
return Li * FG;
}

#endif

#ifdef MAYA_MX39_USING_ENVIRONMENT_PREFILTER_V2

vec3 mx39_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distribution, Mx39FresnelData fd)
{
N = mx_forward_facing_normal(N, V);
vec3 L = fd.refraction ? mx_refraction_solid_sphere(-V, N, fd.ior.x) : -reflect(V, N);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(alpha);
vec3 F = mx39_compute_fresnel(NdotV, fd);
float G = mx_ggx_smith_G2(NdotV, NdotV, avgAlpha);
vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : F * G;
float phongExp = mayaRoughnessToPhongExp(sqrt(avgAlpha));
vec3 Li = mayaGetSpecularEnvironment(N, V, phongExp);
return Li * FG;
}

#endif

#ifdef MAYA_MX39_USING_ENVIRONMENT_NONE

vec3 mx39_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distribution, Mx39FresnelData fd)
{
return vec3(0.0);
}

#endif
24 changes: 15 additions & 9 deletions lib/mayaUsd/render/vp2RenderDelegate/material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,21 +335,27 @@ struct _MaterialXData
{
_MaterialXData()
{
_mtlxSearchPath = HdMtlxSearchPaths();
try {
_mtlxSearchPath = HdMtlxSearchPaths();
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any error in this code path will throw a C++ exception that will completely break the Maya viewport. Making sure we catch any issue here.

#if PXR_VERSION > 2311
_mtlxLibrary = HdMtlxStdLibraries();
_mtlxLibrary = HdMtlxStdLibraries();
#else
_mtlxLibrary = mx::createDocument();
mx::loadLibraries({}, _mtlxSearchPath, _mtlxLibrary);
_mtlxLibrary = mx::createDocument();
mx::loadLibraries({}, _mtlxSearchPath, _mtlxLibrary);
#endif

_FixLibraryTangentInputs(_mtlxLibrary);
_FixLibraryTangentInputs(_mtlxLibrary);

mx::OgsXmlGenerator::setUseLightAPI(MAYA_LIGHTAPI_VERSION_2);
mx::OgsXmlGenerator::setUseLightAPI(MAYA_LIGHTAPI_VERSION_2);

// This environment variable is defined in USD: pxr\usd\usdMtlx\parser.cpp
static const std::string env = TfGetenv("USDMTLX_PRIMARY_UV_NAME");
_mainUvSetName = env.empty() ? UsdUtilsGetPrimaryUVSetName().GetString() : env;
// This environment variable is defined in USD: pxr\usd\usdMtlx\parser.cpp
static const std::string env = TfGetenv("USDMTLX_PRIMARY_UV_NAME");
_mainUvSetName = env.empty() ? UsdUtilsGetPrimaryUVSetName().GetString() : env;

} catch (mx::Exception& e) {
TF_RUNTIME_ERROR(
"Caught exception '%s' while initializing MaterialX library", e.what());
}
}
MaterialX::FileSearchPath _mtlxSearchPath; //!< MaterialX library search path
MaterialX::DocumentPtr _mtlxLibrary; //!< MaterialX library
Expand Down
Loading
Loading