Add support for prebuilt ORM textures
Before this change, the texture assignments for Occlusion, Roughness, and Metalness in the Stingray PBR material path were assumed to be single channel images where the R should be used to build a merged ORM texture. This precluded the use of prebuilt ORM textures. This commit proposes a few changes: * if the same texture is detected in all 3 channels, assume it already is ORM and just pass through. * when combining textures, read R/G/B for O/R/M rather than R/R/R. This allows merging of prebuild ORM textures.
This commit is contained in:
parent
cb9952f5ec
commit
ef974a5e1a
|
@ -255,41 +255,99 @@ ModelData* Raw2Gltf(
|
||||||
*/
|
*/
|
||||||
RawMetRoughMatProps* props = (RawMetRoughMatProps*)material.info.get();
|
RawMetRoughMatProps* props = (RawMetRoughMatProps*)material.info.get();
|
||||||
|
|
||||||
// determine if we need to generate a combined map
|
// determine if we need to generate a combined map, or if we only have 1 map to pass
|
||||||
|
// through
|
||||||
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)
|
|
||||||
: (hasRoughnessMap && hasMetallicMap);
|
auto texturesAreSame = [&](RawTextureUsage a, RawTextureUsage b) -> bool {
|
||||||
if (!atLeastTwoMaps) {
|
// note: at this point the usages will be different, so we can't just compare indexes
|
||||||
// this handles the case of 0 or 1 maps supplied
|
return StringUtils::CompareNoCase(
|
||||||
|
raw.GetTexture(material.textures[a]).fileLocation,
|
||||||
|
raw.GetTexture(material.textures[b]).fileLocation) == 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool isPassThroughTexture = hasOcclusionMap && hasRoughnessMap && hasMetallicMap;
|
||||||
|
if (isPassThroughTexture) {
|
||||||
|
isPassThroughTexture =
|
||||||
|
texturesAreSame(RAW_TEXTURE_USAGE_METALLIC, RAW_TEXTURE_USAGE_ROUGHNESS) &&
|
||||||
|
texturesAreSame(RAW_TEXTURE_USAGE_METALLIC, RAW_TEXTURE_USAGE_OCCLUSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto textureName = [&](RawTextureUsage usage) {
|
||||||
|
int index = material.textures[usage];
|
||||||
|
if (index >= 0) {
|
||||||
|
return raw.GetTexture(index).name.c_str();
|
||||||
|
} else {
|
||||||
|
return "<empty>";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!(hasMetallicMap || hasRoughnessMap || hasOcclusionMap)) {
|
||||||
|
// no data, assume it's a material that just relies on the uniform properties
|
||||||
|
aoMetRoughTex = nullptr;
|
||||||
|
if (verboseOutput) {
|
||||||
|
fmt::printf("Material %s: no ORM textures detected\n", material.name.c_str());
|
||||||
|
}
|
||||||
|
} else if (isPassThroughTexture) {
|
||||||
|
// this handles the case where the same map is assigned to all the channels
|
||||||
aoMetRoughTex = hasMetallicMap
|
aoMetRoughTex = hasMetallicMap
|
||||||
? simpleTex(RAW_TEXTURE_USAGE_METALLIC)
|
? simpleTex(RAW_TEXTURE_USAGE_METALLIC)
|
||||||
: (hasRoughnessMap
|
: (hasRoughnessMap
|
||||||
? simpleTex(RAW_TEXTURE_USAGE_ROUGHNESS)
|
? simpleTex(RAW_TEXTURE_USAGE_ROUGHNESS)
|
||||||
: (hasOcclusionMap ? simpleTex(RAW_TEXTURE_USAGE_OCCLUSION) : nullptr));
|
: (hasOcclusionMap ? simpleTex(RAW_TEXTURE_USAGE_OCCLUSION) : nullptr));
|
||||||
|
if (verboseOutput) {
|
||||||
|
if (aoMetRoughTex) {
|
||||||
|
fmt::printf(
|
||||||
|
"Material %s: detected single ORM texture: %s\n",
|
||||||
|
material.name.c_str(),
|
||||||
|
aoMetRoughTex->name.c_str());
|
||||||
|
} else {
|
||||||
|
fmt::printf("Material %s: no ORM textures detected\n", material.name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// otherwise merge occlusion into the red channel, metallic into blue channel, and
|
/* otherwise we always have to create a new texture that merges
|
||||||
// roughness into the green, of a new combinatory texture
|
* occlusion into the red channel
|
||||||
|
* roughness into the green
|
||||||
|
* metallic into blue channel
|
||||||
|
* with defaults for any unspecified channels
|
||||||
|
*/
|
||||||
aoMetRoughTex = textureBuilder.combine(
|
aoMetRoughTex = textureBuilder.combine(
|
||||||
{
|
{
|
||||||
material.textures[RAW_TEXTURE_USAGE_OCCLUSION],
|
material.textures[RAW_TEXTURE_USAGE_OCCLUSION],
|
||||||
material.textures[RAW_TEXTURE_USAGE_METALLIC],
|
|
||||||
material.textures[RAW_TEXTURE_USAGE_ROUGHNESS],
|
material.textures[RAW_TEXTURE_USAGE_ROUGHNESS],
|
||||||
|
material.textures[RAW_TEXTURE_USAGE_METALLIC],
|
||||||
},
|
},
|
||||||
"ao_met_rough",
|
"ao_met_rough",
|
||||||
[&](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 metallic = (*pixels[1])[0] * (hasMetallicMap ? 1 : props->metallic);
|
* note: we're picking the channels from the sources aligned with where they're
|
||||||
|
* going just in case they were authored that way. This makes an existing ORM
|
||||||
|
* texture "pass through", and has no effect on a grey single type texture.
|
||||||
|
*/
|
||||||
|
const float occlusion = hasOcclusionMap ? (*pixels[0])[0] : 1;
|
||||||
const float roughness =
|
const float roughness =
|
||||||
(*pixels[2])[0] * (hasRoughnessMap ? 1 : props->roughness);
|
(*pixels[1])[1] * (hasRoughnessMap ? 1 : props->roughness);
|
||||||
return {{occlusion,
|
const float metallic = (*pixels[2])[2] * (hasMetallicMap ? 1 : props->metallic);
|
||||||
props->invertRoughnessMap ? 1.0f - roughness : roughness,
|
return {
|
||||||
metallic,
|
{occlusion,
|
||||||
1}};
|
props->invertRoughnessMap ? 1.0f - roughness : roughness,
|
||||||
|
metallic,
|
||||||
|
1}};
|
||||||
},
|
},
|
||||||
false);
|
false);
|
||||||
|
if (aoMetRoughTex && verboseOutput) {
|
||||||
|
fmt::printf(
|
||||||
|
"Material %s: detected multiple ORM textures, combined: [%s, %s, %s] into [%s]\n",
|
||||||
|
material.name.c_str(),
|
||||||
|
textureName(RAW_TEXTURE_USAGE_OCCLUSION),
|
||||||
|
textureName(RAW_TEXTURE_USAGE_ROUGHNESS),
|
||||||
|
textureName(RAW_TEXTURE_USAGE_METALLIC),
|
||||||
|
aoMetRoughTex->name.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
|
baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
|
||||||
diffuseFactor = props->diffuseFactor;
|
diffuseFactor = props->diffuseFactor;
|
||||||
|
@ -806,8 +864,9 @@ ModelData* Raw2Gltf(
|
||||||
extensionsRequired.push_back(KHR_DRACO_MESH_COMPRESSION);
|
extensionsRequired.push_back(KHR_DRACO_MESH_COMPRESSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
json glTFJson{{"asset", {{"generator", "FBX2glTF v" + FBX2GLTF_VERSION}, {"version", "2.0"}}},
|
json glTFJson{
|
||||||
{"scene", rootScene.ix}};
|
{"asset", {{"generator", "FBX2glTF v" + FBX2GLTF_VERSION}, {"version", "2.0"}}},
|
||||||
|
{"scene", rootScene.ix}};
|
||||||
if (!extensionsUsed.empty()) {
|
if (!extensionsUsed.empty()) {
|
||||||
glTFJson["extensionsUsed"] = extensionsUsed;
|
glTFJson["extensionsUsed"] = extensionsUsed;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue