Skip to content

Commit e478643

Browse files
authored
When creating an NRM pack renormalize the normal map and also adjust… (#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.
1 parent a55c8a2 commit e478643

File tree

3 files changed

+114
-12
lines changed

3 files changed

+114
-12
lines changed

glTF-Toolkit/inc/GLTFTextureCompressionUtils.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ namespace Microsoft::glTF::Toolkit
8484
/// </code>
8585
/// </example>
8686
/// </summary>
87-
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);
87+
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);
8888

8989
/// <summary>
9090
/// Applies <see cref="CompressTextureAsDDS" /> to all textures in the document that are accessible via materials according to the

glTF-Toolkit/src/GLTFTextureCompressionUtils.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ using namespace Microsoft::glTF::Toolkit;
2020

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

23-
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)
23+
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)
2424
{
2525
Document outputDoc(doc);
2626

@@ -36,7 +36,7 @@ Document GLTFTextureCompressionUtils::CompressTextureAsDDS(std::shared_ptr<IStre
3636
return outputDoc;
3737
}
3838

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

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

182182
for (auto material : doc.materials.Elements())
183183
{
184-
auto compressIfNotEmpty = [&outputDoc, &streamReader, &outputDirectory, maxTextureSize, retainOriginalImages](const std::string& textureId, TextureCompression compression)
184+
auto compressIfNotEmpty = [&outputDoc, &streamReader, &outputDirectory, maxTextureSize, retainOriginalImages](const std::string& textureId, TextureCompression compression, bool treatAsLinear = true)
185185
{
186186
if (!textureId.empty())
187187
{
188-
outputDoc = CompressTextureAsDDS(streamReader, outputDoc, outputDoc.textures.Get(textureId), compression, outputDirectory, maxTextureSize, true, retainOriginalImages);
188+
outputDoc = CompressTextureAsDDS(streamReader, outputDoc, outputDoc.textures.Get(textureId), compression, outputDirectory, maxTextureSize, true, retainOriginalImages, treatAsLinear);
189189
}
190190
};
191191

192192
// Compress base and emissive texture as BC7
193-
compressIfNotEmpty(material.metallicRoughness.baseColorTexture.textureId, TextureCompression::BC7_SRGB);
194-
compressIfNotEmpty(material.emissiveTexture.textureId, TextureCompression::BC7_SRGB);
193+
compressIfNotEmpty(material.metallicRoughness.baseColorTexture.textureId, TextureCompression::BC7_SRGB, false);
194+
compressIfNotEmpty(material.emissiveTexture.textureId, TextureCompression::BC7_SRGB, false);
195195

196196
// Get textures from the MSFT_packing_occlusionRoughnessMetallic extension
197197
if (material.extensions.find(EXTENSION_MSFT_PACKING_ORM) != material.extensions.end())
@@ -230,7 +230,7 @@ Document GLTFTextureCompressionUtils::CompressAllTexturesForWindowsMR(std::share
230230
if (packingNrmContents.HasMember(MSFT_PACKING_NRM_KEY))
231231
{
232232
auto nrmTextureId = packingNrmContents[MSFT_PACKING_NRM_KEY][MSFT_PACKING_INDEX_KEY].GetInt();
233-
compressIfNotEmpty(std::to_string(nrmTextureId), TextureCompression::BC7);
233+
compressIfNotEmpty(std::to_string(nrmTextureId), TextureCompression::BC7, false); // This tool generates sRGB-packaged images
234234
}
235235
}
236236
}

glTF-Toolkit/src/GLTFTexturePackingUtils.cpp

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,90 @@ namespace
4646
throw GLTFException("Invalid packing.");
4747
}
4848
}
49+
50+
51+
void Renormalize(std::unique_ptr<DirectX::ScratchImage> &normalImage, DirectX::ScratchImage &renormalizedImage)
52+
{
53+
auto image = normalImage->GetImage(0, 0, 0);
54+
if (FAILED(renormalizedImage.Initialize2D(image->format, image->width, image->height, 1, 1)))
55+
{
56+
throw GLTFException("Failed to initialize from texture.");
57+
}
58+
59+
uint8_t *normalPixels = normalImage->GetPixels();
60+
auto metadata = normalImage->GetMetadata();
61+
62+
auto renormalizedPixels = renormalizedImage.GetPixels();
63+
64+
const auto two = DirectX::XMVectorReplicate(2.0f);
65+
const auto minusOne = DirectX::XMVectorReplicate(-1.0f);
66+
const auto half = DirectX::XMVectorReplicate(0.5f);
67+
68+
for (size_t i = 0; i < metadata.width * metadata.height; i += 1)
69+
{
70+
// renormalizedPixels = 0.5 * normalize(normalPixel * 2 - 1) + 0.5
71+
const auto value = DirectX::XMVectorSet(
72+
*GLTFTextureUtils::GetChannelValue(normalPixels, i, Channel::Red),
73+
*GLTFTextureUtils::GetChannelValue(normalPixels, i, Channel::Green),
74+
*GLTFTextureUtils::GetChannelValue(normalPixels, i, Channel::Blue),
75+
0.0f);
76+
77+
auto normal = DirectX::XMVectorMultiplyAdd(value, two, minusOne);
78+
normal = DirectX::XMVector3Normalize(normal);
79+
const auto result = DirectX::XMVectorMultiplyAdd(half, normal, half);
80+
81+
*GLTFTextureUtils::GetChannelValue(renormalizedPixels, i, Channel::Red) = DirectX::XMVectorGetX(result);
82+
*GLTFTextureUtils::GetChannelValue(renormalizedPixels, i, Channel::Green) = DirectX::XMVectorGetY(result);
83+
*GLTFTextureUtils::GetChannelValue(renormalizedPixels, i, Channel::Blue) = DirectX::XMVectorGetZ(result);
84+
}
85+
}
86+
87+
void AdjustRoughness(std::unique_ptr<DirectX::ScratchImage> &roughnessImage, std::unique_ptr<DirectX::ScratchImage> &normalImage, DirectX::ScratchImage &adjustedImage)
88+
{
89+
auto image = roughnessImage->GetImage(0, 0, 0);
90+
if (FAILED(adjustedImage.Initialize2D(image->format, image->width, image->height, 1, 1)))
91+
{
92+
throw GLTFException("Failed to initialize from texture.");
93+
}
94+
95+
const auto two = DirectX::XMVectorReplicate(2.0f);
96+
const auto minusOne = DirectX::XMVectorReplicate(-1.0f);
97+
98+
uint8_t *normalPixels = normalImage->GetPixels();
99+
auto metadata = normalImage->GetMetadata();
100+
101+
auto adjustedPixels = adjustedImage.GetPixels();
102+
uint8_t *roughnessPixels = roughnessImage->GetPixels();
103+
104+
for (size_t i = 0; i < metadata.width * metadata.height; i += 1)
105+
{
106+
auto normal = DirectX::XMVectorSet(
107+
*GLTFTextureUtils::GetChannelValue(normalPixels, i, Channel::Red),
108+
*GLTFTextureUtils::GetChannelValue(normalPixels, i, Channel::Green),
109+
*GLTFTextureUtils::GetChannelValue(normalPixels, i, Channel::Blue),
110+
0.0f);
111+
normal = DirectX::XMVectorMultiplyAdd(normal, two, minusOne);
112+
auto avgNormalLengthSquare = DirectX::XMVector3LengthSq(normal);
113+
float avgNormalLengthSquareF = DirectX::XMVectorGetX(avgNormalLengthSquare);
114+
115+
float oldRoughness = *GLTFTextureUtils::GetChannelValue(roughnessPixels, i, Channel::Green);
116+
if (avgNormalLengthSquareF < 1.0f)
117+
{
118+
auto avgNormalLength = DirectX::XMVectorSqrt(avgNormalLengthSquare);
119+
float avgNormalLengthF = DirectX::XMVectorGetX(avgNormalLength);
120+
float kappa = (3.0f * avgNormalLengthF - avgNormalLengthF * avgNormalLengthSquareF) / (1.0f - avgNormalLengthSquareF);
121+
float variance = 1.0f / (2.0f * kappa);
122+
123+
float newRoughness = sqrt(oldRoughness * oldRoughness + variance);
124+
125+
*GLTFTextureUtils::GetChannelValue(adjustedPixels, i, Channel::Green) = newRoughness;
126+
}
127+
else
128+
{
129+
*GLTFTextureUtils::GetChannelValue(adjustedPixels, i, Channel::Green) = oldRoughness;
130+
}
131+
}
132+
}
49133
}
50134

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

274358
if (packingIncludesNrm && (hasMR || hasNormal))
275359
{
360+
uint8_t *renormalPixels = normalPixels;
361+
DirectX::ScratchImage renormalizedImage;
362+
uint8_t *roughnessPixels = mrPixels;
363+
DirectX::ScratchImage adjustRoughnessImage;
364+
365+
if (hasNormal)
366+
{
367+
Renormalize(normalImage, renormalizedImage);
368+
renormalPixels = renormalizedImage.GetPixels();
369+
370+
if (hasMR)
371+
{
372+
AdjustRoughness(metallicRoughnessImage, normalImage, adjustRoughnessImage);
373+
roughnessPixels = adjustRoughnessImage.GetPixels();
374+
}
375+
}
376+
377+
276378
DirectX::ScratchImage nrm;
277379

278380
auto sourceImage = hasMR ? *metallicRoughnessImage->GetImage(0, 0, 0) : *normalImage->GetImage(0, 0, 0);
@@ -287,15 +389,15 @@ Document GLTFTexturePackingUtils::PackMaterialForWindowsMR(std::shared_ptr<IStre
287389
for (size_t i = 0; i < metadata.width * metadata.height; i += 1)
288390
{
289391
// Normal: N [RG] -> NRM [RG]
290-
*GLTFTextureUtils::GetChannelValue(nrmPixels, i, Channel::Red) = hasNormal ? *GLTFTextureUtils::GetChannelValue(normalPixels, i, Channel::Red) : 255.0f;
291-
*GLTFTextureUtils::GetChannelValue(nrmPixels, i, Channel::Green) = hasNormal ? *GLTFTextureUtils::GetChannelValue(normalPixels, i, Channel::Green) : 255.0f;
392+
*GLTFTextureUtils::GetChannelValue(nrmPixels, i, Channel::Red) = hasNormal ? *GLTFTextureUtils::GetChannelValue(renormalPixels, i, Channel::Red) : 255.0f;
393+
*GLTFTextureUtils::GetChannelValue(nrmPixels, i, Channel::Green) = hasNormal ? *GLTFTextureUtils::GetChannelValue(renormalPixels, i, Channel::Green) : 255.0f;
292394
// Roughness: MR [G] -> NRM [B]
293-
*GLTFTextureUtils::GetChannelValue(nrmPixels, i, Channel::Blue) = hasMR ? *GLTFTextureUtils::GetChannelValue(mrPixels, i, Channel::Green) : 255.0f;
395+
*GLTFTextureUtils::GetChannelValue(nrmPixels, i, Channel::Blue) = hasMR ? *GLTFTextureUtils::GetChannelValue(roughnessPixels, i, Channel::Green) : 255.0f;
294396
// Metalness: MR [B] -> NRM [A]
295397
*GLTFTextureUtils::GetChannelValue(nrmPixels, i, Channel::Alpha) = hasMR ? *GLTFTextureUtils::GetChannelValue(mrPixels, i, Channel::Blue) : 255.0f;
296398
}
297399

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

301403
// Add back to GLTF

0 commit comments

Comments
 (0)