Skip to content

Commit

Permalink
[hdSt] Incorrect MaterialX shading without Dome Light
Browse files Browse the repository at this point in the history
MaterialX uses samplers in its shaderGen to calculate indirect
lighting. When there is a domeLight in the scene or the default
domeLight is enabled these are initialized with the appropriate
HdGetSampler_domeLight**() functions. If there is no domeLlight,
MaterialX still expects valid samplers. When using non-bindless
textures these samplers are declared without bindings specified,
and as a result if the MaterialX material has textures, the shader
will sample from those material textures for the indirect lighting.

Adding a fallback domeLight texture node to the material network
in MaterialXFilter, so that when there is no domeLight we will
sample from this fallback texture instead.

Fixes #1877

(Internal change: 2238260)
  • Loading branch information
klucknav authored and pixar-oss committed Jun 25, 2022
1 parent d3cd39f commit 976f770
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 6 deletions.
1 change: 1 addition & 0 deletions pxr/imaging/hdSt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ pxr_library(hdSt
shaders/terminals.glslfx
shaders/visibility.glslfx
shaders/volume.glslfx
textures/fallbackBlackDomeLight.png

DOXYGEN_FILES
overview.dox
Expand Down
43 changes: 41 additions & 2 deletions pxr/imaging/hdSt/materialXFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "pxr/imaging/hdSt/materialParam.h"
#include "pxr/imaging/hdSt/materialXFilter.h"
#include "pxr/imaging/hdSt/materialXShaderGen.h"
#include "pxr/imaging/hdSt/package.h"
#include "pxr/imaging/hdMtlx/hdMtlx.h"

#include "pxr/usd/sdr/registry.h"
Expand Down Expand Up @@ -58,6 +59,11 @@ TF_DEFINE_PRIVATE_TOKENS(
(opacity)
(opacityThreshold)
(transmission)

// Fallback Dome Light Tokens
(domeLightFallback)
(ND_image_color3)
(file)
);


Expand Down Expand Up @@ -340,6 +346,34 @@ _GetTextureCoordinateName(
}
}

static void
_AddFallbackDomeLightTextureNode(
HdMaterialNetwork2* hdNetwork,
SdfPath const& hdTerminalNodePath,
mx::StringMap* mxHdTextureMap)
{
// Create and add a Fallback Dome Light Texture Node to the hdNetwork
HdMaterialNode2 hdDomeTextureNode;
hdDomeTextureNode.nodeTypeId = _tokens->ND_image_color3;
hdDomeTextureNode.parameters[_tokens->file] =
VtValue(SdfAssetPath(
HdStPackageFallbackDomeLightTexture(),
HdStPackageFallbackDomeLightTexture()));
const SdfPath domeTexturePath =
hdTerminalNodePath.ReplaceName(_tokens->domeLightFallback);
hdNetwork->nodes.insert({domeTexturePath, hdDomeTextureNode});

// Connect the new Texture Node to the Terminal Node
HdMaterialConnection2 domeTextureConn;
domeTextureConn.upstreamNode = domeTexturePath;
domeTextureConn.upstreamOutputName = domeTexturePath.GetNameToken();
hdNetwork->nodes[hdTerminalNodePath].
inputConnections[domeTextureConn.upstreamOutputName] = {domeTextureConn};

// Add the Dome Texture name to the TextureMap for MaterialXShaderGen
(*mxHdTextureMap)[domeTexturePath.GetName()] = domeTexturePath.GetName();
}

// Add the Hydra texture node parameters to the texture nodes and connect the
// texture nodes to the terminal node
static void
Expand All @@ -361,7 +395,7 @@ _UpdateTextureNodes(
// Gather the Hydra Texture Parameters
std::map<TfToken, VtValue> hdParameters;
for (auto const& currParam : hdTextureNode.parameters) {

// Get the MaterialX Input Value string
std::string mxInputValue = HdMtlxConvertToString(currParam.second);

Expand All @@ -380,7 +414,7 @@ _UpdateTextureNodes(
// mxHdTextureMap with this connection name so Hydra's codegen and
// HdStMaterialXShaderGen match up correctly
std::string newConnName = texturePath.GetName() + "_" +
(*mxHdTextureMap)[texturePath.GetName()];;
(*mxHdTextureMap)[texturePath.GetName()];
(*mxHdTextureMap)[texturePath.GetName()] = newConnName;

// Make and add a new connection to the terminal node
Expand Down Expand Up @@ -638,6 +672,11 @@ HdSt_ApplyMaterialXFilter(
&mxHdInfo.textureMap,
&hdPrimvarNodes);

// Add a Fallback DomeLight texture node to make sure the indirect
// light computations always has a texture to sample from
_AddFallbackDomeLightTextureNode(
hdNetwork, terminalNodePath, &mxHdInfo.textureMap);

// Add Hydra parameters for each of the Texture nodes
_UpdateTextureNodes(mtlxDoc, hdNetwork, terminalNodePath, hdTextureNodes,
&mxHdInfo.textureMap, &mxHdInfo.primvarMap,
Expand Down
18 changes: 14 additions & 4 deletions pxr/imaging/hdSt/materialXShaderGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,15 +324,18 @@ HdStMaterialXShaderGen::_EmitMxFunctions(
emitLine("#define u_envRadiance HdGetSampler_domeLightPrefilter() ", mxStage, false);
emitLine("#define u_envIrradiance HdGetSampler_domeLightIrradiance() ", mxStage, false);
emitLine("#else", mxStage, false);
emitLine("uniform sampler2D u_envRadiance;", mxStage, false);
emitLine("uniform sampler2D u_envIrradiance;", mxStage, false);
emitLine("#define u_envRadiance HdGetSampler_domeLightFallback()", mxStage, false);
emitLine("#define u_envIrradiance HdGetSampler_domeLightFallback()", mxStage, false);
emitLine("#endif", mxStage, false);
emitLineBreak(mxStage);

// Define mappings for the MaterialX Textures
if (!_mxHdTextureMap.empty()) {
emitComment("Define MaterialX to Hydra Sampler mappings", mxStage);
for (auto texturePair : _mxHdTextureMap) {
if (texturePair.first == "domeLightFallback") {
continue;
}
emitLine(TfStringPrintf("#define %s_file HdGetSampler_%s()",
texturePair.first.c_str(),
texturePair.second.c_str()),
Expand Down Expand Up @@ -594,19 +597,26 @@ HdStMaterialXShaderGen::_EmitMxInitFunction(
// Note: only need to initialize textures when bindlessTextures are enabled,
// when bindlessTextures are not enabled, mappings are defined in
// HdStMaterialXShaderGen::_EmitMxFunctions
emitLine("#ifdef HD_HAS_domeLightIrradiance", mxStage, false);
emitComment("Initialize Indirect Light Textures and values", mxStage);
if (_bindlessTexturesEnabled) {
emitLine("#ifdef HD_HAS_domeLightIrradiance", mxStage, false);
emitLine("u_envIrradiance = HdGetSampler_domeLightIrradiance()", mxStage);
emitLine("u_envRadiance = HdGetSampler_domeLightPrefilter()", mxStage);
emitLine("#else", mxStage, false);
emitLine("u_envIrradiance = HdGetSampler_domeLightFallback()", mxStage);
emitLine("u_envRadiance = HdGetSampler_domeLightFallback()", mxStage);
emitLine("#endif", mxStage, false);
}
emitLine("u_envRadianceMips = textureQueryLevels(u_envRadiance)", mxStage);
emitLine("#endif", mxStage, false);
emitLineBreak(mxStage);

// Initialize MaterialX Texture samplers with HdGetSampler equivalents
if (_bindlessTexturesEnabled && !_mxHdTextureMap.empty()) {
emitComment("Initialize Material Textures", mxStage);
for (auto texturePair : _mxHdTextureMap) {
if (texturePair.first == "domeLightFallback") {
continue;
}
emitLine(texturePair.first + "_file = "
"HdGetSampler_" + texturePair.second + "()", mxStage);
}
Expand Down
18 changes: 18 additions & 0 deletions pxr/imaging/hdSt/package.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ _GetShaderPath(char const * shader)
return TfToken(path);
}

static TfToken
_GetTexturePath(char const * texture)
{
static PlugPluginPtr plugin = PLUG_THIS_PLUGIN;
const std::string path =
PlugFindPluginResource(plugin, TfStringCatPaths("textures", texture));
TF_VERIFY(!path.empty(), "Could not find texture: %s\n", texture);

return TfToken(path);
}

TfToken
HdStPackageComputeShader()
{
Expand All @@ -57,6 +68,13 @@ HdStPackageDomeLightShader()
return s;
}

TfToken
HdStPackageFallbackDomeLightTexture()
{
static TfToken t = _GetTexturePath("fallbackBlackDomeLight.png");
return t;
}

TfToken
HdStPackagePtexTextureShader()
{
Expand Down
3 changes: 3 additions & 0 deletions pxr/imaging/hdSt/package.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ TfToken HdStPackageComputeShader();
HDST_API
TfToken HdStPackageDomeLightShader();

HDST_API
TfToken HdStPackageFallbackDomeLightTexture();

HDST_API
TfToken HdStPackagePtexTextureShader();

Expand Down
Binary file added pxr/imaging/hdSt/textures/fallbackBlackDomeLight.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#usda 1.0
(
upAxis = "Z"
)

def "TestMaterial" (
references = @texturedSphere.usda@
)
{
rel material:binding = </MaterialX/Materials/TextureTest>
}


def Scope "MaterialX" (
references = [
@./textureTest.mtlx@</MaterialX>,
]
)
{
}

def Xform "lights"
{
def SphereLight "Light"
{
float inputs:radius = 5
float inputs:intensity = 10
Vec3f xformOp:translate = (0, -20, 0)
uniform token[] xformOpOrder = ["xformOp:translate"]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0"?>
<materialx version="1.38">
<nodegraph name="NG_imagetex">
<texcoord name="texcoord0" type="vector2" />
<image name="image0_color" type="color3" nodedef="ND_image_color3">
<input name="file" type="filename" uniform="true" value="./grid.png" />
<input name="texcoord" type="vector2" nodename="texcoord0" />
</image>
<output name="out_color_0" type="color3" nodename="image0_color" />
</nodegraph>
<standard_surface name="SR_test" type="surfaceshader">
<input name="base" type="float" value="1" />
<input name="base_color" type="color3" nodegraph="NG_imagetex" output="out_color_0" />
<input name="metalness" type="float" value="0" />
</standard_surface>
<surfacematerial name="TextureTest" type="material">
<input name="surfaceshader" type="surfaceshader" nodename="SR_test" />
</surfacematerial>
</materialx>

0 comments on commit 976f770

Please sign in to comment.