Skip to content
Open
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
5 changes: 1 addition & 4 deletions libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,6 @@ vec3 mx_eval_sensitivity(float opd, vec3 shift)
// https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html
vec3 mx_fresnel_airy(float cosTheta, FresnelData fd)
{
// XYZ to CIE 1931 RGB color space (using neutral E illuminant)
const mat3 XYZ_TO_RGB = mat3(2.3706743, -0.5138850, 0.0052982, -0.9000405, 1.4253036, -0.0146949, -0.4706338, 0.0885814, 1.0093968);

// Assume vacuum on the outside
float eta1 = 1.0;
float eta2 = max(fd.tf_ior, eta1);
Expand Down Expand Up @@ -393,7 +390,7 @@ vec3 mx_fresnel_airy(float cosTheta, FresnelData fd)
I *= 0.5;

// Convert back to RGB reflectance
I = clamp(mx_matrix_mul(XYZ_TO_RGB, I), 0.0, 1.0);
I = clamp(mx_matrix_mul($xyzToWorkingSpace, I), 0.0, 1.0);

return I;
}
Expand Down
7 changes: 1 addition & 6 deletions libraries/pbrlib/genglsl/mx_blackbody.glsl
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
/// XYZ to Rec.709 RGB colorspace conversion
const mat3 XYZ_to_RGB = mat3( 3.2406, -0.9689, 0.0557,
-1.5372, 1.8758, -0.2040,
-0.4986, 0.0415, 1.0570);

void mx_blackbody(float temperatureKelvin, out vec3 colorValue)
{
float xc, yc;
Expand Down Expand Up @@ -47,6 +42,6 @@ void mx_blackbody(float temperatureKelvin, out vec3 colorValue)

vec3 XYZ = vec3(xc / yc, 1.0, (1.0 - xc - yc) / yc);

colorValue = mx_matrix_mul(XYZ_to_RGB, XYZ);
colorValue = mx_matrix_mul($xyzToWorkingSpace, XYZ);
colorValue = max(colorValue, vec3(0.0));
}
2 changes: 2 additions & 0 deletions source/MaterialXGenHw/HwConstants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const string T_TIME = "$time";
const string T_GEOMPROP = "$geomprop";
const string T_ALPHA_THRESHOLD = "$alphaThreshold";
const string T_NUM_ACTIVE_LIGHT_SOURCES = "$numActiveLightSources";
const string T_XYZ_TO_WORKING_SPACE = "$xyzToWorkingSpace";
const string T_ENV_MATRIX = "$envMatrix";
const string T_ENV_RADIANCE = "$envRadiance";
const string T_ENV_RADIANCE_SAMPLER2D = "$envRadianceSampler2D";
Expand Down Expand Up @@ -112,6 +113,7 @@ const string TIME = "u_time";
const string GEOMPROP = "u_geomprop";
const string ALPHA_THRESHOLD = "u_alphaThreshold";
const string NUM_ACTIVE_LIGHT_SOURCES = "u_numActiveLightSources";
const string XYZ_TO_WORKING_SPACE = "u_xyzToWorkingSpace";
const string ENV_MATRIX = "u_envMatrix";
const string ENV_RADIANCE = "u_envRadiance";
const string ENV_RADIANCE_SPLIT = "u_envRadiance_texture, u_envRadiance_sampler";
Expand Down
8 changes: 6 additions & 2 deletions source/MaterialXGenHw/HwConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ a listing of the variables with a description of what data they should be bound
However, different renderers can have different requirements on naming conventions for these variables.
In order to facilitate this the generators will use token substitution for naming the variables. The
first column below shows the token names that should be used in source code before the token substitution
is done. The second row shows the real identifier names that will be used by default after substitution.
An generator can override these identifier names in order to use a custom naming convention for these.
is done. The second column shows the real identifier names that will be used by default after substitution.
A generator can override these identifier names in order to use a custom naming convention for these.
Overriding identifier names is done by changing the entries in the identifiers map given to the function
replaceIdentifiers(), which is handling the token substitution on a shader stage.

Expand Down Expand Up @@ -67,6 +67,8 @@ Uniform variables :
$lightData[] u_lightData[] struct Array of struct LightData holding parameters for active light sources.
The LightData struct is built dynamically depending on requirements for
bound light shaders.
$xyzToWorkingSpace u_xyzToWorkingSpace mat3 Transform from CIE XYZ to the renderer's linear working color space,
initialized from GenOptions.xyzToWorkingSpace.
$envMatrix u_envMatrix mat4 Rotation matrix for the environment.
$envIrradiance u_envIrradiance sampler2D Sampler for the texture used for diffuse environment lighting.
$envIrradianceSampler2D u_envIrradiance sampler2D For split texture and sampler, takes form of sampler2D(tex, sampler)
Expand Down Expand Up @@ -125,6 +127,7 @@ extern MX_GENHW_API const string T_TIME;
extern MX_GENHW_API const string T_GEOMPROP;
extern MX_GENHW_API const string T_ALPHA_THRESHOLD;
extern MX_GENHW_API const string T_NUM_ACTIVE_LIGHT_SOURCES;
extern MX_GENHW_API const string T_XYZ_TO_WORKING_SPACE;
extern MX_GENHW_API const string T_ENV_MATRIX;
extern MX_GENHW_API const string T_ENV_RADIANCE;
extern MX_GENHW_API const string T_ENV_RADIANCE_SAMPLER2D;
Expand Down Expand Up @@ -188,6 +191,7 @@ extern MX_GENHW_API const string TIME;
extern MX_GENHW_API const string GEOMPROP;
extern MX_GENHW_API const string ALPHA_THRESHOLD;
extern MX_GENHW_API const string NUM_ACTIVE_LIGHT_SOURCES;
extern MX_GENHW_API const string XYZ_TO_WORKING_SPACE;
extern MX_GENHW_API const string ENV_MATRIX;
extern MX_GENHW_API const string ENV_RADIANCE;
extern MX_GENHW_API const string ENV_RADIANCE_SPLIT;
Expand Down
14 changes: 9 additions & 5 deletions source/MaterialXGenHw/HwShaderGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ HwShaderGenerator::HwShaderGenerator(TypeSystemPtr typeSystem, SyntaxPtr syntax)
_tokenSubstitutions[HW::T_GEOMPROP] = HW::GEOMPROP;
_tokenSubstitutions[HW::T_ALPHA_THRESHOLD] = HW::ALPHA_THRESHOLD;
_tokenSubstitutions[HW::T_NUM_ACTIVE_LIGHT_SOURCES] = HW::NUM_ACTIVE_LIGHT_SOURCES;
_tokenSubstitutions[HW::T_XYZ_TO_WORKING_SPACE] = HW::XYZ_TO_WORKING_SPACE;
_tokenSubstitutions[HW::T_ENV_MATRIX] = HW::ENV_MATRIX;
_tokenSubstitutions[HW::T_ENV_RADIANCE] = HW::ENV_RADIANCE;
_tokenSubstitutions[HW::T_ENV_RADIANCE_SAMPLER2D] = HW::ENV_RADIANCE_SAMPLER2D;
Expand Down Expand Up @@ -173,6 +174,9 @@ ShaderPtr HwShaderGenerator::createShader(const string& name, ElementPtr element
// Add a block for data from vertex to pixel shader.
addStageConnectorBlock(HW::VERTEX_DATA, HW::T_VERTEX_DATA_INSTANCE, *vs, *ps);

// Add the transform from CIE XYZ to the working color space.
psPrivateUniforms->add(Type::MATRIX33, HW::T_XYZ_TO_WORKING_SPACE, Value::createValue(context.getOptions().xyzToWorkingSpace));

// Add uniforms for transparent rendering.
if (context.getOptions().hwTransparency)
{
Expand Down Expand Up @@ -448,24 +452,24 @@ void HwShaderGenerator::toVec4(TypeDesc type, string& variable) const

if (type.isFloat3())
{
variable = vec4+"(" + variable + ", 1.0)";
variable = vec4 + "(" + variable + ", 1.0)";
}
else if (type.isFloat2())
{
variable = vec4+"(" + variable + ", 0.0, 1.0)";
variable = vec4 + "(" + variable + ", 0.0, 1.0)";
}
else if (type == Type::FLOAT || type == Type::INTEGER || type == Type::BOOLEAN)
{
variable = vec4+"(" + variable + ", " + variable + ", " + variable + ", 1.0)";
variable = vec4 + "(" + variable + ", " + variable + ", " + variable + ", 1.0)";
}
else if (type == Type::BSDF || type == Type::EDF)
{
variable = vec4+"(" + variable + ", 1.0)";
variable = vec4 + "(" + variable + ", 1.0)";
}
else
{
// Can't understand other types. Just return black.
variable = vec4+"(0.0, 0.0, 0.0, 1.0)";
variable = vec4 + "(0.0, 0.0, 0.0, 1.0)";
}
}

Expand Down
10 changes: 10 additions & 0 deletions source/MaterialXGenShader/GenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

#include <MaterialXFormat/File.h>

#include <MaterialXCore/Types.h>

MATERIALX_NAMESPACE_BEGIN

/// Type of shader interface to be generated
Expand Down Expand Up @@ -78,6 +80,10 @@ class MX_GENSHADER_API GenOptions
GenOptions() :
shaderInterfaceType(SHADER_INTERFACE_COMPLETE),
fileTextureVerticalFlip(false),
xyzToWorkingSpace(
3.2406f, -0.9689f, 0.0557f,
-1.5372f, 1.8758f, -0.2040f,
-0.4986f, 0.0415f, 1.0570f),
addUpstreamDependencies(true),
libraryPrefix("libraries"),
emitColorTransforms(true),
Expand Down Expand Up @@ -117,6 +123,10 @@ class MX_GENSHADER_API GenOptions
/// texture space convention. By default this option is false.
bool fileTextureVerticalFlip;

/// Transform from CIE XYZ to the renderer's linear working color space.
/// Defaults to the CIE XYZ to lin_rec709 transform.
Matrix33 xyzToWorkingSpace;

/// An optional override for the target color space.
/// Shader fragments will be generated to transform
/// input values and textures into this color space.
Expand Down
104 changes: 100 additions & 4 deletions source/MaterialXTest/MaterialXGenGlsl/GenGlsl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,103 @@ TEST_CASE("GenShader: GLSL Unique Names", "[genglsl]")
GenShaderUtil::testUniqueNames(context, mx::Stage::PIXEL);
}

TEST_CASE("GenShader: GLSL XYZ to Working Space", "[genglsl]")
{
// Verify the default CIE XYZ to lin_rec709 transform.
const mx::Matrix33 expectedDefault(
3.2406f, -0.9689f, 0.0557f,
-1.5372f, 1.8758f, -0.2040f,
-0.4986f, 0.0415f, 1.0570f);

const mx::GenOptions defaultOptions;
CHECK(defaultOptions.xyzToWorkingSpace == expectedDefault);

// Include both consumers of the transform in a single shader:
// blackbody emission and thin-film iridescence.
const std::string testDocumentString = R"(<?xml version="1.0"?>
<materialx version="1.39">
<blackbody name="blackbody" type="color3">
<input name="temperature" type="float" value="5000.0" />
</blackbody>

<dielectric_bsdf name="thin_film" type="BSDF">
<input name="ior" type="float" value="2.5" />
<input name="roughness" type="vector2" value="0.0, 0.0" />
<input name="thinfilm_thickness" type="float" value="550.0" />
<input name="thinfilm_ior" type="float" value="1.5" />
</dielectric_bsdf>

<uniform_edf name="emission" type="EDF">
<input name="color" type="color3" nodename="blackbody" />
</uniform_edf>

<surface name="surface" type="surfaceshader">
<input name="bsdf" type="BSDF" nodename="thin_film" />
<input name="edf" type="EDF" nodename="emission" />
</surface>

<surfacematerial name="material" type="material">
<input name="surfaceshader" type="surfaceshader" nodename="surface" />
</surfacematerial>
</materialx>)";

mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath();

mx::DocumentPtr libraries = mx::createDocument();
loadLibraries({ "libraries" }, searchPath, libraries);

mx::DocumentPtr testDoc = mx::createDocument();
mx::readFromXmlString(testDoc, testDocumentString, searchPath);
testDoc->setDataLibrary(libraries);

mx::ElementPtr element = testDoc->getChild("material");
REQUIRE(element);

// Use a clearly distinct value to prove that the GenOptions override
// reaches the generated shader interface.
const mx::Matrix33 customTransform = mx::Matrix33::IDENTITY;

mx::GenContext context(mx::GlslShaderGenerator::create());
context.registerSourceCodeSearchPath(searchPath);
context.getOptions().xyzToWorkingSpace = customTransform;

mx::ShaderPtr shader =
context.getShaderGenerator().generate("xyz_to_working_space", element, context);
REQUIRE(shader);

const mx::ShaderStage& pixelStage = shader->getStage(mx::Stage::PIXEL);
const mx::VariableBlock& privateUniforms =
pixelStage.getUniformBlock(mx::HW::PRIVATE_UNIFORMS);

// The VariableBlock lookup key remains the original token, even though
// token substitution updates the port's emitted name.
const mx::ShaderPort* uniform =
privateUniforms.find(mx::HW::T_XYZ_TO_WORKING_SPACE);

REQUIRE(uniform);
CHECK(uniform->getType() == mx::Type::MATRIX33);
CHECK(uniform->getName() == mx::HW::XYZ_TO_WORKING_SPACE);
CHECK(uniform->getVariable() == mx::HW::XYZ_TO_WORKING_SPACE);

REQUIRE(uniform->getValue());
REQUIRE(uniform->getValue()->isA<mx::Matrix33>());
CHECK(uniform->getValue()->asA<mx::Matrix33>() == customTransform);

const std::string& source = pixelStage.getSourceCode();

// Verify that the private uniform was emitted and all tokens were replaced.
CHECK(source.find("uniform mat3 u_xyzToWorkingSpace") != std::string::npos);
CHECK(source.find("$xyzToWorkingSpace") == std::string::npos);

// Verify that both blackbody and thin-film calculations use the uniform.
CHECK(source.find("mx_matrix_mul(u_xyzToWorkingSpace, XYZ)") != std::string::npos);
CHECK(source.find("mx_matrix_mul(u_xyzToWorkingSpace, I)") != std::string::npos);

// Verify that neither old hardcoded matrix remains.
CHECK(source.find("XYZ_to_RGB") == std::string::npos);
CHECK(source.find("XYZ_TO_RGB") == std::string::npos);
}

TEST_CASE("GenShader: GLSL Light Shaders", "[genglsl]")
{
mx::DocumentPtr doc = mx::createDocument();
Expand Down Expand Up @@ -129,7 +226,7 @@ TEST_CASE("GenShader: GLSL Light Shaders", "[genglsl]")
TEST_CASE("GenShader: GLSL Performance Test", "[genglsl]")
{
mx::GenContext context(mx::GlslShaderGenerator::create());
BENCHMARK("Load documents, validate and generate shader")
BENCHMARK("Load documents, validate and generate shader")
{
return GenShaderUtil::shaderGenPerformanceTest(context);
};
Expand Down Expand Up @@ -172,13 +269,12 @@ static void generateGlslCode(GlslType type)
generator = mx::GlslShaderGenerator::create();
}

const std::unordered_map<GlslType, std::string> TYPE_NAME_MAP =
{
const std::unordered_map<GlslType, std::string> TYPE_NAME_MAP = {
{ GlslType::Essl, "essl" },
{ GlslType::Glsl, "glsl" },
{ GlslType::GlslLayout, "glsl_layout" },
{ GlslType::GlslVulkan, "glsl_vulkan" },
{ GlslType::GlslWgsl , "glsl_wgsl" }
{ GlslType::GlslWgsl, "glsl_wgsl" }
};
const mx::FilePath logPath("genglsl_" + TYPE_NAME_MAP.at(type) + "_generate_test.txt");
GlslShaderGeneratorTester tester(generator, testRootPaths, searchPath, logPath, false);
Expand Down
Loading