Skip to content

Commit 7c7998c

Browse files
committed
Bulk update: glTF animation pointers, volumetrics, and embedded GPU formats; nv_ktx 1.1.0; direct lighting PDFs; staging upload of > 4 GiB buffers; and more.
* Embedded .DDS/.KTX images inside .glb files can now be loaded. * nv_ktx v1.1.0 adds a readFromMemory() API. * Image loading inside nvvkgltf is now faster; embedded image loading is parallelized, and sorting the order so larger images are loaded first helps on files with lots of images. * glTF KHR_animation_pointer support in nvvkgltf. * singleLightContribution() now returns the PDF for light sampling (for direct lighting MIS). LightContrib::halfAngularSize has also been removed; none of our samples used this. * Henyey-Greenstein volume scattering support in nvvkgltf and bsdf_functions.h.slang. * nvvk::StagingUploader now supports nvvk::LargeBuffers, which use sparse binding for buffers larger than 4 GiB. * In nvvk::GBuffer, depth textures are now created with VK_IMAGE_USAGE_STORAGE_BIT so ray-gen shaders can read/write them. * Fixes the meshopt_decode*Version checks inside nvvkgltf/scene.cpp. * Performance improvement for nvutils::loadFile() by avoiding an intermediate std::vector. Co-authored-by: Martin-Karl Lefrancois <mlefrancois@nvidia.com> Co-authored-by: Jean-Eudes Marvie <jmarvie@nvidia.com> Script sync from SHA: f4e1a1c0ca9aeaba5ca1c18a041ac31a4be9a08e
1 parent 28b7c49 commit 7c7998c

20 files changed

+1599
-265
lines changed

nvimageformats/nv_ktx.cpp

Lines changed: 107 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved.
2+
* Copyright (c) 2021-2026, NVIDIA CORPORATION. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*
16-
* SPDX-FileCopyrightText: Copyright (c) 2021-2025, NVIDIA CORPORATION.
16+
* SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION.
1717
* SPDX-License-Identifier: Apache-2.0
1818
*/
1919

@@ -24,6 +24,7 @@
2424
#include <atomic>
2525
#include <cassert> // Some functions produce assertion errors to assist with debugging when NDEBUG is false.
2626
#include <fstream>
27+
#include <limits>
2728
#include <mutex>
2829
#include <sstream>
2930
#include <string.h> // memcpy
@@ -85,6 +86,99 @@ bool GetNumSubresources(size_t a, size_t b, size_t c, size_t& out)
8586
{
8687
return checked_math::mul3(std::max(a, size_t(1)), std::max(b, size_t(1)), std::max(c, size_t(1)), out);
8788
}
89+
90+
// Supports an std::istream interface that operates on an in-memory array.
91+
// Same as in nv_dds.cpp.
92+
class MemoryStreamBuffer : public std::basic_streambuf<char>
93+
{
94+
char* m_data = nullptr;
95+
std::streamoff m_nextIndex = 0; // Always in [0, m_sizeInBytes].
96+
std::streamsize m_sizeInBytes = 0;
97+
98+
public:
99+
MemoryStreamBuffer(char* data, std::streamsize sizeInBytes)
100+
: m_data(data)
101+
, m_sizeInBytes(sizeInBytes)
102+
{
103+
}
104+
pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override
105+
{
106+
const pos_type indicatesError = pos_type(static_cast<off_type>(-1));
107+
switch(dir)
108+
{
109+
case std::ios_base::beg:
110+
if(off < 0 || off > m_sizeInBytes)
111+
return indicatesError;
112+
m_nextIndex = off;
113+
break;
114+
case std::ios_base::cur:
115+
if(((off < 0) && m_nextIndex + off < 0) || (off >= 0 && off > m_sizeInBytes - m_nextIndex))
116+
return indicatesError;
117+
m_nextIndex += off;
118+
break;
119+
case std::ios_base::end:
120+
if(off > 0 || off < -m_sizeInBytes)
121+
return indicatesError;
122+
m_nextIndex = m_sizeInBytes + off;
123+
break;
124+
default:
125+
return indicatesError;
126+
}
127+
return m_nextIndex;
128+
}
129+
pos_type seekpos(pos_type pos, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override
130+
{
131+
return seekoff(static_cast<off_type>(pos), std::ios_base::beg, which);
132+
}
133+
// Gets the number of characters certainly available.
134+
std::streamsize showmanyc() override { return m_sizeInBytes - m_nextIndex; }
135+
// Gets the next character advancing the read pointer; EOF on error.
136+
int_type uflow() override
137+
{
138+
if(m_nextIndex < m_sizeInBytes)
139+
{
140+
return traits_type::to_int_type(m_data[m_nextIndex++]);
141+
}
142+
return traits_type::eof();
143+
}
144+
// Gets the next character without advancing the read pointer; EOF on error.
145+
int_type underflow() override
146+
{
147+
if(m_nextIndex < m_sizeInBytes)
148+
{
149+
return traits_type::to_int_type(m_data[m_nextIndex]);
150+
}
151+
return traits_type::eof();
152+
}
153+
// Reads a given number of characters and stores them into s' character array.
154+
// Returns the number of characters successfully read.
155+
std::streamsize xsgetn(char_type* s, std::streamsize count) override
156+
{
157+
if(count < 0 || s == nullptr || m_nextIndex >= m_sizeInBytes)
158+
{
159+
return 0;
160+
}
161+
const std::streamsize readableChars = std::min(count, m_sizeInBytes - m_nextIndex);
162+
memcpy(s, m_data + m_nextIndex, readableChars);
163+
m_nextIndex += readableChars;
164+
return readableChars;
165+
}
166+
};
167+
168+
// An std::istream interface for a constant, in-memory array.
169+
// Note that Clang emits a Wreorder-ctor warning unless the memory buffer
170+
// members are listed before the istream, so we use multiple inheritance
171+
// here to put them in the right order.
172+
class MemoryStream : private MemoryStreamBuffer, public std::istream
173+
{
174+
public:
175+
MemoryStream(const char* data, std::streamsize sizeInBytes)
176+
: MemoryStreamBuffer(const_cast<char*>(data), sizeInBytes)
177+
, std::istream(this)
178+
{
179+
rdbuf(this);
180+
}
181+
};
88182
} // namespace
89183

90184
ErrorWithText KTXImage::allocate(uint32_t _num_mips, uint32_t _num_layers, uint32_t _num_faces)
@@ -2911,7 +3005,7 @@ ErrorWithText KTXImage::writeKTX2Stream(std::ostream& output, const WriteSetting
29113005
// Fill in the KTXwriter field if it's not already assigned.
29123006
if(key_value_data.find("KTXwriter") == key_value_data.end())
29133007
{
2914-
key_value_data["KTXwriter"] = StringToCharVector("nvpro-samples' nv_ktx version 1.0.1");
3008+
key_value_data["KTXwriter"] = StringToCharVector("nvpro-samples' nv_ktx version 1.1.0");
29153009
}
29163010

29173011
// We now know the offset of the key/value data.
@@ -3203,6 +3297,16 @@ ErrorWithText KTXImage::readFromFile(const char* filename, const ReadSettings& r
32033297
return readFromStream(input_stream, readSettings);
32043298
}
32053299

3300+
ErrorWithText KTXImage::readFromMemory(const char* buffer, size_t bufferSize, const ReadSettings& readSettings)
3301+
{
3302+
if(bufferSize > static_cast<size_t>(std::numeric_limits<std::streamsize>::max()))
3303+
{
3304+
return "The `bufferSize` parameter was too large to be stored in an std::streamsize.";
3305+
}
3306+
MemoryStream stream(buffer, static_cast<std::streamsize>(bufferSize));
3307+
return readFromStream(stream, readSettings);
3308+
}
3309+
32063310
} // namespace nv_ktx
32073311

32083312
//-----------------------------------------------------------------------------

nvimageformats/nv_ktx.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved.
2+
* Copyright (c) 2021-2026, NVIDIA CORPORATION. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -13,13 +13,13 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*
16-
* SPDX-FileCopyrightText: Copyright (c) 2021-2025, NVIDIA CORPORATION.
16+
* SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION.
1717
* SPDX-License-Identifier: Apache-2.0
1818
*/
1919

2020
/*-----------------------------------------------------------------------------
2121
22-
nv_ktx 1.0.1
22+
nv_ktx 1.1.0
2323
2424
This is a mostly self-contained reader and writer for KTX2 files and reader
2525
for KTX1 files. It only relies on Vulkan (for KTX2), GL (for KTX1), and the
@@ -209,6 +209,11 @@ struct KTXImage
209209
ErrorWithText readFromFile(const char* filename, // The .ktx or .ktx2 file to read from.
210210
const ReadSettings& readSettings); // Settings for the reader
211211

212+
// Wrapper for readFromStream for a buffer in memory.
213+
ErrorWithText readFromMemory(const char* buffer, // The buffer in memory.
214+
size_t bufferSize, // Its length in bytes.
215+
const ReadSettings& readSettings); // Settings for the reader.
216+
212217
// Writes this structure in KTX2 format to a stream.
213218
ErrorWithText writeKTX2Stream(std::ostream& output, // The output stream, at the point to start writing
214219
const WriteSettings& writeSettings); // Settings for the writer

nvshaders/bsdf_functions.h.slang

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019-2025, NVIDIA CORPORATION. All rights reserved.
2+
* Copyright (c) 2019-2026, NVIDIA CORPORATION. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*
16-
* SPDX-FileCopyrightText: Copyright (c) 2019-2025, NVIDIA CORPORATION.
16+
* SPDX-FileCopyrightText: Copyright (c) 2019-2026, NVIDIA CORPORATION.
1717
* SPDX-License-Identifier: Apache-2.0
1818
*/
1919

@@ -117,6 +117,80 @@ float3 absorptionCoefficient(PbrMaterial mat)
117117
return -log(max(mat.attenuationColor, float3(0.001))) / max(mat.attenuationDistance, 0.001);
118118
}
119119

120+
/*-------------------------------------------------------------------------------------------------
121+
# Function `henyeyGreenstein`
122+
> Henyey-Greenstein phase function for volumetric scattering.
123+
124+
Parameters:
125+
- cosTheta: cosine of the angle between incident and scattered directions
126+
- g: anisotropy parameter in (-1, 1). g > 0 = forward scatter, g < 0 = back scatter, g = 0 = isotropic
127+
Returns the phase function value (probability density).
128+
Ref: https://www.pbr-book.org/4ed/Volume_Scattering/Phase_Functions#TheHenyeyndashGreensteinPhaseFunction
129+
-------------------------------------------------------------------------------------------------*/
130+
float henyeyGreenstein(float cosTheta, float g)
131+
{
132+
float g2 = g * g;
133+
float denom = 1.0 + g2 - 2.0 * g * cosTheta;
134+
return M_1_PI * 0.25 * (1.0 - g2) / (denom * sqrt(denom));
135+
}
136+
137+
/*-------------------------------------------------------------------------------------------------
138+
# Function `sampleHenyeyGreenstein`
139+
> Samples a direction from the Henyey-Greenstein phase function distribution.
140+
141+
Parameters:
142+
- xi: two random numbers in [0,1]
143+
- g: anisotropy parameter in (-1, 1)
144+
- wi: normalized incident direction (the direction light/ray is traveling)
145+
146+
Returns the sampled scattered direction.
147+
For forward scattering (g > 0), result tends to align with wi (continue forward).
148+
For back scattering (g < 0), result tends to point opposite to wi (bounce back).
149+
Ref: https://www.pbr-book.org/4ed/Volume_Scattering/Phase_Functions#TheHenyeyndashGreensteinPhaseFunction
150+
-------------------------------------------------------------------------------------------------*/
151+
float3 sampleHenyeyGreenstein(float2 xi, float g, float3 wi)
152+
{
153+
float cosTheta;
154+
if(abs(g) < 0.001)
155+
{
156+
// Isotropic scattering
157+
cosTheta = 1.0 - 2.0 * xi.x;
158+
}
159+
else
160+
{
161+
float t = (1.0 - g * g) / (1.0 - g + 2.0 * g * xi.x);
162+
cosTheta = (1.0 + g * g - t * t) / (2.0 * g);
163+
}
164+
165+
float sinTheta = sqrt(max(0.0, 1.0 - cosTheta * cosTheta));
166+
float phi = 2.0 * M_PI * xi.y;
167+
168+
// Build orthonormal basis around wi (the direction light is traveling)
169+
// For forward scattering (g > 0), result will be close to wi
170+
float3 u, v;
171+
orthonormalBasis(wi, u, v);
172+
173+
// Convert spherical to Cartesian in the local frame
174+
return sinTheta * cos(phi) * u + sinTheta * sin(phi) * v + cosTheta * wi;
175+
}
176+
177+
/*-------------------------------------------------------------------------------------------------
178+
# Function `henyeyGreensteinPdf`
179+
> Returns the PDF for sampling a direction using the Henyey-Greenstein phase function.
180+
181+
This is the same as henyeyGreenstein() since sampleHenyeyGreenstein() does perfect sampling and
182+
henyeyGreenstein() is already normalized.
183+
-------------------------------------------------------------------------------------------------*/
184+
float henyeyGreensteinPdf(float cosTheta, float g)
185+
{
186+
float pdf = henyeyGreenstein(cosTheta, g);
187+
// Near |g| = 1, HG approaches a delta distribution;
188+
// catch numerical edge cases (0, NaN, inf) to avoid division by zero or NaN.
189+
if(pdf == 0.0f || isnan(pdf) || isinf(pdf))
190+
pdf = DIRAC;
191+
return pdf;
192+
}
193+
120194
/*-------------------------------------------------------------------------------------------------
121195
# `LOBE_*` Defines
122196
> Indices for lobe weights returned by `computeLobeWeights()`.

nvshaders/gltf_scene_io.h.slang

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved.
2+
* Copyright (c) 2022-2026, NVIDIA CORPORATION. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*
16-
* SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION.
16+
* SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION.
1717
* SPDX-License-Identifier: Apache-2.0
1818
*/
1919

@@ -150,31 +150,34 @@ struct GltfShadeMaterial
150150
float3 diffuseTransmissionColor; // offset 192 - 12 bytes
151151
float diffuseTransmissionFactor; // offset 204 - 4 bytes
152152

153-
int doubleSided; // offset 208 - 4 bytes
153+
float3 multiscatterColor; // offset 208 - 12 bytes // KHR_materials_volume_scatter
154+
float scatterAnisotropy; // offset 220 - 4 bytes // KHR_materials_volume_scatter
155+
156+
int doubleSided; // offset 224 - 4 bytes
154157
// Texture infos (uint16_t, 2 bytes each)
155-
uint16_t pbrBaseColorTexture; // offset 212 - 2 bytes
156-
uint16_t normalTexture; // offset 214 - 2 bytes
157-
uint16_t pbrMetallicRoughnessTexture; // offset 216 - 2 bytes
158-
uint16_t emissiveTexture; // offset 218 - 2 bytes
159-
uint16_t transmissionTexture; // offset 220 - 2 bytes
160-
uint16_t thicknessTexture; // offset 222 - 2 bytes
161-
uint16_t clearcoatTexture; // offset 224 - 2 bytes
162-
uint16_t clearcoatRoughnessTexture; // offset 226 - 2 bytes
163-
uint16_t clearcoatNormalTexture; // offset 228 - 2 bytes
164-
uint16_t specularTexture; // offset 230 - 2 bytes
165-
uint16_t specularColorTexture; // offset 232 - 2 bytes
166-
uint16_t iridescenceTexture; // offset 234 - 2 bytes
167-
uint16_t iridescenceThicknessTexture; // offset 236 - 2 bytes
168-
uint16_t anisotropyTexture; // offset 238 - 2 bytes
169-
uint16_t sheenColorTexture; // offset 240 - 2 bytes
170-
uint16_t sheenRoughnessTexture; // offset 242 - 2 bytes
171-
uint16_t occlusionTexture; // offset 244 - 2 bytes
172-
uint16_t pbrDiffuseTexture; // offset 246 - 2 bytes
173-
uint16_t pbrSpecularGlossinessTexture; // offset 248 - 2 bytes
174-
uint16_t diffuseTransmissionTexture; // offset 250 - 2 bytes
175-
uint16_t diffuseTransmissionColorTexture; // offset 252 - 2 bytes
176-
uint16_t _pad1; // offset 254 - 2 bytes
177-
// Total size: 256 bytes
158+
uint16_t pbrBaseColorTexture; // offset 228 - 2 bytes
159+
uint16_t normalTexture; // offset 230 - 2 bytes
160+
uint16_t pbrMetallicRoughnessTexture; // offset 232 - 2 bytes
161+
uint16_t emissiveTexture; // offset 234 - 2 bytes
162+
uint16_t transmissionTexture; // offset 236 - 2 bytes
163+
uint16_t thicknessTexture; // offset 238 - 2 bytes
164+
uint16_t clearcoatTexture; // offset 240 - 2 bytes
165+
uint16_t clearcoatRoughnessTexture; // offset 242 - 2 bytes
166+
uint16_t clearcoatNormalTexture; // offset 244 - 2 bytes
167+
uint16_t specularTexture; // offset 246 - 2 bytes
168+
uint16_t specularColorTexture; // offset 248 - 2 bytes
169+
uint16_t iridescenceTexture; // offset 250 - 2 bytes
170+
uint16_t iridescenceThicknessTexture; // offset 252 - 2 bytes
171+
uint16_t anisotropyTexture; // offset 254 - 2 bytes
172+
uint16_t sheenColorTexture; // offset 256 - 2 bytes
173+
uint16_t sheenRoughnessTexture; // offset 258 - 2 bytes
174+
uint16_t occlusionTexture; // offset 260 - 2 bytes
175+
uint16_t pbrDiffuseTexture; // offset 262 - 2 bytes
176+
uint16_t pbrSpecularGlossinessTexture; // offset 264 - 2 bytes
177+
uint16_t diffuseTransmissionTexture; // offset 266 - 2 bytes
178+
uint16_t diffuseTransmissionColorTexture; // offset 268 - 2 bytes
179+
uint16_t _pad1; // offset 270 - 2 bytes
180+
// Total size: 272 bytes
178181
};
179182

180183
inline GltfTextureInfo defaultGltfTextureInfo()
@@ -222,6 +225,8 @@ inline GltfShadeMaterial defaultGltfMaterial()
222225
m.pbrGlossinessFactor = 1;
223226
m.diffuseTransmissionColor = float3(1, 1, 1);
224227
m.diffuseTransmissionFactor = 0;
228+
m.multiscatterColor = float3(0, 0, 0);
229+
m.scatterAnisotropy = 0;
225230
m.doubleSided = 0;
226231

227232
m.pbrBaseColorTexture = -1;

0 commit comments

Comments
 (0)