From d9dceade5bc9619b4ffd9d52ee28196f8a3b4b17 Mon Sep 17 00:00:00 2001 From: Piotr Mrozik <33581747+PiotrMrozik@users.noreply.github.com> Date: Mon, 16 Dec 2024 14:42:15 +0100 Subject: [PATCH] Feature/reflectivity (#341) * Introduce new field * First working version of reflectivity calculation with hardcoded alpha. * Introduce alpha of reflectivity. * Cleanup * Fix tape call * Introduce separate test. * PR fixes v1 * minor formating fixes * Update test/src/scene/reflectivityTest.cpp Co-authored-by: Piotr Rybicki * fix naming conflict --------- Co-authored-by: Piotr Rybicki --- include/rgl/api/core.h | 16 ++++ src/RGLFields.hpp | 8 ++ src/api/apiCore.cpp | 21 ++++++ src/gpu/RaytraceRequestContext.hpp | 2 + src/gpu/nodeKernels.cu | 7 ++ src/graph/NodesCore.hpp | 2 + src/graph/RaytraceNode.cpp | 2 + src/tape/TapeCore.hpp | 3 + test/CMakeLists.txt | 1 + test/include/helpers/fieldGenerators.hpp | 3 + test/include/helpers/testPointCloud.hpp | 1 + test/src/TapeTest.cpp | 3 + test/src/helpers/pointsTest.cpp | 20 +++-- test/src/scene/reflectivityTest.cpp | 93 ++++++++++++++++++++++++ 14 files changed, 175 insertions(+), 7 deletions(-) create mode 100644 test/src/scene/reflectivityTest.cpp diff --git a/include/rgl/api/core.h b/include/rgl/api/core.h index 1b13f83a..e4414d69 100644 --- a/include/rgl/api/core.h +++ b/include/rgl/api/core.h @@ -441,6 +441,13 @@ typedef enum : int32_t */ RGL_FIELD_LASER_RETRO_F32, + /** + * Reflectivity of the hit point. + * Reflectivity is a scalar value that describes how much light is reflected by the hit point. + * Reflectivity is a property of the material of the hit point. + */ + RGL_FIELD_REFLECTIVITY_F32, + // Dummy fields RGL_FIELD_PADDING_8 = 1024, RGL_FIELD_PADDING_16, @@ -892,6 +899,15 @@ RGL_API rgl_status_t rgl_node_raytrace_configure_beam_divergence(rgl_node_t node */ RGL_API rgl_status_t rgl_node_raytrace_configure_default_intensity(rgl_node_t node, float default_intensity); +/** + * Modifies RaytraceNode to set reflectivity alpha. + * Reflectivity alpha is used to calculate reflectivity of the hit point. This value is constant for every hit point. + * Default reflectivity alpha is set to 0.1f. + * @param node RaytraceNode to modify. + * @param reflectivity_alpha Reflectivity alpha to set. + */ +RGL_API rgl_status_t rgl_node_raytrace_configure_reflectivity_alpha(rgl_node_t node, float reflectivity_alpha); + /** * Modifies RaytraceNode to set return mode. * Point return types (RGL_FIELD_RETURN_TYPE_U8) will be set to corresponding rgl_return_type_t values, e.g. return mode diff --git a/src/RGLFields.hpp b/src/RGLFields.hpp index d12d8098..14330cf9 100644 --- a/src/RGLFields.hpp +++ b/src/RGLFields.hpp @@ -41,6 +41,7 @@ typedef unsigned char TextureTexelFormat; #define ENTITY_ID_I32 RGL_FIELD_ENTITY_ID_I32 #define INTENSITY_F32 RGL_FIELD_INTENSITY_F32 #define INTENSITY_U8 RGL_FIELD_INTENSITY_U8 +#define REFLECTIVITY_F32 RGL_FIELD_REFLECTIVITY_F32 #define LASER_RETRO_F32 RGL_FIELD_LASER_RETRO_F32 #define RING_ID_U16 RGL_FIELD_RING_ID_U16 #define AZIMUTH_F32 RGL_FIELD_AZIMUTH_F32 @@ -73,6 +74,7 @@ inline const std::set& getAllRealFields() ENTITY_ID_I32, INTENSITY_F32, INTENSITY_U8, + REFLECTIVITY_F32, LASER_RETRO_F32, RING_ID_U16, AZIMUTH_F32, @@ -122,6 +124,7 @@ FIELD(RAY_IDX_U32, uint32_t); // PCL uses uint32_t FIELD(ENTITY_ID_I32, int32_t); FIELD(INTENSITY_F32, float); FIELD(INTENSITY_U8, uint8_t); +FIELD(REFLECTIVITY_F32, float); FIELD(LASER_RETRO_F32, float); FIELD(IS_HIT_I32, int32_t); // Signed may be faster FIELD(IS_GROUND_I32, int32_t); // Signed may be faster @@ -156,6 +159,7 @@ inline std::size_t getFieldSize(rgl_field_t type) case IS_GROUND_I32: return Field::size; case INTENSITY_F32: return Field::size; case INTENSITY_U8: return Field::size; + case REFLECTIVITY_F32: return Field::size; case LASER_RETRO_F32: return Field::size; case RING_ID_U16: return Field::size; case AZIMUTH_F32: return Field::size; @@ -215,6 +219,7 @@ inline std::shared_ptr createArray(rgl_field_t type, Args&&... args) case ENTITY_ID_I32: return Subclass::type>::create(std::forward(args)...); case INTENSITY_F32: return Subclass::type>::create(std::forward(args)...); case INTENSITY_U8: return Subclass::type>::create(std::forward(args)...); + case REFLECTIVITY_F32: return Subclass::type>::create(std::forward(args)...); case LASER_RETRO_F32: return Subclass::type>::create(std::forward(args)...); case RING_ID_U16: return Subclass::type>::create(std::forward(args)...); case AZIMUTH_F32: return Subclass::type>::create(std::forward(args)...); @@ -251,6 +256,7 @@ inline std::string toString(rgl_field_t type) case ENTITY_ID_I32: return "ENTITY_ID_I32"; case INTENSITY_F32: return "INTENSITY_F32"; case INTENSITY_U8: return "INTENSITY_U8"; + case REFLECTIVITY_F32: return "REFLECTIVITY_F32"; case LASER_RETRO_F32: return "LASER_RETRO_F32"; case RING_ID_U16: return "RING_ID_U16"; case AZIMUTH_F32: return "AZIMUTH_F32"; @@ -291,6 +297,7 @@ inline std::vector toRos2Fields(rgl_field_t type) case ENTITY_ID_I32: return {sensor_msgs::msg::PointField::INT32}; case INTENSITY_F32: return {sensor_msgs::msg::PointField::FLOAT32}; case INTENSITY_U8: return {sensor_msgs::msg::PointField::UINT8}; + case REFLECTIVITY_F32: return {sensor_msgs::msg::PointField::FLOAT32}; case LASER_RETRO_F32: return {sensor_msgs::msg::PointField::FLOAT32}; case RING_ID_U16: return {sensor_msgs::msg::PointField::UINT16}; case AZIMUTH_F32: return {sensor_msgs::msg::PointField::FLOAT32}; @@ -342,6 +349,7 @@ inline std::vector toRos2Names(rgl_field_t type) case INTENSITY_F32: case INTENSITY_U8: return {"intensity"}; + case REFLECTIVITY_F32: return {"reflectivity"}; case LASER_RETRO_F32: return {"laser_retro"}; case RING_ID_U16: return {"channel"}; case AZIMUTH_F32: return {"azimuth"}; diff --git a/src/api/apiCore.cpp b/src/api/apiCore.cpp index 38fc516e..750db89e 100644 --- a/src/api/apiCore.cpp +++ b/src/api/apiCore.cpp @@ -1066,6 +1066,27 @@ void TapeCore::tape_node_raytrace_configure_default_intensity(const YAML::Node& rgl_node_raytrace_configure_default_intensity(node, yamlNode[1].as()); } +RGL_API rgl_status_t rgl_node_raytrace_configure_reflectivity_alpha(rgl_node_t node, float reflectivity_alpha) +{ + auto status = rglSafeCall([&]() { + RGL_API_LOG("rgl_node_raytrace_configure_reflectivity_alpha(node={}, default_intensity={})", repr(node), + reflectivity_alpha); + CHECK_ARG(node != nullptr); + CHECK_ARG(reflectivity_alpha >= 0.0f); + RaytraceNode::Ptr raytraceNode = Node::validatePtr(node); + raytraceNode->setReflectivityAlpha(reflectivity_alpha); + }); + TAPE_HOOK(node, reflectivity_alpha); + return status; +} + +void TapeCore::tape_node_raytrace_configure_reflectivity_alpha(const YAML::Node& yamlNode, PlaybackState& state) +{ + auto nodeId = yamlNode[0].as(); + rgl_node_t node = state.nodes.at(nodeId); + rgl_node_raytrace_configure_reflectivity_alpha(node, yamlNode[1].as()); +} + RGL_API rgl_status_t rgl_node_raytrace_configure_return_mode(rgl_node_t node, rgl_return_mode_t return_mode) { auto status = rglSafeCall([&]() { diff --git a/src/gpu/RaytraceRequestContext.hpp b/src/gpu/RaytraceRequestContext.hpp index 0e32a667..0cc2ca6a 100644 --- a/src/gpu/RaytraceRequestContext.hpp +++ b/src/gpu/RaytraceRequestContext.hpp @@ -29,6 +29,7 @@ struct RaytraceRequestContext float farNonHitDistance; float defaultIntensity; + float reflectivityAlpha; const Mat3x4f* raysWorld; size_t rayCount; @@ -58,6 +59,7 @@ struct RaytraceRequestContext Field::type* distance; Field::type* intensityF32; Field::type* intensityU8; + Field::type* reflectivityF32; Field::type* laserRetro; Field::type* timestampF64; Field::type* timestampU32; diff --git a/src/gpu/nodeKernels.cu b/src/gpu/nodeKernels.cu index 3917397c..17d8b2fe 100644 --- a/src/gpu/nodeKernels.cu +++ b/src/gpu/nodeKernels.cu @@ -127,6 +127,10 @@ __device__ void saveReturnAsHit(const RaytraceRequestContext* ctx, int beamIdx, static_cast(std::round(ctx->mrSamples.intensity[sampleIdx])) : UINT8_MAX; } + if (ctx->reflectivityF32 != nullptr) { + const auto distance2 = ctx->mrSamples.distance[sampleIdx] * ctx->mrSamples.distance[sampleIdx]; + ctx->reflectivityF32[returnPointIdx] = ctx->reflectivityAlpha * ctx->mrSamples.intensity[sampleIdx] * distance2; + } if (ctx->entityId != nullptr) { ctx->entityId[returnPointIdx] = ctx->mrSamples.entityId[sampleIdx]; } @@ -183,6 +187,9 @@ __device__ void saveReturnAsNonHit(const RaytraceRequestContext* ctx, int firstS if (ctx->intensityU8 != nullptr) { ctx->intensityU8[returnPointIdx] = 0; } + if (ctx->reflectivityF32 != nullptr) { + ctx->reflectivityF32[returnPointIdx] = 0; + } if (ctx->entityId != nullptr) { ctx->entityId[returnPointIdx] = RGL_ENTITY_INVALID_ID; } diff --git a/src/graph/NodesCore.hpp b/src/graph/NodesCore.hpp index fca18c9e..aac211e5 100644 --- a/src/graph/NodesCore.hpp +++ b/src/graph/NodesCore.hpp @@ -139,6 +139,7 @@ struct RaytraceNode : IPointsNode void setNonHitDistanceValues(float nearDistance, float farDistance); void setNonHitsMask(const int8_t* maskRaw, size_t maskPointCount); void setDefaultIntensity(float intensity) { defaultIntensity = intensity; } + void setReflectivityAlpha(float reflectivity_alpha) { reflectivityAlpha = reflectivity_alpha; } void setReturnMode(rgl_return_mode_t mode) { if (mode == RGL_RETURN_UNKNOWN) { @@ -244,6 +245,7 @@ struct RaytraceNode : IPointsNode float nearNonHitDistance{std::numeric_limits::infinity()}; float farNonHitDistance{std::numeric_limits::infinity()}; float defaultIntensity = 0.0f; + float reflectivityAlpha = 0.1f; MultiReturnSamples mrSampleData = MultiReturnSamples{arrayMgr}; rgl_return_mode_t returnMode = RGL_RETURN_FIRST; diff --git a/src/graph/RaytraceNode.cpp b/src/graph/RaytraceNode.cpp index 9425aa1b..e6d9f936 100644 --- a/src/graph/RaytraceNode.cpp +++ b/src/graph/RaytraceNode.cpp @@ -98,6 +98,7 @@ void RaytraceNode::enqueueExecImpl() .nearNonHitDistance = nearNonHitDistance, .farNonHitDistance = farNonHitDistance, .defaultIntensity = defaultIntensity, + .reflectivityAlpha = reflectivityAlpha, .raysWorld = raysPtr, .rayCount = raysNode->getRayCount(), .rayOriginToWorld = raysNode->getCumulativeRayTransfrom(), @@ -119,6 +120,7 @@ void RaytraceNode::enqueueExecImpl() .distance = getPtrTo(), .intensityF32 = getPtrTo(), .intensityU8 = getPtrTo(), + .reflectivityF32 = getPtrTo(), .laserRetro = getPtrTo(), .timestampF64 = getPtrTo(), .timestampU32 = getPtrTo(), diff --git a/src/tape/TapeCore.hpp b/src/tape/TapeCore.hpp index 2017cb17..fa378fde 100644 --- a/src/tape/TapeCore.hpp +++ b/src/tape/TapeCore.hpp @@ -60,6 +60,7 @@ class TapeCore static void tape_node_raytrace_configure_mask(const YAML::Node& yamlNode, PlaybackState& state); static void tape_node_raytrace_configure_beam_divergence(const YAML::Node& yamlNode, PlaybackState& state); static void tape_node_raytrace_configure_default_intensity(const YAML::Node& yamlNode, PlaybackState& state); + static void tape_node_raytrace_configure_reflectivity_alpha(const YAML::Node& yamlNode, PlaybackState& state); static void tape_node_raytrace_configure_return_mode(const YAML::Node& yamlNode, PlaybackState& state); static void tape_node_points_format(const YAML::Node& yamlNode, PlaybackState& state); static void tape_node_points_yield(const YAML::Node& yamlNode, PlaybackState& state); @@ -122,6 +123,8 @@ class TapeCore TapeCore::tape_node_raytrace_configure_beam_divergence), TAPE_CALL_MAPPING("rgl_node_raytrace_configure_default_intensity", TapeCore::tape_node_raytrace_configure_default_intensity), + TAPE_CALL_MAPPING("rgl_node_raytrace_configure_reflectivity_alpha", + TapeCore::tape_node_raytrace_configure_reflectivity_alpha), TAPE_CALL_MAPPING("rgl_node_raytrace_configure_return_mode", TapeCore::tape_node_raytrace_configure_return_mode), TAPE_CALL_MAPPING("rgl_node_points_format", TapeCore::tape_node_points_format), diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a492542b..c3c626fb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -48,6 +48,7 @@ set(RGL_TEST_FILES src/scene/entityVelocityTest.cpp src/scene/meshAPITest.cpp src/scene/textureTest.cpp + src/scene/reflectivityTest.cpp src/synchronization/graphAndCopyStream.cpp src/synchronization/graphThreadSynchronization.cpp src/synchronization/testKernel.cu diff --git a/test/include/helpers/fieldGenerators.hpp b/test/include/helpers/fieldGenerators.hpp index 8de8a174..d9415d54 100644 --- a/test/include/helpers/fieldGenerators.hpp +++ b/test/include/helpers/fieldGenerators.hpp @@ -36,6 +36,9 @@ static std::function::type(int)> genIntensityF32 = [](int i static std::function::type(int)> genIntensityU8 = [](int i) { return i % std::numeric_limits::type>::max(); }; +static std::function::type(int)> genReflectivityF32 = [](int i) { + return static_cast(i) / (static_cast(i + 1)); +}; static std::function::type(int)> genLaserRetro = [](int i) { return static_cast(i) / (static_cast(i + 1)); }; diff --git a/test/include/helpers/testPointCloud.hpp b/test/include/helpers/testPointCloud.hpp index 19420033..1127e358 100644 --- a/test/include/helpers/testPointCloud.hpp +++ b/test/include/helpers/testPointCloud.hpp @@ -289,6 +289,7 @@ class TestPointCloud {ENTITY_ID_I32, [&](std::size_t count) {setFieldValues(generateFieldValues(count, genEntityId));}}, {INTENSITY_F32, [&](std::size_t count) {setFieldValues(generateFieldValues(count, genIntensityF32));}}, {INTENSITY_U8, [&](std::size_t count) {setFieldValues(generateFieldValues(count, genIntensityU8));}}, + {REFLECTIVITY_F32, [&](std::size_t count) {setFieldValues(generateFieldValues(count, genReflectivityF32));}}, {LASER_RETRO_F32, [&](std::size_t count) {setFieldValues(generateFieldValues(count, genLaserRetro));}}, {RING_ID_U16, [&](std::size_t count) {setFieldValues(generateFieldValues(count, genRingId));}}, {AZIMUTH_F32, [&](std::size_t count) {setFieldValues(generateFieldValues(count, genAzimuth));}}, diff --git a/test/src/TapeTest.cpp b/test/src/TapeTest.cpp index 4e7654e1..4b4e2c8a 100644 --- a/test/src/TapeTest.cpp +++ b/test/src/TapeTest.cpp @@ -252,6 +252,9 @@ TEST_F(TapeTest, RecordPlayAllCalls) float defaultIntensity = 1.1f; EXPECT_RGL_SUCCESS(rgl_node_raytrace_configure_default_intensity(raytrace, defaultIntensity)); + float reflectivityAlpha = 0.1; + EXPECT_RGL_SUCCESS(rgl_node_raytrace_configure_reflectivity_alpha(raytrace, reflectivityAlpha)); + rgl_return_mode_t returnMode = RGL_RETURN_FIRST; EXPECT_RGL_SUCCESS(rgl_node_raytrace_configure_return_mode(raytrace, returnMode)); diff --git a/test/src/helpers/pointsTest.cpp b/test/src/helpers/pointsTest.cpp index 77fdaa6e..3bbf34c6 100644 --- a/test/src/helpers/pointsTest.cpp +++ b/test/src/helpers/pointsTest.cpp @@ -5,18 +5,20 @@ class PointCloudTest : public ::testing::TestWithParam { protected: - std::vector allNotDummyFields = {XYZ_VEC3_F32, IS_HIT_I32, RAY_IDX_U32, ENTITY_ID_I32, INTENSITY_F32, - RING_ID_U16, AZIMUTH_F32, DISTANCE_F32, RETURN_TYPE_U8, TIME_STAMP_F64}; + std::vector allNotDummyFields = {XYZ_VEC3_F32, IS_HIT_I32, RAY_IDX_U32, ENTITY_ID_I32, + INTENSITY_F32, REFLECTIVITY_F32, RING_ID_U16, AZIMUTH_F32, + DISTANCE_F32, RETURN_TYPE_U8, TIME_STAMP_F64}; - std::vector fieldsWithPaddings = {PADDING_32, XYZ_VEC3_F32, PADDING_16, IS_HIT_I32, PADDING_8, - RAY_IDX_U32, ENTITY_ID_I32, INTENSITY_F32, RING_ID_U16, AZIMUTH_F32, - DISTANCE_F32, RETURN_TYPE_U8, TIME_STAMP_F64, PADDING_16, PADDING_32}; + std::vector fieldsWithPaddings = { + PADDING_32, XYZ_VEC3_F32, PADDING_16, IS_HIT_I32, PADDING_8, RAY_IDX_U32, ENTITY_ID_I32, INTENSITY_F32, + REFLECTIVITY_F32, RING_ID_U16, AZIMUTH_F32, DISTANCE_F32, RETURN_TYPE_U8, TIME_STAMP_F64, PADDING_16, PADDING_32}; std::vector::type> pointCoord; std::vector::type> isHit; std::vector::type> rayIdx; std::vector::type> entityId; std::vector::type> intensity; + std::vector::type> reflectivity; std::vector::type> ringId; std::vector::type> azimuth; std::vector::type> distance; @@ -31,6 +33,7 @@ class PointCloudTest : public ::testing::TestWithParam Field::type rayIdx; Field::type entityId; Field::type intensity; + Field::type reflectivity; Field::type ringId; Field::type azimuth; Field::type distance; @@ -45,8 +48,8 @@ class PointCloudTest : public ::testing::TestWithParam points.reserve(pointsCount); for (int i = 0; i < pointsCount; i++) { points.emplace_back(TestPointStruct{genCoord(i), genHalfHit(i), genRayIdx(i), genEntityId(i), genIntensityF32(i), - genRingId(i), genAzimuth(i), genDistance(i), genReturnType(i), - genTimeStampF64(i)}); + genReflectivityF32(i), genRingId(i), genAzimuth(i), genDistance(i), + genReturnType(i), genTimeStampF64(i)}); } return points; } @@ -58,6 +61,7 @@ class PointCloudTest : public ::testing::TestWithParam rayIdx = generateFieldValues(pointsCount, genRayIdx); entityId = generateFieldValues(pointsCount, genEntityId); intensity = generateFieldValues(pointsCount, genIntensityF32); + reflectivity = generateFieldValues(pointsCount, genReflectivityF32); ringId = generateFieldValues(pointsCount, genRingId); azimuth = generateFieldValues(pointsCount, genAzimuth); distance = generateFieldValues(pointsCount, genDistance); @@ -72,6 +76,7 @@ class PointCloudTest : public ::testing::TestWithParam pointCloud.setFieldValues(rayIdx); pointCloud.setFieldValues(entityId); pointCloud.setFieldValues(intensity); + pointCloud.setFieldValues(reflectivity); pointCloud.setFieldValues(ringId); pointCloud.setFieldValues(azimuth); pointCloud.setFieldValues(distance); @@ -92,6 +97,7 @@ class PointCloudTest : public ::testing::TestWithParam EXPECT_EQ(pointCloud.getFieldValues(), rayIdx); EXPECT_EQ(pointCloud.getFieldValues(), entityId); EXPECT_EQ(pointCloud.getFieldValues(), intensity); + EXPECT_EQ(pointCloud.getFieldValues(), reflectivity); EXPECT_EQ(pointCloud.getFieldValues(), ringId); EXPECT_EQ(pointCloud.getFieldValues(), azimuth); EXPECT_EQ(pointCloud.getFieldValues(), distance); diff --git a/test/src/scene/reflectivityTest.cpp b/test/src/scene/reflectivityTest.cpp new file mode 100644 index 00000000..1f6b6eee --- /dev/null +++ b/test/src/scene/reflectivityTest.cpp @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include + +#include +struct ReflectivityTest : public RGLTestWithParam> +{}; + +INSTANTIATE_TEST_SUITE_P(Parametrized, ReflectivityTest, + testing::Combine( + testing::Values(0.012f, 0.12f, 1.23f), + testing::Values(u_char(0), u_char(127), u_char(255)), + testing::Values(0.012, 0.123, 1.23))); + +TEST_P(ReflectivityTest, read_value) +{ + auto [alpha, value, distance] = GetParam(); + + int textureWidth = 100; + int textureHeight = 100; + + rgl_texture_t texture = nullptr; + rgl_entity_t entity = nullptr; + rgl_mesh_t mesh = makeCubeMesh(); + auto textureRawData = generateStaticColorTexture(textureWidth, textureHeight, value); + + EXPECT_RGL_SUCCESS(rgl_texture_create(&texture, textureRawData.data(), textureWidth, textureHeight)); + EXPECT_RGL_SUCCESS(rgl_mesh_set_texture_coords(mesh, cubeUVs, ARRAY_SIZE(cubeUVs))); + + EXPECT_RGL_SUCCESS(rgl_entity_create(&entity, nullptr, mesh)); + EXPECT_RGL_SUCCESS(rgl_entity_set_intensity_texture(entity, texture)); + + // Scale the cube in order to test different distances + rgl_mat3x4f cubeTransform = Mat3x4f::TRS({0, 0, 0}, {0, 0, 0}, {distance, distance, distance}).toRGL(); + EXPECT_RGL_SUCCESS(rgl_entity_set_transform(entity, &cubeTransform)); + + // Create RGL graph pipeline. + rgl_node_t useRaysNode = nullptr, raytraceNode = nullptr, compactNode = nullptr, yieldNode = nullptr; + + std::vector rays = {// Ray must be incident perpendicular to the surface to receive all intensity + Mat3x4f::TRS({0, 0, 0}, {0, 0, 0}).toRGL()}; + + std::vector yieldFields = {INTENSITY_F32, REFLECTIVITY_F32, DISTANCE_F32}; + + EXPECT_RGL_SUCCESS(rgl_node_rays_from_mat3x4f(&useRaysNode, rays.data(), rays.size())); + EXPECT_RGL_SUCCESS(rgl_node_raytrace(&raytraceNode, nullptr)); + EXPECT_RGL_SUCCESS(rgl_node_points_compact_by_field(&compactNode, RGL_FIELD_IS_HIT_I32)); + EXPECT_RGL_SUCCESS(rgl_node_points_yield(&yieldNode, yieldFields.data(), yieldFields.size())); + + EXPECT_RGL_SUCCESS(rgl_graph_node_add_child(useRaysNode, raytraceNode)); + EXPECT_RGL_SUCCESS(rgl_graph_node_add_child(raytraceNode, compactNode)); + EXPECT_RGL_SUCCESS(rgl_graph_node_add_child(compactNode, yieldNode)); + + EXPECT_RGL_SUCCESS(rgl_node_raytrace_configure_reflectivity_alpha(raytraceNode, alpha)); + + EXPECT_RGL_SUCCESS(rgl_graph_run(raytraceNode)); + + std::vector<::Field::type> outIntensity; + std::vector<::Field::type> outReflectivity; + std::vector<::Field::type> outDistance; + + int32_t outCount, outSizeOf; + EXPECT_RGL_SUCCESS(rgl_graph_get_result_size(yieldNode, INTENSITY_F32, &outCount, &outSizeOf)); + EXPECT_EQ(outSizeOf, getFieldSize(INTENSITY_F32)); + + EXPECT_RGL_SUCCESS(rgl_graph_get_result_size(yieldNode, REFLECTIVITY_F32, &outCount, &outSizeOf)); + EXPECT_EQ(outSizeOf, getFieldSize(REFLECTIVITY_F32)); + + EXPECT_RGL_SUCCESS(rgl_graph_get_result_size(yieldNode, DISTANCE_F32, &outCount, &outSizeOf)); + EXPECT_EQ(outSizeOf, getFieldSize(DISTANCE_F32)); + + outIntensity.resize(outCount); + outReflectivity.resize(outCount); + outDistance.resize(outCount); + + EXPECT_RGL_SUCCESS(rgl_graph_get_result_data(yieldNode, INTENSITY_F32, outIntensity.data())); + EXPECT_RGL_SUCCESS(rgl_graph_get_result_data(yieldNode, REFLECTIVITY_F32, outReflectivity.data())); + EXPECT_RGL_SUCCESS(rgl_graph_get_result_data(yieldNode, DISTANCE_F32, outDistance.data())); + + for (int i = 0; i < outCount; ++i) { + EXPECT_NEAR(((float) value), outIntensity.at(i), EPSILON_F); + float outDistanceValue = outDistance.at(i); + float intensity = outIntensity.at(i); + float reflectivityValue = alpha * outDistanceValue * outDistanceValue * intensity; + + // Reflectivity test is conducted with greater epsilon. + // This is due to lack of distance impact on intensity. + // As long as distance is not included into intensity calculations, reflectivity value will grow relatively fast with the distance. + EXPECT_NEAR(reflectivityValue, outReflectivity.at(i), 1e-3f); + } +} \ No newline at end of file