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

Adds optionVars for controlling specular lighting options #2261

Merged
merged 3 commits into from
Apr 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions lib/mayaUsd/render/MaterialXGenOgsXml/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ set(HEADERS
# install
# -----------------------------------------------------------------------------
list(APPEND LIGHT_IMPLEMENTATIONS
libraries/mx_lighting_maya_none.glsl
libraries/mx_lighting_maya_v1.glsl
libraries/mx_lighting_maya_v2.glsl
libraries/mx_lighting_maya_v3.glsl
Expand Down
47 changes: 39 additions & 8 deletions lib/mayaUsd/render/MaterialXGenOgsXml/GlslFragmentGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ const string MAYA_ENV_RADIANCE_SAMPLE = "specularI";
const string MAYA_ENV_ROUGHNESS = "roughness";
} // namespace

const string& HwSpecularEnvironmentSamples::name()
{
static const string USER_DATA_ENV_SAMPLES = "HwSpecularEnvironmentSamples";
return USER_DATA_ENV_SAMPLES;
}

string GlslFragmentSyntax::getVariableName(
const string& name,
const TypeDesc* type,
Expand Down Expand Up @@ -169,17 +175,42 @@ ShaderPtr GlslFragmentGenerator::generate(
emitInclude("stdlib/genglsl/lib/mx_math.glsl", context, pixelStage);
emitLineBreak(pixelStage);

if (lighting) {
switch (OgsXmlGenerator::useLightAPI()) {
case 3:
emitInclude("pbrlib/genglsl/ogsxml/mx_lighting_maya_v3.glsl", context, pixelStage);
break;
case 2:
int specularMethod = context.getOptions().hwSpecularEnvironmentMethod;
if (specularMethod == SPECULAR_ENVIRONMENT_FIS) {
emitLine(
"#define DIRECTIONAL_ALBEDO_METHOD "
+ std::to_string(int(context.getOptions().hwDirectionalAlbedoMethod)),
pixelStage,
false);
emitLineBreak(pixelStage);
HwSpecularEnvironmentSamplesPtr pSamples
= context.getUserData<HwSpecularEnvironmentSamples>(
HwSpecularEnvironmentSamples::name());
if (pSamples) {
emitLine(
"#define MX_NUM_FIS_SAMPLES "
+ std::to_string(pSamples->hwSpecularEnvironmentSamples),
pixelStage,
false);
} else {
emitLine("#define MX_NUM_FIS_SAMPLES 64", pixelStage, false);
}
emitLineBreak(pixelStage);
emitInclude("pbrlib/genglsl/ogsxml/mx_lighting_maya_v3.glsl", context, pixelStage);
} else if (specularMethod == SPECULAR_ENVIRONMENT_PREFILTER) {
if (OgsXmlGenerator::useLightAPI() < 2) {
emitInclude("pbrlib/genglsl/ogsxml/mx_lighting_maya_v1.glsl", context, pixelStage);
} else {
emitInclude("pbrlib/genglsl/ogsxml/mx_lighting_maya_v2.glsl", context, pixelStage);
break;
default: emitInclude("pbrlib/genglsl/ogsxml/mx_lighting_maya_v1.glsl", context, pixelStage);
}
} else if (specularMethod == SPECULAR_ENVIRONMENT_NONE) {
emitInclude("pbrlib/genglsl/ogsxml/mx_lighting_maya_none.glsl", context, pixelStage);
} else {
throw ExceptionShaderGenError(
"Invalid hardware specular environment method specified: '"
+ std::to_string(specularMethod) + "'");
}
emitLineBreak(pixelStage);

// Set the include file to use for uv transformations,
// depending on the vertical flip flag.
Expand Down
23 changes: 23 additions & 0 deletions lib/mayaUsd/render/MaterialXGenOgsXml/GlslFragmentGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include <MaterialXGenGlsl/GlslShaderGenerator.h>
#include <MaterialXGenGlsl/GlslSyntax.h>
#include <MaterialXGenShader/GenUserData.h>

MATERIALX_NAMESPACE_BEGIN

Expand All @@ -21,6 +22,28 @@ namespace Stage {
extern const string UNIFORMS;
} // namespace Stage

class HwSpecularEnvironmentSamples;
using HwSpecularEnvironmentSamplesPtr = shared_ptr<class HwSpecularEnvironmentSamples>;
class HwSpecularEnvironmentSamples : public GenUserData
{
public:
HwSpecularEnvironmentSamples(int numSamples)
: hwSpecularEnvironmentSamples(numSamples)
{
}

static const std::string& name();

/// Create and return a new instance.
static HwSpecularEnvironmentSamplesPtr create(int numSamples)
{
return std::make_shared<HwSpecularEnvironmentSamples>(numSamples);
}

/// Number of environment samples to take under FIS lighting.
int hwSpecularEnvironmentSamples = 64;
};

/// Syntax class for GLSL fragments.
class GlslFragmentSyntax : public GlslSyntax
{
Expand Down
104 changes: 96 additions & 8 deletions lib/mayaUsd/render/MaterialXGenOgsXml/OgsFragment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,70 @@
#include <MaterialXCross/Cross.h>
#endif

#include <maya/MGlobal.h>
#include <maya/MString.h>

#include <iostream>

namespace MaterialXMaya {
namespace {

/// String option var controlling the environment method. Valid values are "none", "prefiltered" and
/// "fis". Default values are based on available light API:
///
/// API | Default | Options
/// V1 | prefiltered | none, prefiltered
/// V2 | prefiltered | none, prefiltered
/// V3 | fis | none, prefiltered, fis
const MString OPTVAR_ENVIRONMENT_METHOD = "MxMayaEnvironmentMethod";

/// Int option var to control the number of FIS samples. Larger values will slow down GLSL
/// rendering speed and can lead to TDR. Default is 64, expected range is 1 - 1024 by powers of
/// two. Has no effect unless fis mode is available and selected.
const MString OPTVAR_NUM_SAMPLES = "MxMayaEnvironmentSamples";

/// String option var to control the GGX albedo computations. Valid values are "polynomial" and
/// "montecarlo". The latter one has a performance impact on the rendering. Has no effect unless
/// fis mode is available and selected.
const MString OPTVAR_ALBEDO_METHOD = "MxMayaEnvironmentAlbedoMethod";

// Find the expected environment mode depending on Maya capabilities and optionVars:
mx::HwSpecularEnvironmentMethod _getEnvironmentOptions(int& numSamples, bool& isMonteCarlo)
{
bool varExists = false;
switch (mx::OgsXmlGenerator::useLightAPI()) {
case 1:
case 2: {
// We default with prefilter but will respect "None" as a choice
MString envMethod = MGlobal::optionVarStringValue(OPTVAR_ENVIRONMENT_METHOD, &varExists);
if (varExists && envMethod == "none") {
return mx::SPECULAR_ENVIRONMENT_NONE;
} else {
return mx::SPECULAR_ENVIRONMENT_PREFILTER;
}
} break;
case 3: {
// We default with fis
MString envMethod = MGlobal::optionVarStringValue(OPTVAR_ENVIRONMENT_METHOD, &varExists);
if (varExists) {
if (envMethod == "none") {
return mx::SPECULAR_ENVIRONMENT_NONE;
} else if (envMethod == "prefiltered") {
return mx::SPECULAR_ENVIRONMENT_PREFILTER;
}
}
numSamples = MGlobal::optionVarIntValue(OPTVAR_NUM_SAMPLES, &varExists);
if (!varExists) {
numSamples = 64;
}
MString albedoMethod = MGlobal::optionVarStringValue(OPTVAR_ALBEDO_METHOD, &varExists);
isMonteCarlo = (varExists && albedoMethod == "montecarlo");
return mx::SPECULAR_ENVIRONMENT_FIS;
} break;
}
return mx::SPECULAR_ENVIRONMENT_NONE;
}

// The base class for classes wrapping GLSL fragment generators for use during
// OgsFragment construction.
class GlslGeneratorWrapperBase
Expand Down Expand Up @@ -50,12 +110,23 @@ class GlslGeneratorWrapperBase
}

protected:
void setCommonOptions(mx::GenOptions& genOptions, const mx::ShaderGenerator& generator)
void setCommonOptions(
mx::GenOptions& genOptions,
mx::GenContext& context,
const mx::ShaderGenerator& generator)
{
// Use FIS environment lookup for surface shader generation but
// disable for texture nodes to avoid additional unneeded XML parameter
// generation.
genOptions.hwSpecularEnvironmentMethod = mx::SPECULAR_ENVIRONMENT_NONE;
int numSamples = 64;
bool isMonteCarlo = false;
genOptions.hwSpecularEnvironmentMethod = _getEnvironmentOptions(numSamples, isMonteCarlo);
// FIS option has further sub-options to check:
if (genOptions.hwSpecularEnvironmentMethod == mx::SPECULAR_ENVIRONMENT_FIS) {
context.pushUserData(
mx::HwSpecularEnvironmentSamples::name(),
mx::HwSpecularEnvironmentSamples::create(numSamples));
if (isMonteCarlo) {
genOptions.hwDirectionalAlbedoMethod = mx::DIRECTIONAL_ALBEDO_MONTE_CARLO;
}
}

// Set to use no direct lighting
if (mx::OgsXmlGenerator::useLightAPI() >= 2) {
Expand Down Expand Up @@ -137,7 +208,7 @@ class LocalGlslGeneratorWrapper : public GlslGeneratorWrapperBase

genContext.registerSourceCodeSearchPath(_librarySearchPath);

setCommonOptions(genOptions, *generator);
setCommonOptions(genOptions, genContext, *generator);

// Every light ends up as a directional light once processed thru Maya:
mx::DocumentPtr document = _element->getDocument();
Expand Down Expand Up @@ -170,7 +241,7 @@ class ExternalGlslGeneratorWrapper : public GlslGeneratorWrapperBase
mx::ShaderGenerator& generator = _genContext.getShaderGenerator();
mx::GenOptions& genOptions = _genContext.getOptions();

setCommonOptions(genOptions, generator);
setCommonOptions(genOptions, _genContext, generator);

return generator.generate(baseFragmentName, _element, _genContext);
}
Expand Down Expand Up @@ -223,7 +294,8 @@ std::string generateFragment(
// MaterialX fragment).
std::ostringstream nameStream;
const size_t sourceHash = std::hash<std::string> {}(fragmentSource);
nameStream << baseFragmentName << "__" << std::hex << sourceHash;
nameStream << baseFragmentName << "__" << std::hex << sourceHash
<< OgsFragment::getSpecularEnvKey();
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Shader code depends on environment settings, so we must make sure we add the key everywhere we want to cache shaders.

std::string fragmentName = nameStream.str();

// Substitute the placeholder name token with the actual name.
Expand Down Expand Up @@ -386,4 +458,20 @@ std::string OgsFragment::getMatrix4Name(const std::string& matrix3Name)
return matrix3Name + mx::GlslFragmentGenerator::MATRIX3_TO_MATRIX4_POSTFIX;
}

std::string OgsFragment::getSpecularEnvKey()
{
std::string retVal;
int numSamples = 64;
bool isMonteCarlo = false;
switch (_getEnvironmentOptions(numSamples, isMonteCarlo)) {
case mx::SPECULAR_ENVIRONMENT_FIS:
retVal += "F" + std::to_string(numSamples) + (isMonteCarlo ? "MC" : "P");
break;
case mx::SPECULAR_ENVIRONMENT_PREFILTER: retVal = "P"; break;
default: retVal = "N"; break;
}

return retVal;
}

} // namespace MaterialXMaya
3 changes: 3 additions & 0 deletions lib/mayaUsd/render/MaterialXGenOgsXml/OgsFragment.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ class OgsFragment
/// Required because OGS doesn't support matrix3 parameters.
static std::string getMatrix4Name(const std::string& matrix3Name);

/// Get a string that is unique for each environment settings possible:
static std::string getSpecularEnvKey();

private:
/// The constructor implementation that public constructors delegate to.
template <typename GLSL_GENERATOR_WRAPPER>
Expand Down
17 changes: 17 additions & 0 deletions lib/mayaUsd/render/MaterialXGenOgsXml/OgsXmlGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ const pugi::char_t* CONNECT("connect");
const pugi::char_t* CONNECTIONS("connections");
const pugi::char_t* DESCRIPTION("description");
const pugi::char_t* DIFFUSEI("diffuseI");
const pugi::char_t* ENVMATRIX("u_envMatrix");
const pugi::char_t* ENVRADIANCEMIPS("u_envRadianceMips");
const pugi::char_t* ENVRADIANCESAMPLES("u_envRadianceSamples");
const pugi::char_t* FEATURE_LEVEL("feature_level");
const pugi::char_t* FLAGS("flags");
const pugi::char_t* FRAGMENT("fragment");
Expand Down Expand Up @@ -134,6 +137,13 @@ const pugi::char_t* VALUE("value");
const pugi::char_t* VALUES("values");
const pugi::char_t* VERSION("version");

// The shadergen will insert some uniforms that are not actually used in the code. The full list is
// here:
bool isUnusedUniform(const std::string& name)
{
return name == ENVMATRIX || name == ENVRADIANCEMIPS || name == ENVRADIANCESAMPLES;
}

std::string DOT_COMBINE(const pugi::char_t* frag, const pugi::char_t* attr)
{
std::string retVal(frag);
Expand Down Expand Up @@ -202,6 +212,10 @@ void xmlAddProperties(
continue;
}

if (isUnusedUniform(shaderPort->getVariable())) {
continue;
}

if (shaderPort->getType() == Type::FILENAME) {
const string& samplerName = shaderPort->getVariable();
const string textureName = OgsXmlGenerator::samplerToTextureName(samplerName);
Expand Down Expand Up @@ -242,6 +256,9 @@ void xmlAddValues(pugi::xml_node& parent, const VariableBlock& block, bool skipL
// Skip diffuseI and specularI when generating light rig graph.
continue;
}
if (isUnusedUniform(p->getVariable())) {
continue;
}
if (p->getValue()) {
auto type = getOgsTypeMap().find(p->getType()->getName());
if (type != getOgsTypeMap().end()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// This is the same algorithm as found in libraries\pbrlib\genglsl\lib\mx_environment_prefilter.glsl
// but adjusted for Maya. At this time we will compute a roughness based on the radiance and
// irradiance samples, so materials with small amount of roughness will look wrong.
//
// A more precise roughness computation can be done using Maya samplers, but this requires
// knowing that the Maya sampling functions are there, otherwise compilation will fail unless
// there is an IBL active in the Maya lighting.

#include "pbrlib/genglsl/lib/mx_microfacet_specular.glsl"

vec3 mx_environment_irradiance(vec3 N)
{
return vec3(0);
}

vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 roughness, int distribution, FresnelData fd)
{
return vec3(0);
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,6 @@

#include "pbrlib/genglsl/lib/mx_microfacet_specular.glsl"

// TODO: Make the number of samples either an environment variable or an optionVar.
// Keeping it a hard constant instead of a uniform allows loop unrolling by the compiler.
#define MX_NUM_FIS_SAMPLES 64

// TODO: We could also expose another toggle between the extremely slow:
// mx_ggx_dir_albedo_monte_carlo()
// And the faster:
// mx_ggx_dir_albedo_analytic()
// But we will currently default to the faster one.

// https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch20.html
// Section 20.4 Equation 13
float mx_latlong_compute_lod(vec3 dir, float pdf, float maxMipLevel, int envSamples)
Expand All @@ -28,6 +18,10 @@ float mx_latlong_compute_lod(vec3 dir, float pdf, float maxMipLevel, int envSamp

vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 roughness, int distribution, FresnelData fd)
{
if (mayaGetSpecularEnvironmentNumLOD() == 0) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Bail out early if there is no dome light.

return vec3(0);
}

// Generate tangent frame.
vec3 Y = normalize(cross(N, X));
X = cross(Y, N);
Expand Down
Loading