Skip to content

Commit

Permalink
When creating an NRM pack renormalize the normal map and also adjust… (
Browse files Browse the repository at this point in the history
…#49)

When creating an NRM pack renormalize the normal map and also adjust the roughness map based on those normals. Also one more sRGB bug had been lingering.
  • Loading branch information
najadojo committed Jan 16, 2019
1 parent a55c8a2 commit e478643
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 12 deletions.
2 changes: 1 addition & 1 deletion glTF-Toolkit/inc/GLTFTextureCompressionUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ namespace Microsoft::glTF::Toolkit
/// </code>
/// </example>
/// </summary>
static Document CompressTextureAsDDS(std::shared_ptr<IStreamReader> streamReader, const Document & doc, const Texture & texture, TextureCompression compression, const std::string& outputDirectory, size_t maxTextureSize = std::numeric_limits<size_t>::max(), bool generateMipMaps = true, bool retainOriginalImage = true);
static Document CompressTextureAsDDS(std::shared_ptr<IStreamReader> streamReader, const Document & doc, const Texture & texture, TextureCompression compression, const std::string& outputDirectory, size_t maxTextureSize = std::numeric_limits<size_t>::max(), bool generateMipMaps = true, bool retainOriginalImage = true, bool treatAsLinear = true);

/// <summary>
/// Applies <see cref="CompressTextureAsDDS" /> to all textures in the document that are accessible via materials according to the
Expand Down
14 changes: 7 additions & 7 deletions glTF-Toolkit/src/GLTFTextureCompressionUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ using namespace Microsoft::glTF::Toolkit;

const char* Microsoft::glTF::Toolkit::EXTENSION_MSFT_TEXTURE_DDS = "MSFT_texture_dds";

Document GLTFTextureCompressionUtils::CompressTextureAsDDS(std::shared_ptr<IStreamReader> streamReader, const Document & doc, const Texture & texture, TextureCompression compression, const std::string& outputDirectory, size_t maxTextureSize, bool generateMipMaps, bool retainOriginalImage)
Document GLTFTextureCompressionUtils::CompressTextureAsDDS(std::shared_ptr<IStreamReader> streamReader, const Document & doc, const Texture & texture, TextureCompression compression, const std::string& outputDirectory, size_t maxTextureSize, bool generateMipMaps, bool retainOriginalImage, bool treatAsLinear)
{
Document outputDoc(doc);

Expand All @@ -36,7 +36,7 @@ Document GLTFTextureCompressionUtils::CompressTextureAsDDS(std::shared_ptr<IStre
return outputDoc;
}

auto image = std::make_unique<DirectX::ScratchImage>(GLTFTextureUtils::LoadTexture(streamReader, doc, texture.id, compression == TextureCompression::BC7_SRGB ? false : true));
auto image = std::make_unique<DirectX::ScratchImage>(GLTFTextureUtils::LoadTexture(streamReader, doc, texture.id, treatAsLinear));

// Resize up to a multiple of 4
auto metadata = image->GetMetadata();
Expand Down Expand Up @@ -181,17 +181,17 @@ Document GLTFTextureCompressionUtils::CompressAllTexturesForWindowsMR(std::share

for (auto material : doc.materials.Elements())
{
auto compressIfNotEmpty = [&outputDoc, &streamReader, &outputDirectory, maxTextureSize, retainOriginalImages](const std::string& textureId, TextureCompression compression)
auto compressIfNotEmpty = [&outputDoc, &streamReader, &outputDirectory, maxTextureSize, retainOriginalImages](const std::string& textureId, TextureCompression compression, bool treatAsLinear = true)
{
if (!textureId.empty())
{
outputDoc = CompressTextureAsDDS(streamReader, outputDoc, outputDoc.textures.Get(textureId), compression, outputDirectory, maxTextureSize, true, retainOriginalImages);
outputDoc = CompressTextureAsDDS(streamReader, outputDoc, outputDoc.textures.Get(textureId), compression, outputDirectory, maxTextureSize, true, retainOriginalImages, treatAsLinear);
}
};

// Compress base and emissive texture as BC7
compressIfNotEmpty(material.metallicRoughness.baseColorTexture.textureId, TextureCompression::BC7_SRGB);
compressIfNotEmpty(material.emissiveTexture.textureId, TextureCompression::BC7_SRGB);
compressIfNotEmpty(material.metallicRoughness.baseColorTexture.textureId, TextureCompression::BC7_SRGB, false);
compressIfNotEmpty(material.emissiveTexture.textureId, TextureCompression::BC7_SRGB, false);

// Get textures from the MSFT_packing_occlusionRoughnessMetallic extension
if (material.extensions.find(EXTENSION_MSFT_PACKING_ORM) != material.extensions.end())
Expand Down Expand Up @@ -230,7 +230,7 @@ Document GLTFTextureCompressionUtils::CompressAllTexturesForWindowsMR(std::share
if (packingNrmContents.HasMember(MSFT_PACKING_NRM_KEY))
{
auto nrmTextureId = packingNrmContents[MSFT_PACKING_NRM_KEY][MSFT_PACKING_INDEX_KEY].GetInt();
compressIfNotEmpty(std::to_string(nrmTextureId), TextureCompression::BC7);
compressIfNotEmpty(std::to_string(nrmTextureId), TextureCompression::BC7, false); // This tool generates sRGB-packaged images
}
}
}
Expand Down
110 changes: 106 additions & 4 deletions glTF-Toolkit/src/GLTFTexturePackingUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,90 @@ namespace
throw GLTFException("Invalid packing.");
}
}


void Renormalize(std::unique_ptr<DirectX::ScratchImage> &normalImage, DirectX::ScratchImage &renormalizedImage)
{
auto image = normalImage->GetImage(0, 0, 0);
if (FAILED(renormalizedImage.Initialize2D(image->format, image->width, image->height, 1, 1)))
{
throw GLTFException("Failed to initialize from texture.");
}

uint8_t *normalPixels = normalImage->GetPixels();
auto metadata = normalImage->GetMetadata();

auto renormalizedPixels = renormalizedImage.GetPixels();

const auto two = DirectX::XMVectorReplicate(2.0f);
const auto minusOne = DirectX::XMVectorReplicate(-1.0f);
const auto half = DirectX::XMVectorReplicate(0.5f);

for (size_t i = 0; i < metadata.width * metadata.height; i += 1)
{
// renormalizedPixels = 0.5 * normalize(normalPixel * 2 - 1) + 0.5
const auto value = DirectX::XMVectorSet(
*GLTFTextureUtils::GetChannelValue(normalPixels, i, Channel::Red),
*GLTFTextureUtils::GetChannelValue(normalPixels, i, Channel::Green),
*GLTFTextureUtils::GetChannelValue(normalPixels, i, Channel::Blue),
0.0f);

auto normal = DirectX::XMVectorMultiplyAdd(value, two, minusOne);
normal = DirectX::XMVector3Normalize(normal);
const auto result = DirectX::XMVectorMultiplyAdd(half, normal, half);

*GLTFTextureUtils::GetChannelValue(renormalizedPixels, i, Channel::Red) = DirectX::XMVectorGetX(result);
*GLTFTextureUtils::GetChannelValue(renormalizedPixels, i, Channel::Green) = DirectX::XMVectorGetY(result);
*GLTFTextureUtils::GetChannelValue(renormalizedPixels, i, Channel::Blue) = DirectX::XMVectorGetZ(result);
}
}

void AdjustRoughness(std::unique_ptr<DirectX::ScratchImage> &roughnessImage, std::unique_ptr<DirectX::ScratchImage> &normalImage, DirectX::ScratchImage &adjustedImage)
{
auto image = roughnessImage->GetImage(0, 0, 0);
if (FAILED(adjustedImage.Initialize2D(image->format, image->width, image->height, 1, 1)))
{
throw GLTFException("Failed to initialize from texture.");
}

const auto two = DirectX::XMVectorReplicate(2.0f);
const auto minusOne = DirectX::XMVectorReplicate(-1.0f);

uint8_t *normalPixels = normalImage->GetPixels();
auto metadata = normalImage->GetMetadata();

auto adjustedPixels = adjustedImage.GetPixels();
uint8_t *roughnessPixels = roughnessImage->GetPixels();

for (size_t i = 0; i < metadata.width * metadata.height; i += 1)
{
auto normal = DirectX::XMVectorSet(
*GLTFTextureUtils::GetChannelValue(normalPixels, i, Channel::Red),
*GLTFTextureUtils::GetChannelValue(normalPixels, i, Channel::Green),
*GLTFTextureUtils::GetChannelValue(normalPixels, i, Channel::Blue),
0.0f);
normal = DirectX::XMVectorMultiplyAdd(normal, two, minusOne);
auto avgNormalLengthSquare = DirectX::XMVector3LengthSq(normal);
float avgNormalLengthSquareF = DirectX::XMVectorGetX(avgNormalLengthSquare);

float oldRoughness = *GLTFTextureUtils::GetChannelValue(roughnessPixels, i, Channel::Green);
if (avgNormalLengthSquareF < 1.0f)
{
auto avgNormalLength = DirectX::XMVectorSqrt(avgNormalLengthSquare);
float avgNormalLengthF = DirectX::XMVectorGetX(avgNormalLength);
float kappa = (3.0f * avgNormalLengthF - avgNormalLengthF * avgNormalLengthSquareF) / (1.0f - avgNormalLengthSquareF);
float variance = 1.0f / (2.0f * kappa);

float newRoughness = sqrt(oldRoughness * oldRoughness + variance);

*GLTFTextureUtils::GetChannelValue(adjustedPixels, i, Channel::Green) = newRoughness;
}
else
{
*GLTFTextureUtils::GetChannelValue(adjustedPixels, i, Channel::Green) = oldRoughness;
}
}
}
}

std::unordered_set<int> GLTFTexturePackingUtils::GetTextureIndicesFromMsftExtensions(const Material& material)
Expand Down Expand Up @@ -273,6 +357,24 @@ Document GLTFTexturePackingUtils::PackMaterialForWindowsMR(std::shared_ptr<IStre

if (packingIncludesNrm && (hasMR || hasNormal))
{
uint8_t *renormalPixels = normalPixels;
DirectX::ScratchImage renormalizedImage;
uint8_t *roughnessPixels = mrPixels;
DirectX::ScratchImage adjustRoughnessImage;

if (hasNormal)
{
Renormalize(normalImage, renormalizedImage);
renormalPixels = renormalizedImage.GetPixels();

if (hasMR)
{
AdjustRoughness(metallicRoughnessImage, normalImage, adjustRoughnessImage);
roughnessPixels = adjustRoughnessImage.GetPixels();
}
}


DirectX::ScratchImage nrm;

auto sourceImage = hasMR ? *metallicRoughnessImage->GetImage(0, 0, 0) : *normalImage->GetImage(0, 0, 0);
Expand All @@ -287,15 +389,15 @@ Document GLTFTexturePackingUtils::PackMaterialForWindowsMR(std::shared_ptr<IStre
for (size_t i = 0; i < metadata.width * metadata.height; i += 1)
{
// Normal: N [RG] -> NRM [RG]
*GLTFTextureUtils::GetChannelValue(nrmPixels, i, Channel::Red) = hasNormal ? *GLTFTextureUtils::GetChannelValue(normalPixels, i, Channel::Red) : 255.0f;
*GLTFTextureUtils::GetChannelValue(nrmPixels, i, Channel::Green) = hasNormal ? *GLTFTextureUtils::GetChannelValue(normalPixels, i, Channel::Green) : 255.0f;
*GLTFTextureUtils::GetChannelValue(nrmPixels, i, Channel::Red) = hasNormal ? *GLTFTextureUtils::GetChannelValue(renormalPixels, i, Channel::Red) : 255.0f;
*GLTFTextureUtils::GetChannelValue(nrmPixels, i, Channel::Green) = hasNormal ? *GLTFTextureUtils::GetChannelValue(renormalPixels, i, Channel::Green) : 255.0f;
// Roughness: MR [G] -> NRM [B]
*GLTFTextureUtils::GetChannelValue(nrmPixels, i, Channel::Blue) = hasMR ? *GLTFTextureUtils::GetChannelValue(mrPixels, i, Channel::Green) : 255.0f;
*GLTFTextureUtils::GetChannelValue(nrmPixels, i, Channel::Blue) = hasMR ? *GLTFTextureUtils::GetChannelValue(roughnessPixels, i, Channel::Green) : 255.0f;
// Metalness: MR [B] -> NRM [A]
*GLTFTextureUtils::GetChannelValue(nrmPixels, i, Channel::Alpha) = hasMR ? *GLTFTextureUtils::GetChannelValue(mrPixels, i, Channel::Blue) : 255.0f;
}

// sRGB conversion not needed for PNG in BGRA
// Assumed sRGB because PNG defaults to that color space.
auto imagePath = GLTFTextureUtils::SaveAsPng(&nrm, "packing_nrm_" + material.id + ".png", outputDirectory, &GUID_WICPixelFormat32bppBGRA);

// Add back to GLTF
Expand Down

0 comments on commit e478643

Please sign in to comment.