Я разрабатываю систему трассировки лучей, и она работает, сейчас я пытаюсь поддерживать больше примитивов (пока она поддерживает: сферы, прямоугольники, плоскости и треугольники), и у меня возникают проблемы с цилиндрами.
Я знаю, что для пересечения луча с цилиндром мне нужно сделать две проверки, первая — с телом (с этим я получаю бесконечный цилиндр), для этого я предполагаю, что круг в двух измерениях, в плоскости xz (x² + z² = r, где r — радиус), тогда мне нужно проверить, что координата Y находится между 0 и высотой, и, наконец, мне нужно проверить, находится ли пересечение в заглавных буквах (x² + z²<= r, где r — радиус).
Мой код следующий (см. Комментарии для более подробного объяснения)
Intersection Cylinder::hit(Ray ray)
{
ray.setOrigin(vec3(getInverseTransform() * vec4(ray.getOrigin(),1)));
ray.setDirection(glm::normalize(vec3(getInverseTransform() * vec4(ray.getDirection(),0))));
// R(t) = o + td
// x² + z² = r²
// (ox+tdx)² + (oz+tdz)² = r²
// (ox)² + 2oxtdx + (tdx)² + (oz)² + 2oztdz + (tdz)² = r²
// t²(dx + dz) + 2t(oxdx + ozdz) + (ox)² + (oz)² - r² = 0
// a=(dx + dz); b = 2(oxdx + ozdz); c = (ox)² + (oz)² - r²
float a = ray.getDirection().x*ray.getDirection().x + ray.getDirection().z*ray.getDirection().z;
float b = 2*(ray.getOrigin().x*ray.getDirection().x + ray.getOrigin().z*ray.getDirection().z);
float c = ray.getOrigin().x*ray.getOrigin().x + ray.getOrigin().z*ray.getOrigin().z - m_radius*m_radius;
float discr = b*b - 4*a*c;
if (discr < 0)
{
return Intersection(false);
}
float x1 = (-b+sqrt(discr))/(2*a);
float x2 = (-b-sqrt(discr))/(2*a);
float t;
//choose the smallest and >=0 t
if (x1 > x2)
{
t=x2;
}
if (t < 0)
{
t=x1;
}// if both solution are <0 => NO INTERSECTION!
if (t<0)
{
return Intersection(false);
}
// normal calculation
// f(x,y) = x² + z² - r² = 0
// T = (dx/dt, y, dz/dt)
// 0 = df/dt = (df/dx, y, df/dz) · T
// N = (2x, 0, 2z)
vec3 point = ray.getOrigin() + ray.getDirection()*t;
vec3 normal = vec3(2*point.x, 0.0f, 2*point.z);// If the y-component from point computed is smaller than 0 or bigger than height => NO INTERSECTION!
if (point.y < 0 || point.y > m_height)
{
return Intersection(false);
}
//If ray direction is not pararel to Y Plane
if (ray.getDirection().y != 0.0f) //Paralel
{
//Compute t's for point intersection in the Y Plane
float t3 = (0-ray.getOrigin().y)/ray.getDirection().y;
float t4 = (m_height-ray.getOrigin().y)/ray.getDirection().y;
float t2;
//choose the smallest and >=0 t
t2 = std::min(t3,t4);
if (t2 < 0)
{
t2 = std::max(t3,t4);
}
if (t2 >= 0)
{
// If there is a t >= 0 compute de point and check if the point is inside the cap
vec3 point1 = ray.getOrigin() + ray.getDirection()*t2;
std::cout << "point " << point1.y << " hipo " << point1.x*point1.x + point1.z*point1.z << " radio " << m_radius*m_radius << std::endl;
if (point1.x*point1.x + point1.z*point1.z <= m_radius*m_radius+0.9f)
{
// Intersection point is inside cap but, Which t is the smallest? t from cap or t from body cylinder?
// I choose the smallest t and check if the t is from cap and compute normal and return intersection.
t = std::min(t,t2);
if (t == t3)
{
normal = vec3(0.0f,-1.0f,0.0f);
return Intersection(true, point1, normal);
}
else if (t == t4)
{
normal = vec3(0.0f,1.0f,0.0f);
return Intersection(true, point1, normal);
}
}
}
}
// Intersection in the body cylinder, compute the point and return the intersection
point = ray.getOrigin() + ray.getDirection()*t;
return Intersection(true, point, normal);
}
этот код приводит к следующему изображению
(Как вы можете видеть, верхний колпачок не рендерится, и я хочу также рендерить колпачки)
Я исследовал и проблема выглядит здесь:
point1.x*point1.x + point1.z*point1.z <= m_radius*m_radius
Код никогда не вводится здесь, вот выходной текст (генерируется std::cout << "point " << point1.y << " hipo " << point1.x*point1.x + point1.z*point1.z << " radio " << m_radius*m_radius << std::endl;
) из первых пикселей (которые должны войти в это условие, потому что первые пиксели соответствуют верхней крышке)
point 0.5 hipo 0.0900812 radio 0.09
point 0.5 hipo 0.0900206 radio 0.09
point 0.5 hipo 0.0900812 radio 0.09
point 0.5 hipo 0.0900206 radio 0.09
Pixel: y: 280
point 0.5 hipo 0.0913921 radio 0.09
point 0.5 hipo 0.120013 radio 0.09
point 0.5 hipo 0.0913921 radio 0.09
point 0.5 hipo 0.120013 radio 0.09
Pixel: y: 281
point 0.5 hipo 0.0930369 radio 0.09
point 0.5 hipo 0.183345 radio 0.09
point 0.5 hipo 0.0930369 radio 0.09
point 0.5 hipo 0.183345 radio 0.09
Pixel: y: 282
point 0.5 hipo 0.0950108 radio 0.09
point 0.5 hipo 0.261889 radio 0.09
point 0.5 hipo 0.0903952 radio 0.09
point 0.5 hipo 0.0903952 radio 0.09
point 0.5 hipo 0.0950108 radio 0.09
point 0.5 hipo 0.261889 radio 0.09
Pixel: y: 283
point 0.5 hipo 0.0973093 radio 0.09
point 0.5 hipo 0.347767 radio 0.09
point 0.5 hipo 0.0927148 radio 0.09
point 0.5 hipo 0.0927148 radio 0.09
point 0.5 hipo 0.0973093 radio 0.09
point 0.5 hipo 0.347767 radio 0.09
Как вы можете видеть никогда hipo
является < чем radio
Я хочу сделать весь цилиндр с крышками. Кто-нибудь может направить меня, чтобы сделать весь цилиндр? (тело и шапки)
Спасибо
Я предполагаю, что вы можете найти точки пересечения между лучом и поверхностью цилиндра, полученные как значения t вдоль луча. Сделайте аналогичное вычисление, чтобы найти пересечения с плоскостями двух базисов.
Вы получите две пары [tc0, tc1], [tp0, tp1]. Если эти интервалы не перекрываются, луч не попадает в цилиндр. В противном случае, самые большие из tc0 и tp0 сообщают вам, какая поверхность на самом деле поражена, а это значение t говорит вам, где.
Других решений пока нет …