From e4786433c524c0120697ce3326cef2e6c0cc89b4 Mon Sep 17 00:00:00 2001 From: Jamie Marconi Date: Wed, 16 Jan 2019 12:56:28 -0800 Subject: [PATCH] =?UTF-8?q?When=20creating=20an=20NRM=20pack=20renormalize?= =?UTF-8?q?=20the=20normal=20map=20and=20also=20adjust=E2=80=A6=20(#49)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .../inc/GLTFTextureCompressionUtils.h | 2 +- .../src/GLTFTextureCompressionUtils.cpp | 14 +-- glTF-Toolkit/src/GLTFTexturePackingUtils.cpp | 110 +++++++++++++++++- 3 files changed, 114 insertions(+), 12 deletions(-) diff --git a/glTF-Toolkit/inc/GLTFTextureCompressionUtils.h b/glTF-Toolkit/inc/GLTFTextureCompressionUtils.h index 49d2f5a..0dcda33 100644 --- a/glTF-Toolkit/inc/GLTFTextureCompressionUtils.h +++ b/glTF-Toolkit/inc/GLTFTextureCompressionUtils.h @@ -84,7 +84,7 @@ namespace Microsoft::glTF::Toolkit /// /// /// - static Document CompressTextureAsDDS(std::shared_ptr streamReader, const Document & doc, const Texture & texture, TextureCompression compression, const std::string& outputDirectory, size_t maxTextureSize = std::numeric_limits::max(), bool generateMipMaps = true, bool retainOriginalImage = true); + static Document CompressTextureAsDDS(std::shared_ptr streamReader, const Document & doc, const Texture & texture, TextureCompression compression, const std::string& outputDirectory, size_t maxTextureSize = std::numeric_limits::max(), bool generateMipMaps = true, bool retainOriginalImage = true, bool treatAsLinear = true); /// /// Applies to all textures in the document that are accessible via materials according to the diff --git a/glTF-Toolkit/src/GLTFTextureCompressionUtils.cpp b/glTF-Toolkit/src/GLTFTextureCompressionUtils.cpp index b1a221c..bda847b 100644 --- a/glTF-Toolkit/src/GLTFTextureCompressionUtils.cpp +++ b/glTF-Toolkit/src/GLTFTextureCompressionUtils.cpp @@ -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 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 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); @@ -36,7 +36,7 @@ Document GLTFTextureCompressionUtils::CompressTextureAsDDS(std::shared_ptr(GLTFTextureUtils::LoadTexture(streamReader, doc, texture.id, compression == TextureCompression::BC7_SRGB ? false : true)); + auto image = std::make_unique(GLTFTextureUtils::LoadTexture(streamReader, doc, texture.id, treatAsLinear)); // Resize up to a multiple of 4 auto metadata = image->GetMetadata(); @@ -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()) @@ -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 } } } diff --git a/glTF-Toolkit/src/GLTFTexturePackingUtils.cpp b/glTF-Toolkit/src/GLTFTexturePackingUtils.cpp index 2f671f5..ec0fd45 100644 --- a/glTF-Toolkit/src/GLTFTexturePackingUtils.cpp +++ b/glTF-Toolkit/src/GLTFTexturePackingUtils.cpp @@ -46,6 +46,90 @@ namespace throw GLTFException("Invalid packing."); } } + + + void Renormalize(std::unique_ptr &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 &roughnessImage, std::unique_ptr &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 GLTFTexturePackingUtils::GetTextureIndicesFromMsftExtensions(const Material& material) @@ -273,6 +357,24 @@ Document GLTFTexturePackingUtils::PackMaterialForWindowsMR(std::shared_ptrGetImage(0, 0, 0) : *normalImage->GetImage(0, 0, 0); @@ -287,15 +389,15 @@ Document GLTFTexturePackingUtils::PackMaterialForWindowsMR(std::shared_ptr 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