Ray Oriented bounding box (OBB) intersection function not working with scale - c++

So I have this function for getting the intersection of a ray with an OBB:
std::optional<float> Ray::hitsOBB(const glm::vec3& min, const glm::vec3& max, const glm::mat4& modelMatrix) {
float tMin = 0.0f;
float tMax = 100000.0f;
glm::vec3 OBBposition_worldspace(modelMatrix[3].x, modelMatrix[3].y, modelMatrix[3].z);
glm::vec3 delta = OBBposition_worldspace - origin;
{
glm::vec3 xaxis(modelMatrix[0].x, modelMatrix[0].y, modelMatrix[0].z);
float e = glm::dot(xaxis, delta);
float f = glm::dot(direction, xaxis);
if (fabs(f) > 0.001f) {
float t1 = (e + min.x) / f;
float t2 = (e + max.x) / f;
if (t1 > t2) std::swap(t1, t2);
if (t2 < tMax) tMax = t2;
if (t1 > tMin) tMin = t1;
if (tMin > tMax) return {};
}
else {
if (-e + min.x > 0.0f || -e + max.x < 0.0f) return {};
}
}
{
glm::vec3 yaxis(modelMatrix[1].x, modelMatrix[1].y, modelMatrix[1].z);
float e = glm::dot(yaxis, delta);
float f = glm::dot(direction, yaxis);
if (fabs(f) > 0.001f) {
float t1 = (e + min.y) / f;
float t2 = (e + max.y) / f;
if (t1 > t2) std::swap(t1, t2);
if (t2 < tMax) tMax = t2;
if (t1 > tMin) tMin = t1;
if (tMin > tMax) return {};
}
else {
if (-e + min.y > 0.0f || -e + max.y < 0.0f) return {};
}
}
{
glm::vec3 zaxis(modelMatrix[2].x, modelMatrix[2].y, modelMatrix[2].z);
float e = glm::dot(zaxis, delta);
float f = glm::dot(direction, zaxis);
if (fabs(f) > 0.001f) {
float t1 = (e + min.z) / f;
float t2 = (e + max.z) / f;
if (t1 > t2) std::swap(t1, t2);
if (t2 < tMax) tMax = t2;
if (t1 > tMin) tMin = t1;
if (tMin > tMax) return {};
}
else {
if (-e + min.z > 0.0f || -e + max.z < 0.0f) return {};
}
}
return tMin;
}
I use it to click on cubes in OpenGL. The test case is a cube with min and max of (-1, -1, -1) and (1, 1, 1). Now this works fine when rotating and translating the modelMatrix but it does not seem to take scale into account. I've tried pre-multiplying the min and max with the modelMatrix scale but to no avail. Anyone know what's going on?

Related

How do I choose a correct Epsilon for computing ray intersections?

Good day,
I am trying to compute if a ray hits a plane and then a triangle for my ray tracer. I do not know how to choose an epsilon value to account for floating point errors. Triangles can get pretty small and some of them are missed by my rays.
struct Ray {
glm::vec3 origin { 0.0f };
glm::vec3 direction { 0.0f, 0.0f, -1.0f };
float t { std::numeric_limits<float>::max() };
};
struct Plane {
float D = 0.0f;
glm::vec3 normal { 0.0f, 1.0f, 0.0f };
};
bool pointInTriangle(const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& n, const glm::vec3& p)
{
Plane plane = trianglePlane(v0,v1,v2);
if (glm::dot(plane.normal, p) - plane.D == 0)
{
std::cout << p[0] << " " << p[1] << " " << p[2] << std::endl;
float area0 = glm::length(glm::cross(v0 - v1, v0 - v2)) / 2;
float a1 = glm::length(glm::cross(v2 - v1, p - v1)) / 2 / area0;
float a2 = glm::length(glm::cross( v0 - v2, p - v2)) / 2 / area0;
float a3 = glm::length(glm::cross(v1 - v0, p - v0)) / 2 / area0;
if (a1 < 0)
return false;
if (a2 < 0)
return false;
if (a3 < 0)
return false;
if (a1 + a2 + a3 > 1)
return false;
if (a1 + a2 + a3 < 1)
return false;
return true;
}
return false;
}
bool intersectRayWithPlane(const Plane& plane, Ray& ray)
{
float t = (plane.D - glm::dot(ray.origin, plane.normal)) / glm::dot(plane.normal, ray.direction);
if (t <= 0)
return false;
if (t < ray.t)
{
if (glm::dot((ray.origin + ray.direction * t), plane.normal) - plane.D == 0)
{
ray.t = t;
return true;
}
}
return false;
}

Have slight issue with edge in self made triangle rasterizer C++

The triangles are mostly ok but one or 2 triangles would have a missing pixel between them
which probably means theres a issue with edge cases but i cant figure it out
the white spaces only happen on the edge of the triangles so i assume there is something wrong with either my edge equation or my top left rule but im really lost on what is
wrong with it
struct EdgeEqn {
float a, b, c;
bool tl;
};
GLPbo::EdgeEqn construct(const glm::vec3 a, const glm::vec3& b) {
GLPbo::EdgeEqn x = { a.y - b.y,b.x - a.x,(b.y - a.y) * a.x - (b.x - a.x) * a.y };
if (x.a > 0) {
x.tl = true;
}
else if (x.a < 0) {
x.tl = false;
}
else if (x.b < 0) {
x.tl = true;
}
else {
x.tl = false;
}
return x;
}
bool edgeFunction(GLPbo::EdgeEqn x, const glm::vec2& c, float& line_eval)
{
float eval = x.a * c.x + x.b * c.y + x.c;
line_eval = eval;
return (eval > 0.f || (eval == 0.f && x.tl))
? true : false;
}
bool Topleft_increase(GLPbo::EdgeEqn x, float eval) {
return (eval > 0.f || (eval == 0.f && x.tl))
? true : false;
}
bool GLPbo::render_triangle(glm::vec3 const& p0, glm::vec3 const& p1,
glm::vec3 const& p2, glm::vec3 clr) {
if (((p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y)) < 0) {
culling++;
return false;
}
GLPbo::Color random = { static_cast<GLubyte>(clr.r), static_cast<GLubyte>(clr.g)
, static_cast<GLubyte>(clr.b), 255 };
int min_x = static_cast<int>(std::min({ p0.x, p1.x, p2.x }));
int min_y = static_cast<int>(std::min({ p0.y, p1.y, p2.y }));
int max_x = static_cast<int>(std::max({ p0.x, p1.x, p2.x }));
int max_y = static_cast<int>(std::max({ p0.y, p1.y, p2.y }));
GLPbo::EdgeEqn l0 = construct(p1, p2);
GLPbo::EdgeEqn l1 = construct(p2, p0);
GLPbo::EdgeEqn l2 = construct(p0, p1);
float ev0 = 0, ev1 = 0, ev2 = 0;
bool e0 = edgeFunction(l0, glm::vec2(min_x + 0.5, min_y + 0.5), ev0);
bool e1 = edgeFunction(l1, glm::vec2(min_x + 0.5, min_y + 0.5), ev1);
bool e2 = edgeFunction(l2, glm::vec2(min_x + 0.5, min_y + 0.5), ev2);
for (int y = min_y; y <= max_y; ++y) {
bool hE0 = e0; bool hE1 = e1; bool hE2 = e2;
float hEv0 = ev0; float hEv1 = ev1; float hEv2 = ev2;
for (int x = min_x; x <= max_x; ++x) {
if (hE0 == true && hE1 == true && hE2 == true) {
set_pixel(x, y, random);
}
hEv0 += l0.a;
hEv1 += l1.a;
hEv2 += l2.a;
hE0 = Topleft_increase(l0, hEv0);
hE1 = Topleft_increase(l1, hEv1);
hE2 = Topleft_increase(l2, hEv2);
}
ev0 += l0.b;
ev1 += l1.b;
ev2 += l2.b;
e0 = Topleft_increase(l0, ev0);
e1 = Topleft_increase(l1, ev1);
e2 = Topleft_increase(l2, ev2);
}
return true;
}

GLSL Sphere - ray intersection geometric solution

I'm trying to implement sphere ray intersection in GLSL, both the geometric and analytical solution. I'm having trouble solving the geom one, it should have something to do with how I return true or false:
bool hitSphere(Ray ray, Sphere sphere, float t_min, float t_max, out float t_out) {
// Geometric solution
float R2 = sphere.radius * sphere.radius;
vec3 L = sphere.position - ray.origin;
float tca = dot(L, normalize(ray.direction));
// if(tca < 0) return false;
float D2 = dot(L, L) - tca * tca;
if(D2 > R2) return false;
float thc = sqrt(R2 - D2);
float t0 = tca - thc;
float t1 = tca + thc;
if(t0 < t_max && t0 > t_min) {
t_out = t0;
return true;
}
if(t1 < t_max && t1 > t_min) {
t_out = t1;
return true;
}
return false;
}
I think the problem is with how I deal with t0 and t1 for none, one or both intersection cases.
Edit: the analytic version that does work:
vec3 oc = ray.origin - sphere.position;
float a = dot(ray.direction, ray.direction);
float b = dot(oc, ray.direction);
float c = dot(oc, oc) - sphere.radius * sphere.radius;
float discriminant = b * b - a * c;
if (discriminant > 0.0f) {
if(b > 0)
t_out = (-b + sqrt(discriminant)) / a;
else
t_out = (-b - sqrt(discriminant)) / a;
if(t_out < t_max && t_out > t_min) {
return true;
}
}
return false;
The issue is caused by t_out. The algorithm has to compute t_out in that way, that X is the intersected point of the ray and the surface of the sphere, for:
X = ray.origin + ray.direction * t_out;
In the working algorithm t_out depends on the length of ray.direction. t_out becomes smaller, if the magnitude of the vector ray.direction is greater.
In the algorithm, which doesn't work, ray.direction is normalized.
float tca = dot(L, normalize(ray.direction));
Hence t_out is computed for a ray direction length of 1. Actually you compute a t_out' where t_out' = t_out * length(ray.direction).
Divide t0 respectively t1 by the length of ray.direction:
bool hitSphere_2(Ray ray, Sphere sphere, float t_min, float t_max, out float t_out)
{
float R2 = sphere.radius * sphere.radius;
vec3 L = sphere.position - ray.origin;
float tca = dot(L, normalize(ray.direction));
// if(tca < 0) return false;
float D2 = dot(L, L) - tca * tca;
if(D2 > R2) return false;
float thc = sqrt(R2 - D2);
float t0 = tca - thc;
float t1 = tca + thc;
if (t0 < t_max && t0 > t_min) {
t_out = t0 / length(ray.direction); // <---
return true;
}
if (t1 < t_max && t1 > t_min) {
t_out = t1 / length(ray.direction); // <---
return true;
}
return false;
}

Inverse sampling from a Lambertian surface

In the book Physically Based Rendering, a Lambertian surface is sampled in the following way (see http://www.pbr-book.org/3ed-2018/Light_Transport_I_Surface_Reflection/Sampling_Reflection_Functions.html#):
void Sample_f(Vector3f const& wo, Vector3f* wi, const Point2f& u)
{
// Cosine-sample the hemisphere, flipping the direction if necessary
*wi = CosineSampleHemisphere(u);
if (wo.z < 0) wi->z *= -1;
}
inline Vector3f CosineSampleHemisphere(Point2f const& u)
{
Point2f d = ConcentricSampleDisk(u);
Float z = std::sqrt(std::max((Float)0, 1 - d.x * d.x - d.y * d.y));
return Vector3f(d.x, d.y, z);
}
Point2f ConcentricSampleDisk(Point2f const& u)
{
// Map uniform random numbers to $[-1,1]^2$
Point2f uOffset = 2.f * u - Vector2f(1, 1);
// Handle degeneracy at the origin
if (uOffset.x == 0 && uOffset.y == 0) return Point2f(0, 0);
// Apply concentric mapping to point
Float theta, r;
if (std::abs(uOffset.x) > std::abs(uOffset.y)) {
r = uOffset.x;
theta = PiOver4 * (uOffset.y / uOffset.x);
} else {
r = uOffset.y;
theta = PiOver2 - PiOver4 * (uOffset.x / uOffset.y);
}
return r * Point2f(std::cos(theta), std::sin(theta));
}
What I want to do now is, given wo and wi, compute u such that the invocation of Sample_f(wo, &wi_other, u) yields wi_other == wi (at least approximately).
While it's not hard to basically solve this problem, my solution is suffering from floating-point imprecision. If you are familiar with ray tracing: If a ray following the accurately computed direction wi hits a surface point p, it might turn out that approximately computed direction wi_other closely misses the whole surface on which p is located.
This is my solution so far:
Point2f invert_sample_f(pbrt::Vector3f wi, pbrt::Vector3f const& wo)
{
if (wo.z < 0)
wi.z *= -1;
return cosine_sample_hemisphere_inverse(wi);
}
template<typename RealType = pbrt::Float>
pbrt::Point2<RealType> cosine_sample_hemisphere_inverse(pbrt::Vector3<RealType> const& w) {
return concentric_map_inverse<RealType>({ w.x, w.y });
}
template<typename RealType = pbrt::Float>
pbrt::Point2<RealType> concentric_map_inverse(pbrt::Point2<RealType> u)
{
u = cartesian_to_polar(u);
auto const& r = u.x;
auto& phi = u.y;
if (r == 0)
return { 0, 0 };
// wrap ϕ -> [-π/4, 7π/4)
if (phi >= 7 * pbrt::PiOver4)
phi -= 2 * pbrt::Pi;
if (-pbrt::PiOver4 < phi && phi < pbrt::PiOver4)
{// sector 1
u = { r, r * phi / pbrt::PiOver4 };
}
else if (pbrt::PiOver4 <= phi && phi <= 3 * pbrt::PiOver4)
{// sector 2
u = { r * (2 - phi / pbrt::PiOver4), r };
}
else if (3 * pbrt::PiOver4 < phi && phi < 5 * pbrt::PiOver4)
{// sector 3
u = { -r, r * (4 - phi / pbrt::PiOver4) };
}
else // 5 * pbrt::PiOver4 <= phi && phi <= -pbrt::PiOver4
{// sector 4
u = { r * (phi / pbrt::PiOver4 - 6), -r };
}
return (u + pbrt::Vector2<RealType>{ 1, 1 }) / 2;
}
template<typename RealType = pbrt::Float>
pbrt::Point2<RealType> cartesian_to_polar(pbrt::Point2<RealType> const& p)
{
auto const &x = p.x,
&y = p.y;
RealType phi;
if (x < 0)
phi = pbrt::Pi + std::atan(y / x);
else if (x > 0)
phi = y < 0 ? 2 * pbrt::Pi + std::atan(y / x) : std::atan(y / x);
else // x == 0
phi = y < 0 ? 3 * pbrt::PiOver2 : pbrt::PiOver2;
RealType const r = std::sqrt(x * x + y * y);
return { r, phi };
}
Can we somehow decrease the error of the solution?

Problem with triangle-triangle collision detection [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I'm developing a game engine for a university project and I can't get the collision detection system to work. I've found this paper that explains an algorithm for triangle-triangle collision detection created by Chaman-Leopoldj, but somehow I can't implement it. I know it is a bit long but the algorithm can be found at pages 8 and 22-24
here is the code I wrote:
this is the wrapper function
bool Octree::triangleTriangleIntersection(glm::vec3 A, glm::vec3 B, glm::vec3 C, glm::vec3 P, glm::vec3 Q, glm::vec3 R) {
glm::vec3 U = B - A;
glm::vec3 V = C - A;
glm::vec3 S = Q - P;
glm::vec3 T = R - P;
glm::vec3 AP = P - A;
float sigma = dot(U * V, U * V);
glm::vec3 alpha = (S * (U * V)) / sigma;
glm::vec3 beta = (T * (U * V)) / sigma;
glm::vec3 gamma = (AP * (U * V)) / sigma;
float alphau = dot(alpha, U);
float alphav = dot(alpha, V);
float alphauv = dot(alpha, U - V);
float gammau = dot(gamma, U);
float gammav = dot(gamma, V);
float gammauv = dot(gamma, U - V);
float betau = dot(beta, U);
float betav = dot(beta, V);
float betauv = dot(beta, U - V);
float Xm, XM, Sm = 0, SM = 1;
float Ym, YM, Tm = 0, TM = 1;
if (findSolution_x(-gammau, alphau, betau, 1 - gammau, -1 - gammav, alphav, betav, -gammav, Xm, XM)) {
if (Xm > Sm) Sm = Xm;
if (XM < SM) SM = XM;
}
else {
return false;
}
if (findSolution_x(-gammau, alphau, betau, 1 - gammau, -gammauv, alphauv, betauv, 1 - gammauv, Xm, XM)) {
if (Xm > Sm) Sm = Xm;
if (XM < SM) SM = XM;
}
else {
return false;
}
if (findSolution_x(-1 - gammav, alphav, betav, -gammav, -gammauv, alphauv, betauv, 1 - gammauv, Xm, XM)) {
if (Xm > Sm) Sm = Xm;
if (XM < SM) SM = XM;
}
else {
return false;
}
if (Sm > SM)
return false;
else {
float delta = (SM - Sm) / 20;
for (float s = Sm; s <= SM; s += delta) {
if (findSolution_y(-gammau, alphau, betau, 1 - gammau, -1 - gammav, alphav, betav, -gammav, s, Ym, YM)) {
if (Ym > Tm) Tm = Ym;
if (YM < TM) TM = YM;
}
else {
return false;
}
if (findSolution_y(-gammau, alphau, betau, 1 - gammau, -gammauv, alphauv, betauv, 1 - gammauv, s, Ym, YM)) {
if (Ym > Tm) Tm = Ym;
if (YM < TM) TM = YM;
}
else {
return false;
}
if (findSolution_y(-1 - gammav, alphav, betav, -gammav, -gammauv, alphauv, betauv, 1 - gammauv, s, Ym, YM)) {
if (Ym > Tm) Tm = Ym;
if (YM < TM) TM = YM;
}
else {
return false;
}
if (Tm > TM)
return false;
else
return true;
}
}
return false;}
solve_x
bool Octree::findSolution_x(float m, float a, float b, float n, float M, float A, float B, float N, float& Xm, float& XM) {
const float epsilon = 0.00001;
float denom = (a*B- A* b);
float Sm1, SM1;
Sm1 = (m* B- N* b);
SM1 = (n* B- M* b);
if (b< 0 || B< 0) {
Sm1 *= -1;
SM1 *= -1;
}
Sm1 /= denom;
SM1 /= denom;
float Sm1Rounded = round(Sm1);
float SM1Rounded = round(SM1);
if (abs(Sm1Rounded - Sm1 <= epsilon)) Sm1 = Sm1Rounded;
if (abs(SM1Rounded - SM1 <= epsilon)) SM1 = SM1Rounded;
Xm = Sm1;
XM = SM1;
if (denom == 0) {
Xm *= -1;
}
return true;}
solve_y
bool Octree::findSolution_y(float m, float a, float b, float n, float M, float A, float B, float N, float x, float& Ym, float& YM) {
const float epsilon = 0.00001;
float Sm1, SM1, Sm2, SM2;
Sm1 = m- (a* x);
Sm2 = M- (A* x);
SM1 = n- (a* x);
SM2 = N- (A* x);
if (b< 0 || B< 0) {
Sm1 *= -1;
SM1 *= -1;
Sm2 *= -1;
SM2 *= -1;
}
if (Sm1 > SM1 || Sm2 > SM2) return false;
Sm1 /= b;
SM1 /= b;
Sm2 /= B;
SM2 /= B;
float Sm1Rounded = round(Sm1);
float SM1Rounded = round(SM1);
float Sm2Rounded = round(Sm2);
float SM2Rounded = round(SM2);
if (abs(Sm1Rounded - Sm1 <= epsilon)) Sm1 = Sm1Rounded;
if (abs(SM1Rounded - SM1 <= epsilon)) SM1 = SM1Rounded;
if (abs(Sm2Rounded - Sm2 <= epsilon)) Sm2 = Sm2Rounded;
if (abs(SM2Rounded - SM2 <= epsilon)) SM2 = SM2Rounded;
if (param2 > 0 && param6 > 0) {
Sm1 >= Sm2 ? Ym = Sm1 : Ym = Sm2;
SM1 >= SM2 ? YM = SM2 : YM = SM1;
}
else if (param2 > 0) {
Ym = Sm1;
YM = SM1;
}
else if (param6 > 0) {
Ym = Sm2;
YM = SM2;
}
return true;}
I suspect I've put wrong conditions in one of my ifs but I just followed the guide lines of the paper so I really don't know. Hope you guys can help me.
EDIT: the epsilon is needed to round values below certain error. this is a problem deriving from assimp not reading values of OBJs properly, turning a 1.000000 into 1.0000045 for example.
I'm not going to try to debug your code for you, and someone is going to downvote me for an incomplete answer, but I'm going to offer some basic advice.
This is basic advice on debugging something this big. In my opinion, you need to set up a simple test. Write a tiny program that links with your code. Create your two triangles manually that you know collide, and then see if your code detects it.
No? Figure out HOW they collide and HOW you should have detected it, and then add print statements to your code where it should be colliding, and see why it isn't catching it.
What you might want to do is use some paper. Lay out a couple of triangles and then manually (no computer involved) step through the code you're using and see if it makes sense.
And if it doesn't, come up with your own code. I think you could define triangle collision as:
If any segment of T1 intersects with any segment of T2. You should be able to figure out a way of testing if two line segments intersect, and then just run all segments of T1 against T2.
OR if one triangle is entirely encapsulated inside the other. A big triangle and a little triangle.
This is part of the joy and frustration of coding -- learning to understand the algorithms you're using.