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) {
|
||||
/**
|
||||
* 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();
|
||||
|
||||
// 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
|
||||
bool hasMetallicMap = material.textures[RAW_TEXTURE_USAGE_METALLIC] >= 0;
|
||||
bool hasRoughnessMap = material.textures[RAW_TEXTURE_USAGE_ROUGHNESS] >= 0;
|
||||
bool hasOcclusionMap = material.textures[RAW_TEXTURE_USAGE_OCCLUSION] >= 0;
|
||||
bool atLeastTwoMaps = hasMetallicMap ? (hasRoughnessMap || hasOcclusionMap)
|
||||
: (hasRoughnessMap && hasMetallicMap);
|
||||
if (!atLeastTwoMaps) {
|
||||
// this handles the case of 0 or 1 maps supplied
|
||||
aoMetRoughTex = hasMetallicMap
|
||||
? simpleTex(RAW_TEXTURE_USAGE_METALLIC)
|
||||
: (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
|
||||
if (atLeastTwoMaps) {
|
||||
// if there's at least two of metallic/roughness/occlusion, it makes sense to
|
||||
// merge them: occlusion into the red channel, metallic into blue channel, and
|
||||
// roughness into the green.
|
||||
aoMetRoughTex = textureBuilder.combine(
|
||||
{
|
||||
material.textures[RAW_TEXTURE_USAGE_OCCLUSION],
|
||||
|
@ -288,24 +289,27 @@ ModelData* Raw2Gltf(
|
|||
[&](const std::vector<const TextureBuilder::pixel*> pixels)
|
||||
-> TextureBuilder::pixel {
|
||||
const float occlusion = (*pixels[0])[0];
|
||||
const float metallic = (*pixels[1])[0] * (hasMetallicMap ? 1 : props->metallic);
|
||||
const float roughness =
|
||||
(*pixels[2])[0] * (hasRoughnessMap ? 1 : props->roughness);
|
||||
const float metallic = (*pixels[1])[0];
|
||||
const float roughness = (*pixels[2])[0];
|
||||
return {{occlusion,
|
||||
props->invertRoughnessMap ? 1.0f - roughness : roughness,
|
||||
metallic,
|
||||
1}};
|
||||
},
|
||||
false);
|
||||
}
|
||||
baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
|
||||
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
|
||||
if (hasOcclusionMap) {
|
||||
// will only be true if there were actual non-trivial pixels
|
||||
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 {
|
||||
/**
|
||||
* Traditional FBX Material -> PBR Met/Rough glTF.
|
||||
|
@ -389,6 +393,7 @@ ModelData* Raw2Gltf(
|
|||
|
||||
khrCmnUnlitMat.reset(new KHRCmnUnlitMaterial());
|
||||
}
|
||||
// after all the special cases have had a go, check if we need to look up occlusion map
|
||||
if (!occlusionTexture) {
|
||||
occlusionTexture = simpleTex(RAW_TEXTURE_USAGE_OCCLUSION).get();
|
||||
}
|
||||
|
|
|
@ -59,15 +59,13 @@ void to_json(json& j, const PBRMetallicRoughness& d) {
|
|||
if (d.baseColorFactor.LengthSquared() > 0) {
|
||||
j["baseColorFactor"] = toStdVec(d.baseColorFactor);
|
||||
}
|
||||
if (d.metRoughTexture != nullptr) {
|
||||
j["metallicRoughnessTexture"] = *d.metRoughTexture;
|
||||
// if a texture is provided, throw away metallic/roughness values
|
||||
j["roughnessFactor"] = 1.0f;
|
||||
j["metallicFactor"] = 1.0f;
|
||||
} else {
|
||||
// without a texture, however, use metallic/roughness as constants
|
||||
// we always copy metallic/roughness straight to the glTF:
|
||||
// - if there's a texture, they're linear multiplier
|
||||
// - if there's no texture, they're constants
|
||||
j["metallicFactor"] = d.metallic;
|
||||
j["roughnessFactor"] = d.roughness;
|
||||
if (d.metRoughTexture != nullptr) {
|
||||
j["metallicRoughnessTexture"] = *d.metRoughTexture;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue