Derive gaussian splat SH order from resource layout
This commit is contained in:
@@ -43,6 +43,11 @@ enum class GaussianSplatSectionFormat : Core::uint32 {
|
|||||||
};
|
};
|
||||||
|
|
||||||
constexpr Core::uint32 kGaussianSplatSHCoefficientCount = 45;
|
constexpr Core::uint32 kGaussianSplatSHCoefficientCount = 45;
|
||||||
|
constexpr Core::uint32 kGaussianSplatSHColorChannelCount = 3;
|
||||||
|
constexpr Core::uint32 kGaussianSplatMaxSHOrder = 3;
|
||||||
|
|
||||||
|
Core::uint32 ResolveGaussianSplatSHOrderFromCoefficientCount(Core::uint32 coefficientCount);
|
||||||
|
Core::uint32 ResolveGaussianSplatSHOrderFromSectionStride(Core::uint32 elementStride);
|
||||||
|
|
||||||
struct GaussianSplatPositionRecord {
|
struct GaussianSplatPositionRecord {
|
||||||
Math::Vector3 position = Math::Vector3::Zero();
|
Math::Vector3 position = Math::Vector3::Zero();
|
||||||
@@ -113,6 +118,7 @@ public:
|
|||||||
GaussianSplatSectionFormat GetOtherFormat() const { return m_metadata.otherFormat; }
|
GaussianSplatSectionFormat GetOtherFormat() const { return m_metadata.otherFormat; }
|
||||||
GaussianSplatSectionFormat GetColorFormat() const { return m_metadata.colorFormat; }
|
GaussianSplatSectionFormat GetColorFormat() const { return m_metadata.colorFormat; }
|
||||||
GaussianSplatSectionFormat GetSHFormat() const { return m_metadata.shFormat; }
|
GaussianSplatSectionFormat GetSHFormat() const { return m_metadata.shFormat; }
|
||||||
|
Core::uint32 GetSHOrder() const;
|
||||||
GaussianSplatSectionFormat GetChunkFormat() const { return m_metadata.chunkFormat; }
|
GaussianSplatSectionFormat GetChunkFormat() const { return m_metadata.chunkFormat; }
|
||||||
GaussianSplatSectionFormat GetCameraFormat() const { return m_metadata.cameraFormat; }
|
GaussianSplatSectionFormat GetCameraFormat() const { return m_metadata.cameraFormat; }
|
||||||
const Containers::Array<GaussianSplatSection>& GetSections() const { return m_sections; }
|
const Containers::Array<GaussianSplatSection>& GetSections() const { return m_sections; }
|
||||||
|
|||||||
@@ -1064,6 +1064,11 @@ bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
|
|||||||
|
|
||||||
commandList->SetPipelineState(pipelineState);
|
commandList->SetPipelineState(pipelineState);
|
||||||
|
|
||||||
|
const float shOrder =
|
||||||
|
visibleGaussianSplat.gaussianSplat != nullptr
|
||||||
|
? static_cast<float>(visibleGaussianSplat.gaussianSplat->GetSHOrder())
|
||||||
|
: 0.0f;
|
||||||
|
|
||||||
const PerObjectConstants perObjectConstants = {
|
const PerObjectConstants perObjectConstants = {
|
||||||
sceneData.cameraData.projection,
|
sceneData.cameraData.projection,
|
||||||
sceneData.cameraData.view,
|
sceneData.cameraData.view,
|
||||||
@@ -1080,7 +1085,7 @@ bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
|
|||||||
Math::Vector4(
|
Math::Vector4(
|
||||||
static_cast<float>(cachedGaussianSplat->splatCount),
|
static_cast<float>(cachedGaussianSplat->splatCount),
|
||||||
static_cast<float>(workingSet->sortCapacity),
|
static_cast<float>(workingSet->sortCapacity),
|
||||||
3.0f,
|
shOrder,
|
||||||
0.0f)
|
0.0f)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,38 @@
|
|||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace Resources {
|
namespace Resources {
|
||||||
|
|
||||||
|
Core::uint32 ResolveGaussianSplatSHOrderFromCoefficientCount(Core::uint32 coefficientCount) {
|
||||||
|
if (coefficientCount == 0u || (coefficientCount % kGaussianSplatSHColorChannelCount) != 0u) {
|
||||||
|
return 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::uint32 remainingTriplets = coefficientCount / kGaussianSplatSHColorChannelCount;
|
||||||
|
Core::uint32 resolvedOrder = 0u;
|
||||||
|
for (Core::uint32 order = 1u; order <= kGaussianSplatMaxSHOrder; ++order) {
|
||||||
|
const Core::uint32 bandTriplets = (2u * order) + 1u;
|
||||||
|
if (remainingTriplets < bandTriplets) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
remainingTriplets -= bandTriplets;
|
||||||
|
resolvedOrder = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolvedOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::uint32 ResolveGaussianSplatSHOrderFromSectionStride(Core::uint32 elementStride) {
|
||||||
|
if (elementStride < sizeof(float)) {
|
||||||
|
return 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Core::uint32 floatCount = elementStride / static_cast<Core::uint32>(sizeof(float));
|
||||||
|
const Core::uint32 coefficientCount = floatCount > kGaussianSplatSHCoefficientCount
|
||||||
|
? kGaussianSplatSHCoefficientCount
|
||||||
|
: floatCount;
|
||||||
|
return ResolveGaussianSplatSHOrderFromCoefficientCount(coefficientCount);
|
||||||
|
}
|
||||||
|
|
||||||
GaussianSplat::GaussianSplat() = default;
|
GaussianSplat::GaussianSplat() = default;
|
||||||
|
|
||||||
GaussianSplat::~GaussianSplat() = default;
|
GaussianSplat::~GaussianSplat() = default;
|
||||||
@@ -69,6 +101,11 @@ const GaussianSplatSHRecord* GaussianSplat::GetSHRecords() const {
|
|||||||
return static_cast<const GaussianSplatSHRecord*>(GetSectionData(GaussianSplatSectionType::SH));
|
return static_cast<const GaussianSplatSHRecord*>(GetSectionData(GaussianSplatSectionType::SH));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Core::uint32 GaussianSplat::GetSHOrder() const {
|
||||||
|
const GaussianSplatSection* shSection = FindSection(GaussianSplatSectionType::SH);
|
||||||
|
return shSection != nullptr ? ResolveGaussianSplatSHOrderFromSectionStride(shSection->elementStride) : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
bool GaussianSplat::ValidateSections(const Containers::Array<GaussianSplatSection>& sections,
|
bool GaussianSplat::ValidateSections(const Containers::Array<GaussianSplatSection>& sections,
|
||||||
size_t payloadSize) const {
|
size_t payloadSize) const {
|
||||||
for (size_t index = 0; index < sections.Size(); ++index) {
|
for (size_t index = 0; index < sections.Size(); ++index) {
|
||||||
|
|||||||
@@ -328,6 +328,7 @@ void GaussianSplatSceneTest::PrepareRuntimeProject() {
|
|||||||
ASSERT_NE(m_gaussianSplat.Get(), nullptr);
|
ASSERT_NE(m_gaussianSplat.Get(), nullptr);
|
||||||
ASSERT_TRUE(m_gaussianSplat->IsValid());
|
ASSERT_TRUE(m_gaussianSplat->IsValid());
|
||||||
ASSERT_GT(m_gaussianSplat->GetSplatCount(), 0u);
|
ASSERT_GT(m_gaussianSplat->GetSplatCount(), 0u);
|
||||||
|
ASSERT_EQ(m_gaussianSplat->GetSHOrder(), 3u);
|
||||||
m_subsetGaussianSplat = CreateGaussianSplatSubset(
|
m_subsetGaussianSplat = CreateGaussianSplatSubset(
|
||||||
*m_gaussianSplat.Get(),
|
*m_gaussianSplat.Get(),
|
||||||
kBaselineSubsetSplatCount,
|
kBaselineSubsetSplatCount,
|
||||||
@@ -335,6 +336,7 @@ void GaussianSplatSceneTest::PrepareRuntimeProject() {
|
|||||||
ASSERT_NE(m_subsetGaussianSplat, nullptr);
|
ASSERT_NE(m_subsetGaussianSplat, nullptr);
|
||||||
ASSERT_TRUE(m_subsetGaussianSplat->IsValid());
|
ASSERT_TRUE(m_subsetGaussianSplat->IsValid());
|
||||||
ASSERT_GT(m_subsetGaussianSplat->GetSplatCount(), 0u);
|
ASSERT_GT(m_subsetGaussianSplat->GetSplatCount(), 0u);
|
||||||
|
ASSERT_EQ(m_subsetGaussianSplat->GetSHOrder(), 3u);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GaussianSplatSceneTest::BuildScene() {
|
void GaussianSplatSceneTest::BuildScene() {
|
||||||
|
|||||||
@@ -8,6 +8,14 @@ using namespace XCEngine::Math;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
TEST(GaussianSplat, ResolvesSHOrderFromCoefficientCount) {
|
||||||
|
EXPECT_EQ(ResolveGaussianSplatSHOrderFromCoefficientCount(0u), 0u);
|
||||||
|
EXPECT_EQ(ResolveGaussianSplatSHOrderFromCoefficientCount(9u), 1u);
|
||||||
|
EXPECT_EQ(ResolveGaussianSplatSHOrderFromCoefficientCount(24u), 2u);
|
||||||
|
EXPECT_EQ(ResolveGaussianSplatSHOrderFromCoefficientCount(45u), 3u);
|
||||||
|
EXPECT_EQ(ResolveGaussianSplatSHOrderFromCoefficientCount(46u), 0u);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(GaussianSplat, CreateOwnedStoresMetadataSectionsAndPayload) {
|
TEST(GaussianSplat, CreateOwnedStoresMetadataSectionsAndPayload) {
|
||||||
GaussianSplat gaussianSplat;
|
GaussianSplat gaussianSplat;
|
||||||
|
|
||||||
@@ -48,6 +56,29 @@ TEST(GaussianSplat, CreateOwnedStoresMetadataSectionsAndPayload) {
|
|||||||
EXPECT_NE(gaussianSplat.GetSectionData(GaussianSplatSectionType::Positions), nullptr);
|
EXPECT_NE(gaussianSplat.GetSectionData(GaussianSplatSectionType::Positions), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(GaussianSplat, GetSHOrderUsesSHSectionStride) {
|
||||||
|
GaussianSplat gaussianSplat;
|
||||||
|
|
||||||
|
GaussianSplatMetadata metadata;
|
||||||
|
metadata.splatCount = 1u;
|
||||||
|
|
||||||
|
XCEngine::Containers::Array<GaussianSplatSection> sections;
|
||||||
|
sections.PushBack(GaussianSplatSection{
|
||||||
|
GaussianSplatSectionType::SH,
|
||||||
|
GaussianSplatSectionFormat::SHFloat32,
|
||||||
|
0u,
|
||||||
|
96u,
|
||||||
|
1u,
|
||||||
|
96u
|
||||||
|
});
|
||||||
|
|
||||||
|
XCEngine::Containers::Array<XCEngine::Core::uint8> payload;
|
||||||
|
payload.Resize(96u);
|
||||||
|
|
||||||
|
ASSERT_TRUE(gaussianSplat.CreateOwned(metadata, std::move(sections), std::move(payload)));
|
||||||
|
EXPECT_EQ(gaussianSplat.GetSHOrder(), 2u);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(GaussianSplat, RejectsInvalidSectionLayout) {
|
TEST(GaussianSplat, RejectsInvalidSectionLayout) {
|
||||||
GaussianSplat gaussianSplat;
|
GaussianSplat gaussianSplat;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user