Fix the previous fixes.
Alright, less haphazardly now after the two previous botched commits, this fixes mistakes and bugs made a year or more in the past: - We now always pass the metallic and roughness factors through all the way to the glTF layer. They should not be multiplied into the generated textures, and so they should be present as-is in glTF output. - We only generate the AO/Rough/Net combined texture if at least two of the constituent textures are present. - We only reference the generated texture as an occlusionTexture if there really was an occlusion map present (and it had non-trivial pixels). It's also now ridiculously clear that: - The material conversion section is long and tortured and it's very easy to screw up. It should be broken into functions and classes. - We urgely need a real regression suite, and we need to model some artificial FBX files that test both realistic scenarios and edge- case permutations.
This commit is contained in:
parent
46f6234af5
commit
1f21a50cc9
|
@ -256,28 +256,29 @@ ModelData* Raw2Gltf(
|
||||||
if (material.info->shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH) {
|
if (material.info->shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH) {
|
||||||
/**
|
/**
|
||||||
* PBR FBX Material -> PBR Met/Rough glTF.
|
* PBR FBX Material -> PBR Met/Rough glTF.
|
||||||
*
|
|
||||||
* METALLIC and ROUGHNESS textures are packed in G and B channels of a rough/met texture.
|
|
||||||
* Other values translate directly.
|
|
||||||
*/
|
*/
|
||||||
RawMetRoughMatProps* props = (RawMetRoughMatProps*)material.info.get();
|
RawMetRoughMatProps* props = (RawMetRoughMatProps*)material.info.get();
|
||||||
|
|
||||||
|
// diffuse and emissive are noncontroversial
|
||||||
|
baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
|
||||||
|
diffuseFactor = props->diffuseFactor;
|
||||||
|
emissiveFactor = props->emissiveFactor;
|
||||||
|
emissiveIntensity = props->emissiveIntensity;
|
||||||
|
|
||||||
|
// we always send the metallic/roughness factors onto the glTF generator
|
||||||
|
metallic = props->metallic;
|
||||||
|
roughness = props->roughness;
|
||||||
|
|
||||||
// determine if we need to generate a combined map
|
// determine if we need to generate a combined map
|
||||||
bool hasMetallicMap = material.textures[RAW_TEXTURE_USAGE_METALLIC] >= 0;
|
bool hasMetallicMap = material.textures[RAW_TEXTURE_USAGE_METALLIC] >= 0;
|
||||||
bool hasRoughnessMap = material.textures[RAW_TEXTURE_USAGE_ROUGHNESS] >= 0;
|
bool hasRoughnessMap = material.textures[RAW_TEXTURE_USAGE_ROUGHNESS] >= 0;
|
||||||
bool hasOcclusionMap = material.textures[RAW_TEXTURE_USAGE_OCCLUSION] >= 0;
|
bool hasOcclusionMap = material.textures[RAW_TEXTURE_USAGE_OCCLUSION] >= 0;
|
||||||
bool atLeastTwoMaps = hasMetallicMap ? (hasRoughnessMap || hasOcclusionMap)
|
bool atLeastTwoMaps = hasMetallicMap ? (hasRoughnessMap || hasOcclusionMap)
|
||||||
: (hasRoughnessMap && hasMetallicMap);
|
: (hasRoughnessMap && hasMetallicMap);
|
||||||
if (!atLeastTwoMaps) {
|
if (atLeastTwoMaps) {
|
||||||
// this handles the case of 0 or 1 maps supplied
|
// if there's at least two of metallic/roughness/occlusion, it makes sense to
|
||||||
aoMetRoughTex = hasMetallicMap
|
// merge them: occlusion into the red channel, metallic into blue channel, and
|
||||||
? simpleTex(RAW_TEXTURE_USAGE_METALLIC)
|
// roughness into the green.
|
||||||
: (hasRoughnessMap
|
|
||||||
? simpleTex(RAW_TEXTURE_USAGE_ROUGHNESS)
|
|
||||||
: (hasOcclusionMap ? simpleTex(RAW_TEXTURE_USAGE_OCCLUSION) : nullptr));
|
|
||||||
} else {
|
|
||||||
// otherwise merge occlusion into the red channel, metallic into blue channel, and
|
|
||||||
// roughness into the green, of a new combinatory texture
|
|
||||||
aoMetRoughTex = textureBuilder.combine(
|
aoMetRoughTex = textureBuilder.combine(
|
||||||
{
|
{
|
||||||
material.textures[RAW_TEXTURE_USAGE_OCCLUSION],
|
material.textures[RAW_TEXTURE_USAGE_OCCLUSION],
|
||||||
|
@ -288,24 +289,27 @@ ModelData* Raw2Gltf(
|
||||||
[&](const std::vector<const TextureBuilder::pixel*> pixels)
|
[&](const std::vector<const TextureBuilder::pixel*> pixels)
|
||||||
-> TextureBuilder::pixel {
|
-> TextureBuilder::pixel {
|
||||||
const float occlusion = (*pixels[0])[0];
|
const float occlusion = (*pixels[0])[0];
|
||||||
const float metallic = (*pixels[1])[0] * (hasMetallicMap ? 1 : props->metallic);
|
const float metallic = (*pixels[1])[0];
|
||||||
const float roughness =
|
const float roughness = (*pixels[2])[0];
|
||||||
(*pixels[2])[0] * (hasRoughnessMap ? 1 : props->roughness);
|
|
||||||
return {{occlusion,
|
return {{occlusion,
|
||||||
props->invertRoughnessMap ? 1.0f - roughness : roughness,
|
props->invertRoughnessMap ? 1.0f - roughness : roughness,
|
||||||
metallic,
|
metallic,
|
||||||
1}};
|
1}};
|
||||||
},
|
},
|
||||||
false);
|
false);
|
||||||
}
|
if (hasOcclusionMap) {
|
||||||
baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
|
// will only be true if there were actual non-trivial pixels
|
||||||
diffuseFactor = props->diffuseFactor;
|
|
||||||
metallic = props->metallic;
|
|
||||||
roughness = props->roughness;
|
|
||||||
emissiveFactor = props->emissiveFactor;
|
|
||||||
emissiveIntensity = props->emissiveIntensity;
|
|
||||||
// this will set occlusionTexture to null, if no actual occlusion map exists
|
|
||||||
occlusionTexture = aoMetRoughTex.get();
|
occlusionTexture = aoMetRoughTex.get();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// this handles the case of 0 or 1 maps supplied
|
||||||
|
if (hasMetallicMap) {
|
||||||
|
aoMetRoughTex = simpleTex(RAW_TEXTURE_USAGE_METALLIC);
|
||||||
|
} else if (hasRoughnessMap) {
|
||||||
|
aoMetRoughTex = simpleTex(RAW_TEXTURE_USAGE_ROUGHNESS);
|
||||||
|
}
|
||||||
|
// else only occlusion map is possible: that check is handled further below
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
/**
|
/**
|
||||||
* Traditional FBX Material -> PBR Met/Rough glTF.
|
* Traditional FBX Material -> PBR Met/Rough glTF.
|
||||||
|
@ -389,6 +393,7 @@ ModelData* Raw2Gltf(
|
||||||
|
|
||||||
khrCmnUnlitMat.reset(new KHRCmnUnlitMaterial());
|
khrCmnUnlitMat.reset(new KHRCmnUnlitMaterial());
|
||||||
}
|
}
|
||||||
|
// after all the special cases have had a go, check if we need to look up occlusion map
|
||||||
if (!occlusionTexture) {
|
if (!occlusionTexture) {
|
||||||
occlusionTexture = simpleTex(RAW_TEXTURE_USAGE_OCCLUSION).get();
|
occlusionTexture = simpleTex(RAW_TEXTURE_USAGE_OCCLUSION).get();
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,15 +59,13 @@ void to_json(json& j, const PBRMetallicRoughness& d) {
|
||||||
if (d.baseColorFactor.LengthSquared() > 0) {
|
if (d.baseColorFactor.LengthSquared() > 0) {
|
||||||
j["baseColorFactor"] = toStdVec(d.baseColorFactor);
|
j["baseColorFactor"] = toStdVec(d.baseColorFactor);
|
||||||
}
|
}
|
||||||
if (d.metRoughTexture != nullptr) {
|
// we always copy metallic/roughness straight to the glTF:
|
||||||
j["metallicRoughnessTexture"] = *d.metRoughTexture;
|
// - if there's a texture, they're linear multiplier
|
||||||
// if a texture is provided, throw away metallic/roughness values
|
// - if there's no texture, they're constants
|
||||||
j["roughnessFactor"] = 1.0f;
|
|
||||||
j["metallicFactor"] = 1.0f;
|
|
||||||
} else {
|
|
||||||
// without a texture, however, use metallic/roughness as constants
|
|
||||||
j["metallicFactor"] = d.metallic;
|
j["metallicFactor"] = d.metallic;
|
||||||
j["roughnessFactor"] = d.roughness;
|
j["roughnessFactor"] = d.roughness;
|
||||||
|
if (d.metRoughTexture != nullptr) {
|
||||||
|
j["metallicRoughnessTexture"] = *d.metRoughTexture;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue