如何在 C++ 中进行线程安全的光线投射?
how to make thread safe raycasting in C++?
我正在用 C++ 实现简单的平面光线投射,我使用 OpenMP 进行多线程处理。
这是遍历所有像素并计算交点的函数:
#pragma omp parallel for num_threads(std::thread::hardware_concurrency())
for (int x = 0; x < camera_.GetWidth(); x++)
{
for (uint32_t y = 0; y < camera_.GetHeight(); y++)
{
Ray ray(camera_.GetPosition());
ray.SetDirection(GetDirection(glm::vec2((2.0f * x) / camera_.GetWidth() - 1.0f, (2.0f * y) / camera_.GetHeight() - 1.0f)));
cogs::Color3f temp = cogs::Color3f(0, 0, 0);
for (uint16_t p = 0; p < models_.size(); p++)
{
//mutex.lock();
if (models_.at(p)->Intersection(ray))
{
ray.SetParamT(models_.at(p)->GetDistanceT());
temp = models_.at(p)->GetColor() * (camera_.GetLightEnergy() / ray.GetParamT());
}
//mutex.unlock();
}
renderer.SetPixel(x, y, temp );
}
}
bool Plane::Intersection(Ray &ray)
{
float dot = glm::dot(ray.GetDirection(), normal_);
if (dot == 0.0f)
{
return false;
}
float t = glm::dot(reference_ - ray.GetOrigin(), normal_) / dot;
if (t <= 0.0001 || t > ray.GetParamT())
{
return false;
}
SetDistanceT(t);
return true;
}
结果如下:
如您所见,一些像素被混淆了,但是如果我取消注释互斥锁和解锁这段代码,那么结果是正确的,但计算时间比不使用 pragma omp 需要更长的时间
知道如何使线程更安全吗?当我开始时,几乎一半的像素都混淆了,然后在我的自定义 类 中,我将每个变量添加为私有变量并创建 getter 和 setter,这有所帮助,但它仍然不完全正确
编辑:根据@Jérôme Richard 建议更新了代码并添加了交集代码
调用一个名为 bool Plane::Intersect(Ray &)
的方法,该方法 对 Plane 对象进行变异 除了确定是否存在交集,然后将此变异的结果从Plane
对象同时希望它没有在另一个线程中被更改,这可能是您的问题(或至少是其中之一),而且这不是很好的软件工程。
我建议完全摆脱整个 SetDistanceT
/ GetDistanceT
功能。只是 return 与直接检查交叉点的方法的距离,并在其参数和实例中使该方法 const
。
(你如何处理 dot
为零的情况?在这种情况下你可以 return std::numeric_limits<double>::infinity()
,或者你可以使用 return 方法例如 std::pair<bool, double>
,或自定义结构,或...)
编辑: 当你使用它时,我会在你修复它之后将此方法声明为 float Intersect(const Ray &) const
,以便编译器 强制 它不会改变射线或平面。 (第二个 const
表示该方法不会改变它所调用的实例,在本例中是您与之相交的平面。)
我正在用 C++ 实现简单的平面光线投射,我使用 OpenMP 进行多线程处理。
这是遍历所有像素并计算交点的函数:
#pragma omp parallel for num_threads(std::thread::hardware_concurrency())
for (int x = 0; x < camera_.GetWidth(); x++)
{
for (uint32_t y = 0; y < camera_.GetHeight(); y++)
{
Ray ray(camera_.GetPosition());
ray.SetDirection(GetDirection(glm::vec2((2.0f * x) / camera_.GetWidth() - 1.0f, (2.0f * y) / camera_.GetHeight() - 1.0f)));
cogs::Color3f temp = cogs::Color3f(0, 0, 0);
for (uint16_t p = 0; p < models_.size(); p++)
{
//mutex.lock();
if (models_.at(p)->Intersection(ray))
{
ray.SetParamT(models_.at(p)->GetDistanceT());
temp = models_.at(p)->GetColor() * (camera_.GetLightEnergy() / ray.GetParamT());
}
//mutex.unlock();
}
renderer.SetPixel(x, y, temp );
}
}
bool Plane::Intersection(Ray &ray)
{
float dot = glm::dot(ray.GetDirection(), normal_);
if (dot == 0.0f)
{
return false;
}
float t = glm::dot(reference_ - ray.GetOrigin(), normal_) / dot;
if (t <= 0.0001 || t > ray.GetParamT())
{
return false;
}
SetDistanceT(t);
return true;
}
结果如下:
如您所见,一些像素被混淆了,但是如果我取消注释互斥锁和解锁这段代码,那么结果是正确的,但计算时间比不使用 pragma omp 需要更长的时间
知道如何使线程更安全吗?当我开始时,几乎一半的像素都混淆了,然后在我的自定义 类 中,我将每个变量添加为私有变量并创建 getter 和 setter,这有所帮助,但它仍然不完全正确
编辑:根据@Jérôme Richard 建议更新了代码并添加了交集代码
调用一个名为 bool Plane::Intersect(Ray &)
的方法,该方法 对 Plane 对象进行变异 除了确定是否存在交集,然后将此变异的结果从Plane
对象同时希望它没有在另一个线程中被更改,这可能是您的问题(或至少是其中之一),而且这不是很好的软件工程。
我建议完全摆脱整个 SetDistanceT
/ GetDistanceT
功能。只是 return 与直接检查交叉点的方法的距离,并在其参数和实例中使该方法 const
。
(你如何处理 dot
为零的情况?在这种情况下你可以 return std::numeric_limits<double>::infinity()
,或者你可以使用 return 方法例如 std::pair<bool, double>
,或自定义结构,或...)
编辑: 当你使用它时,我会在你修复它之后将此方法声明为 float Intersect(const Ray &) const
,以便编译器 强制 它不会改变射线或平面。 (第二个 const
表示该方法不会改变它所调用的实例,在本例中是您与之相交的平面。)