Thanks to the suggestion in the comments and in particular the tip of @Homer512 I have come to this solution, please tell me if you think it could be done better.
// shape.cuh
#pragma once
#include <variant>
#include "sphere.cuh"
using Shape = std::variant<Sphere>;
//sphere.cuh
#pragma once
#include "ray.cuh"
#include "vec3.cuh"
class Sphere {
public:
__host__ __device__ Sphere(const Vec3 ¢er, float radius);
__device__ auto hit(const Ray &r) const -> bool;
private:
Vec3 center;
float radius;
};
h_shapes
initialized as
const std::vector<Shape> &h_shapes = scene->getShapes();
const size_t num_shapes = h_shapes.size();
Shape *d_shapes;
CUDA_ERROR_CHECK(cudaMalloc((void **)&d_shapes, num_shapes * sizeof(Shape)));
CUDA_ERROR_CHECK(cudaMemcpy(d_shapes, h_shapes.data(),
num_shapes * sizeof(Sphere),
cudaMemcpyHostToDevice));
and finally the device function to get the color
template <class... Ts> struct overload : Ts... {
using Ts::operator()...;
};
__device__ auto getColor(const Ray &ray, const Shape *shapes,
const size_t num_shapes) -> uchar4 {
for (size_t i = 0; i < num_shapes; i++) {
bool hit = std::visit(
overload{
[&ray](const Sphere &s) { return s.hit(ray); },
},
shapes[i]);
if (hit) {
return make_uchar4(1, 0, 0, UCHAR_MAX);
}
}
return make_uchar4(0, 0, 1, UCHAR_MAX);
}
Here I don't really like the fact that for each new shape I have to write the [&ray](const Sphere/Cube/Pyramid &s) { return s.ray(ray) }
when I already have defined a type that represents the union of multiple shapes