Skip to content

Commit

Permalink
Support explicitly authored bitangents in GLSL backend (#1156)
Browse files Browse the repository at this point in the history
  • Loading branch information
pablode authored Dec 22, 2022
1 parent 4a8913d commit 29cac85
Show file tree
Hide file tree
Showing 11 changed files with 210 additions and 60 deletions.
71 changes: 52 additions & 19 deletions source/MaterialXGenGlsl/Nodes/BitangentNodeGlsl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,36 @@ ShaderNodeImplPtr BitangentNodeGlsl::create()
return std::make_shared<BitangentNodeGlsl>();
}

void BitangentNodeGlsl::createVariables(const ShaderNode& node, GenContext&, Shader& shader) const
void BitangentNodeGlsl::createVariables(const ShaderNode& node, GenContext& context, Shader& shader) const
{
const GenOptions& options = context.getOptions();

ShaderStage& vs = shader.getStage(Stage::VERTEX);
ShaderStage& ps = shader.getStage(Stage::PIXEL);

addStageInput(HW::VERTEX_INPUTS, Type::VECTOR3, HW::T_IN_NORMAL, vs);
addStageInput(HW::VERTEX_INPUTS, Type::VECTOR3, HW::T_IN_TANGENT, vs);
if (options.hwImplicitBitangents)
{
addStageInput(HW::VERTEX_INPUTS, Type::VECTOR3, HW::T_IN_NORMAL, vs);
addStageInput(HW::VERTEX_INPUTS, Type::VECTOR3, HW::T_IN_TANGENT, vs);
}
else
{
addStageInput(HW::VERTEX_INPUTS, Type::VECTOR3, HW::T_IN_BITANGENT, vs);
}

const ShaderInput* spaceInput = node.getInput(SPACE);
const int space = spaceInput ? spaceInput->getValue()->asA<int>() : OBJECT_SPACE;
if (space == WORLD_SPACE)
{
addStageUniform(HW::PRIVATE_UNIFORMS, Type::MATRIX44, HW::T_WORLD_MATRIX, vs);
addStageUniform(HW::PRIVATE_UNIFORMS, Type::MATRIX44, HW::T_WORLD_INVERSE_TRANSPOSE_MATRIX, vs);
addStageConnector(HW::VERTEX_DATA, Type::VECTOR3, HW::T_BITANGENT_WORLD, vs, ps);
addStageUniform(HW::PRIVATE_UNIFORMS, Type::MATRIX44, HW::T_WORLD_MATRIX, vs);

if (options.hwImplicitBitangents)
{
addStageConnector(HW::VERTEX_DATA, Type::VECTOR3, HW::T_NORMAL_WORLD, vs, ps);
addStageConnector(HW::VERTEX_DATA, Type::VECTOR3, HW::T_TANGENT_WORLD, vs, ps);
addStageUniform(HW::PRIVATE_UNIFORMS, Type::MATRIX44, HW::T_WORLD_INVERSE_TRANSPOSE_MATRIX, vs);
}
}
else
{
Expand All @@ -39,6 +54,7 @@ void BitangentNodeGlsl::createVariables(const ShaderNode& node, GenContext&, Sha
void BitangentNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& context, ShaderStage& stage) const
{
const GlslShaderGenerator& shadergen = static_cast<const GlslShaderGenerator&>(context.getShaderGenerator());
const GenOptions& options = context.getOptions();

const ShaderInput* spaceInput = node.getInput(SPACE);
const int space = spaceInput ? spaceInput->getValue()->asA<int>() : OBJECT_SPACE;
Expand All @@ -48,23 +64,32 @@ void BitangentNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& con
const string prefix = shadergen.getVertexDataPrefix(vertexData);
if (space == WORLD_SPACE)
{
ShaderPort* normal = vertexData[HW::T_NORMAL_WORLD];
if (!normal->isEmitted())
{
normal->setEmitted();
shadergen.emitLine(prefix + normal->getVariable() + " = normalize((" + HW::T_WORLD_INVERSE_TRANSPOSE_MATRIX + " * vec4(" + HW::T_IN_NORMAL + ", 0.0)).xyz)", stage);
}
ShaderPort* tangent = vertexData[HW::T_TANGENT_WORLD];
if (!tangent->isEmitted())
{
tangent->setEmitted();
shadergen.emitLine(prefix + tangent->getVariable() + " = normalize((" + HW::T_WORLD_MATRIX + " * vec4(" + HW::T_IN_TANGENT + ", 0.0)).xyz)", stage);
}
ShaderPort* bitangent = vertexData[HW::T_BITANGENT_WORLD];

if (!bitangent->isEmitted())
{
bitangent->setEmitted();
shadergen.emitLine(prefix + bitangent->getVariable() + " = cross(" + normal->getVariable() + ", " + tangent->getVariable() + ")", stage);

if (options.hwImplicitBitangents)
{
ShaderPort* normal = vertexData[HW::T_NORMAL_WORLD];
if (!normal->isEmitted())
{
normal->setEmitted();
shadergen.emitLine(prefix + normal->getVariable() + " = normalize((" + HW::T_WORLD_INVERSE_TRANSPOSE_MATRIX + " * vec4(" + HW::T_IN_NORMAL + ", 0.0)).xyz)", stage);
}
ShaderPort* tangent = vertexData[HW::T_TANGENT_WORLD];
if (!tangent->isEmitted())
{
tangent->setEmitted();
shadergen.emitLine(prefix + tangent->getVariable() + " = normalize((" + HW::T_WORLD_MATRIX + " * vec4(" + HW::T_IN_TANGENT + ", 0.0)).xyz)", stage);
}
shadergen.emitLine(prefix + bitangent->getVariable() + " = cross(" + prefix + normal->getVariable() + ", " + prefix + tangent->getVariable() + ")", stage);
}
else
{
shadergen.emitLine(prefix + bitangent->getVariable() + " = normalize((" + HW::T_WORLD_MATRIX + " * vec4(" + HW::T_IN_BITANGENT + ", 0.0)).xyz)", stage);
}
}
}
else
Expand All @@ -73,7 +98,15 @@ void BitangentNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& con
if (!bitangent->isEmitted())
{
bitangent->setEmitted();
shadergen.emitLine(prefix + bitangent->getVariable() + " = cross(" + HW::T_IN_NORMAL + ", " + HW::T_IN_TANGENT + ")", stage);

if (options.hwImplicitBitangents)
{
shadergen.emitLine(prefix + bitangent->getVariable() + " = cross(" + HW::T_IN_NORMAL + ", " + HW::T_IN_TANGENT + ")", stage);
}
else
{
shadergen.emitLine(prefix + bitangent->getVariable() + " = " + HW::T_IN_BITANGENT, stage);
}
}
}
END_SHADER_STAGE(stage, Stage::VERTEX)
Expand Down
5 changes: 5 additions & 0 deletions source/MaterialXGenShader/GenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class MX_GENSHADER_API GenOptions
hwMaxActiveLightSources(3),
hwNormalizeUdimTexCoords(false),
hwWriteAlbedoTable(false),
hwImplicitBitangents(true),
emitColorTransforms(true)
{
}
Expand Down Expand Up @@ -173,6 +174,10 @@ class MX_GENSHADER_API GenOptions
/// Defaults to false.
bool hwWriteAlbedoTable;

/// Calculate fallback bitangents from existing normals and tangents
/// inside the <bitangent> node.
bool hwImplicitBitangents;

/// Enable emitting colorspace transform code if a color management
/// system is defined.
/// Defaults to true.
Expand Down
3 changes: 3 additions & 0 deletions source/MaterialXGenShader/HwShaderGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace HW
const string T_IN_POSITION = "$inPosition";
const string T_IN_NORMAL = "$inNormal";
const string T_IN_TANGENT = "$inTangent";
const string T_IN_BITANGENT = "$inBitangent";
const string T_IN_TEXCOORD = "$inTexcoord";
const string T_IN_GEOMPROP = "$inGeomprop";
const string T_IN_COLOR = "$inColor";
Expand Down Expand Up @@ -71,6 +72,7 @@ namespace HW
const string IN_POSITION = "i_position";
const string IN_NORMAL = "i_normal";
const string IN_TANGENT = "i_tangent";
const string IN_BITANGENT = "i_bitangent";
const string IN_TEXCOORD = "i_texcoord";
const string IN_GEOMPROP = "i_geomprop";
const string IN_COLOR = "i_color";
Expand Down Expand Up @@ -168,6 +170,7 @@ HwShaderGenerator::HwShaderGenerator(SyntaxPtr syntax) :
_tokenSubstitutions[HW::T_IN_POSITION] = HW::IN_POSITION;
_tokenSubstitutions[HW::T_IN_NORMAL] = HW::IN_NORMAL;
_tokenSubstitutions[HW::T_IN_TANGENT] = HW::IN_TANGENT;
_tokenSubstitutions[HW::T_IN_BITANGENT] = HW::IN_BITANGENT;
_tokenSubstitutions[HW::T_IN_TEXCOORD] = HW::IN_TEXCOORD;
_tokenSubstitutions[HW::T_IN_GEOMPROP] = HW::IN_GEOMPROP;
_tokenSubstitutions[HW::T_IN_COLOR] = HW::IN_COLOR;
Expand Down
2 changes: 2 additions & 0 deletions source/MaterialXGenShader/HwShaderGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ namespace HW
extern MX_GENSHADER_API const string T_IN_POSITION;
extern MX_GENSHADER_API const string T_IN_NORMAL;
extern MX_GENSHADER_API const string T_IN_TANGENT;
extern MX_GENSHADER_API const string T_IN_BITANGENT;
extern MX_GENSHADER_API const string T_IN_TEXCOORD;
extern MX_GENSHADER_API const string T_IN_GEOMPROP;
extern MX_GENSHADER_API const string T_IN_COLOR;
Expand Down Expand Up @@ -141,6 +142,7 @@ namespace HW
extern MX_GENSHADER_API const string IN_POSITION;
extern MX_GENSHADER_API const string IN_NORMAL;
extern MX_GENSHADER_API const string IN_TANGENT;
extern MX_GENSHADER_API const string IN_BITANGENT;
extern MX_GENSHADER_API const string IN_TEXCOORD;
extern MX_GENSHADER_API const string IN_GEOMPROP;
extern MX_GENSHADER_API const string IN_COLOR;
Expand Down
98 changes: 82 additions & 16 deletions source/MaterialXRender/CgltfLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,32 @@ void computeMeshPaths(GLTFMeshPathList& meshPaths, cgltf_node* cnode, FilePath p
}
}

void decodeVec4Tangents(MeshStreamPtr vec4TangentStream, MeshStreamPtr normalStream, MeshStreamPtr& tangentStream, MeshStreamPtr& bitangentStream)
{
if (vec4TangentStream->getSize() != normalStream->getSize())
{
return;
}

tangentStream = MeshStream::create("i_" + MeshStream::TANGENT_ATTRIBUTE, MeshStream::TANGENT_ATTRIBUTE, 0);
bitangentStream = MeshStream::create("i_" + MeshStream::BITANGENT_ATTRIBUTE, MeshStream::BITANGENT_ATTRIBUTE, 0);

tangentStream->resize(vec4TangentStream->getSize());
bitangentStream->resize(vec4TangentStream->getSize());

for (size_t i = 0; i < vec4TangentStream->getSize(); i++)
{
const Vector4& vec4Tangent = vec4TangentStream->getElement<Vector4>(i);
const Vector3& normal = normalStream->getElement<Vector3>(i);

Vector3& tangent = tangentStream->getElement<Vector3>(i);
Vector3& bitangent = bitangentStream->getElement<Vector3>(i);

tangent = Vector3(vec4Tangent[0], vec4Tangent[1], vec4Tangent[2]);
bitangent = normal.cross(tangent) * vec4Tangent[3];
}
}

} // anonymous namespace

bool CgltfLoader::load(const FilePath& filePath, MeshList& meshList, bool texcoordVerticalFlip)
Expand Down Expand Up @@ -242,7 +268,7 @@ bool CgltfLoader::load(const FilePath& filePath, MeshList& meshList, bool texcoo
MeshStreamPtr normalStream = nullptr;
MeshStreamPtr colorStream = nullptr;
MeshStreamPtr texcoordStream = nullptr;
MeshStreamPtr tangentStream = nullptr;
MeshStreamPtr vec4TangentStream = nullptr;
int colorAttrIndex = 0;

// Read in vertex streams
Expand Down Expand Up @@ -274,27 +300,31 @@ bool CgltfLoader::load(const FilePath& filePath, MeshList& meshList, bool texcoo

bool isPositionStream = (attribute->type == cgltf_attribute_type_position);
bool isNormalStream = (attribute->type == cgltf_attribute_type_normal);
bool isTangentStream = (attribute->type == cgltf_attribute_type_tangent);
bool isColorStream = (attribute->type == cgltf_attribute_type_color);
bool isTexCoordStream = (attribute->type == cgltf_attribute_type_texcoord);

if (isPositionStream)
{
// Create position stream
positionStream = MeshStream::create("i_" + MeshStream::POSITION_ATTRIBUTE, MeshStream::POSITION_ATTRIBUTE, streamIndex);
mesh->addStream(positionStream);
geomStream = positionStream;
}
else if (attribute->type == cgltf_attribute_type_normal)
else if (isNormalStream)
{
normalStream = MeshStream::create("i_" + MeshStream::NORMAL_ATTRIBUTE, MeshStream::NORMAL_ATTRIBUTE, streamIndex);
mesh->addStream(normalStream);
geomStream = normalStream;
}
else if (attribute->type == cgltf_attribute_type_tangent)
else if (isTangentStream)
{
tangentStream = MeshStream::create("i_" + MeshStream::TANGENT_ATTRIBUTE, MeshStream::TANGENT_ATTRIBUTE, streamIndex);
mesh->addStream(tangentStream);
geomStream = tangentStream;
vec4TangentStream = MeshStream::create("i_" + MeshStream::TANGENT_ATTRIBUTE + "4", MeshStream::TANGENT_ATTRIBUTE, streamIndex);
vec4TangentStream->setStride(MeshStream::STRIDE_4D); // glTF stores the bitangent sign in the 4th component
geomStream = vec4TangentStream;
desiredVectorSize = 4;
}
else if (attribute->type == cgltf_attribute_type_color)
else if (isColorStream)
{
colorStream = MeshStream::create("i_" + MeshStream::COLOR_ATTRIBUTE + "_" + std::to_string(colorAttrIndex), MeshStream::COLOR_ATTRIBUTE, streamIndex);
mesh->addStream(colorStream);
Expand All @@ -306,7 +336,7 @@ bool CgltfLoader::load(const FilePath& filePath, MeshList& meshList, bool texcoo
}
colorAttrIndex++;
}
else if (attribute->type == cgltf_attribute_type_texcoord)
else if (isTexCoordStream)
{
texcoordStream = MeshStream::create("i_" + MeshStream::TEXCOORD_ATTRIBUTE + "_0", MeshStream::TEXCOORD_ATTRIBUTE, 0);
mesh->addStream(texcoordStream);
Expand All @@ -329,6 +359,7 @@ bool CgltfLoader::load(const FilePath& filePath, MeshList& meshList, bool texcoo
{
MeshFloatBuffer& buffer = geomStream->getData();
cgltf_size vertexCount = accessor->count;
geomStream->reserve(vertexCount);

if (_debugLevel > 0)
{
Expand Down Expand Up @@ -391,6 +422,11 @@ bool CgltfLoader::load(const FilePath& filePath, MeshList& meshList, bool texcoo
}
}

if (!positionStream)
{
continue;
}

// Read indexing
MeshPartitionPtr part = MeshPartition::create();
size_t indexCount = 0;
Expand All @@ -399,7 +435,7 @@ bool CgltfLoader::load(const FilePath& filePath, MeshList& meshList, bool texcoo
{
indexCount = indexAccessor->count;
}
else if (positionStream)
else
{
indexCount = positionStream->getData().size();
}
Expand Down Expand Up @@ -430,24 +466,54 @@ bool CgltfLoader::load(const FilePath& filePath, MeshList& meshList, bool texcoo
mesh->addPartition(part);

// Update positional information.
if (positionStream)
{
mesh->setVertexCount(positionStream->getData().size() / MeshStream::STRIDE_3D);
}
mesh->setVertexCount(positionStream->getData().size() / MeshStream::STRIDE_3D);
mesh->setMinimumBounds(boxMin);
mesh->setMaximumBounds(boxMax);
Vector3 sphereCenter = (boxMax + boxMin) * 0.5;
mesh->setSphereCenter(sphereCenter);
mesh->setSphereRadius((sphereCenter - boxMin).getMagnitude());

// Generate tangents, normals and texture coordinates if none provided
if (!tangentStream && positionStream)
// According to glTF spec. 3.7.2.1, tangents must be ignored when normals are missing
if (vec4TangentStream && normalStream)
{
// Decode glTF vec4 tangents to MaterialX vec3 tangents and bitangents
MeshStreamPtr tangentStream;
MeshStreamPtr bitangentStream;
decodeVec4Tangents(vec4TangentStream, normalStream, tangentStream, bitangentStream);

if (tangentStream)
{
mesh->addStream(tangentStream);
}
if (bitangentStream)
{
mesh->addStream(bitangentStream);
}
}

// Generate tangents, normals and texture coordinates if none are provided
if (!normalStream)
{
normalStream = mesh->generateNormals(positionStream);
mesh->addStream(normalStream);
}
if (!texcoordStream)
{
tangentStream = mesh->generateTangents(positionStream, normalStream, texcoordStream);
texcoordStream = mesh->generateTextureCoordinates(positionStream);
mesh->addStream(texcoordStream);
}
if (!vec4TangentStream)
{
MeshStreamPtr tangentStream = mesh->generateTangents(positionStream, normalStream, texcoordStream);
if (tangentStream)
{
mesh->addStream(tangentStream);
}
MeshStreamPtr bitangentStream = mesh->generateBitangents(normalStream, tangentStream);
if (bitangentStream)
{
mesh->addStream(bitangentStream);
}
}
}
}
Expand Down
Loading

0 comments on commit 29cac85

Please sign in to comment.