Problem with triangle-triangle collision detection [closed] - c++

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.

Related

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?

Ray tracing sphere reflection bug

I am trying to implement the ray tracing algorithm and I have some trouble computing the reflected rays of spherical objects.It seems that
for some particular rays, the reflected ray just passes through and is collinear with the traced ray.
Bellow is how i record the ray - sphere intersection:
bool Sphere::intersectLocal(const ray & r, isect & i) const {
Vec3d P = r.getPosition();
Vec3d D = r.getDirection();
//D.normalize();
double a = dot(D, D);
double b = 2 * dot(P, D);
double c = dot(P, P) - 1;
double delta = b * b - 4 * a * c;
if (delta < 0)
return false;
if (delta == 0) {
double t = -b / 2 * a;
Vec3d Q = P + t * D;
Vec3d N = Q;
N.normalize();
i.setT(t);
i.setN(N);
i.setObject(this);
return true;
}
if (delta > 0) {
double t1 = (-b - sqrt(delta)) / 2 * a;
double t2 = (-b + sqrt(delta)) / 2 * a;
double t;
if (t1 > 0) t = t1;
else if (t2 > 0) t = t2;
else return false;
Vec3d N = P + t * D;
N.normalize();
i.setT(t);
i.setN(N);
i.setObject(this);
return true;
}
return false;
}
And this is how I compute the reflected ray for each intersection:
isect i;
if (scene - > intersect(r, i)) {
// An intersection occured!
const Material & m = i.getMaterial();
double t = i.t;
Vec3d N = i.N;
Vec3d I = m.shade(scene, r, i); //local illumination
if (!m.kr(i).iszero() && depth >= 0) {
// compute reflection direction
Vec3d raydir = r.getDirection();
Vec3d refldir = 2 * dot(-raydir, i.N) * i.N + raydir;
refldir.normalize();
ray reflectionRay = ray(r.at(i.t), refldir, ray::RayType::REFLECTION);
Vec3d reflection = traceRay(reflectionRay, thresh, depth - 1);
Vec3d R = reflection;
I += R;
}
return I;
} else {
// No intersection. This ray travels to infinity, so we color
// it according to the background color, which in this (simple) case
// is just black.
return Vec3d(0.0, 0.0, 0.0);
}
The code above seems to work fine for most of the points on the sphere where the rays intersect, but for others it does not reflect as i expected
If I see right, this makes the normal face same direction as the ray. So with ray==normal==reflected_ray nothing gets reflected.
Vec3d Q = P + t * D;
Vec3d N = Q;
About errors in floating-point arithmetic and how to deal with it:
What Every Computer Scientist Should Know About Floating-Point Arithmetic
Here you can find how to compare floating-point numbers. Searching for relative absolute compare floating you may find more information.
https://floating-point-gui.de/errors/comparison/
This is an excerpt from my code in C#. Almost never use absolute compare.
public static bool IsAlmostRelativeEquals(this double d1, double d2, double epsilon)
{
double absDiff = Math.Abs(d1 - d2);
if (double.IsPositiveInfinity(absDiff))
return false;
if (absDiff < epsilon)
return true;
double absMax = Math.Max(Math.Abs(d1), Math.Abs(d2));
return Math.Abs(d1 - d2) <= epsilon * absMax;
}
public static bool IsAlmostZero(this double d, double epsilon)
{
double abs = Math.Abs(d);
if (double.IsPositiveInfinity(abs))
return false;
return abs < epsilon;
}

Adding unused formal parameters to C++ method results in different behavior

When I add some extra formal parameters double tmin=0.0, double tmax=0.0 to the constructor of the Ray in the code below, I always obtain a wrong image with a white top border. These formal parameters currently contribute in no way (i.e. are unused) to the code. So how is it possible to obtain a different image?
System specifications:
OS: Windows 8.1
Compiler: MSVC 2015
Code:
#include "stdafx.h"
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <random>
std::default_random_engine generator(606418532);
std::uniform_real_distribution<double> distribution = std::uniform_real_distribution<double>(0.0, 1.0);
double erand48(unsigned short *x) {
return distribution(generator);
}
#define M_PI 3.14159265358979323846
struct Vector3 {
double x, y, z;
Vector3(double x_ = 0, double y_ = 0, double z_ = 0) { x = x_; y = y_; z = z_; }
Vector3 operator+(const Vector3 &b) const { return Vector3(x + b.x, y + b.y, z + b.z); }
Vector3 operator-(const Vector3 &b) const { return Vector3(x - b.x, y - b.y, z - b.z); }
Vector3 operator*(double b) const { return Vector3(x*b, y*b, z*b); }
Vector3 mult(const Vector3 &b) const { return Vector3(x*b.x, y*b.y, z*b.z); }
Vector3& norm() { return *this = *this * (1 / sqrt(x*x + y*y + z*z)); }
double Dot(const Vector3 &b) const { return x*b.x + y*b.y + z*b.z; } // cross:
Vector3 operator%(Vector3&b) { return Vector3(y*b.z - z*b.y, z*b.x - x*b.z, x*b.y - y*b.x); }
};
//struct Ray { Vector3 o, d; Ray(const Vector3 &o_, const Vector3 &d_, double tmin=0.0, double tmax=0.0) : o(o_), d(d_) {} };
struct Ray { Vector3 o, d; Ray(const Vector3 &o_, const Vector3 &d_) : o(o_), d(d_) {} };
enum Reflection_t { DIFFUSE, SPECULAR, REFRACTIVE };
struct Sphere {
double rad; // radius
Vector3 p, e, f; // position, emission, color
Reflection_t reflection_t; // reflection type (DIFFuse, SPECular, REFRactive)
Sphere(double rad_, Vector3 p_, Vector3 e_, Vector3 f_, Reflection_t reflection_t) :
rad(rad_), p(p_), e(e_), f(f_), reflection_t(reflection_t) {}
double intersect(const Ray &r) const {
Vector3 op = p - r.o;
double t, eps = 1e-4, b = op.Dot(r.d), det = b*b - op.Dot(op) + rad*rad;
if (det<0) return 0; else det = sqrt(det);
return (t = b - det)>eps ? t : ((t = b + det)>eps ? t : 0);
}
};
Sphere spheres[] = {
Sphere(1e5, Vector3(1e5 + 1,40.8,81.6), Vector3(),Vector3(.75,.25,.25),DIFFUSE),//Left
Sphere(1e5, Vector3(-1e5 + 99,40.8,81.6),Vector3(),Vector3(.25,.25,.75),DIFFUSE),//Rght
Sphere(1e5, Vector3(50,40.8, 1e5), Vector3(),Vector3(.75,.75,.75),DIFFUSE),//Back
Sphere(1e5, Vector3(50,40.8,-1e5 + 170), Vector3(),Vector3(), DIFFUSE),//Frnt
Sphere(1e5, Vector3(50, 1e5, 81.6), Vector3(),Vector3(.75,.75,.75),DIFFUSE),//Botm
Sphere(1e5, Vector3(50,-1e5 + 81.6,81.6),Vector3(),Vector3(.75,.75,.75),DIFFUSE),//Top
Sphere(16.5,Vector3(27,16.5,47), Vector3(),Vector3(1,1,1)*.999, SPECULAR),//Mirr
Sphere(16.5,Vector3(73,16.5,78), Vector3(),Vector3(1,1,1)*.999, REFRACTIVE),//Glas
Sphere(600, Vector3(50,681.6 - .27,81.6),Vector3(12,12,12), Vector3(), DIFFUSE) //Lite
};
inline double clamp(double x) { return x<0 ? 0 : x>1 ? 1 : x; }
inline int toInt(double x) { return int(pow(clamp(x), 1 / 2.2) * 255 + .5); }
inline bool intersect(const Ray &r, double &t, int &id) {
double n = sizeof(spheres) / sizeof(Sphere), d, inf = t = 1e20;
for (int i = int(n); i--;) if ((d = spheres[i].intersect(r)) && d<t) { t = d; id = i; }
return t<inf;
}
Vector3 radiance(const Ray &r_, int depth_, unsigned short *Xi) {
double t; // distance to intersection
int id = 0; // id of intersected object
Ray r = r_;
int depth = depth_;
Vector3 cl(0, 0, 0); // accumulated color
Vector3 cf(1, 1, 1); // accumulated reflectance
while (1) {
if (!intersect(r, t, id)) return cl; // if miss, return black
const Sphere &obj = spheres[id]; // the hit object
Vector3 x = r.o + r.d*t, n = (x - obj.p).norm(), nl = n.Dot(r.d)<0 ? n : n*-1, f = obj.f;
double p = f.x>f.y && f.x>f.z ? f.x : f.y>f.z ? f.y : f.z; // max refl
cl = cl + cf.mult(obj.e);
if (++depth>5) if (erand48(Xi)<p) f = f*(1 / p); else return cl; //R.R.
cf = cf.mult(f);
if (obj.reflection_t == DIFFUSE) { // Ideal DIFFUSE reflection
double r1 = 2 * M_PI*erand48(Xi), r2 = erand48(Xi), r2s = sqrt(r2);
Vector3 w = nl, u = ((fabs(w.x)>.1 ? Vector3(0, 1) : Vector3(1)) % w).norm(), v = w%u;
Vector3 d = (u*cos(r1)*r2s + v*sin(r1)*r2s + w*sqrt(1 - r2)).norm();
r = Ray(x, d);
continue;
}
else if (obj.reflection_t == SPECULAR) {
r = Ray(x, r.d - n * 2 * n.Dot(r.d));
continue;
}
Ray reflRay(x, r.d - n * 2 * n.Dot(r.d));
bool into = n.Dot(nl)>0;
double nc = 1, nt = 1.5, nnt = into ? nc / nt : nt / nc, ddn = r.d.Dot(nl), cos2t;
if ((cos2t = 1 - nnt*nnt*(1 - ddn*ddn))<0) {
r = reflRay;
continue;
}
Vector3 tdir = (r.d*nnt - n*((into ? 1 : -1)*(ddn*nnt + sqrt(cos2t)))).norm();
double a = nt - nc, b = nt + nc, R0 = a*a / (b*b), c = 1 - (into ? -ddn : tdir.Dot(n));
double Re = R0 + (1 - R0)*c*c*c*c*c, Tr = 1 - Re, P = .25 + .5*Re, RP = Re / P, TP = Tr / (1 - P);
if (erand48(Xi)<P) {
cf = cf*RP;
r = reflRay;
}
else {
cf = cf*TP;
r = Ray(x, tdir);
}
continue;
}
}
int main(int argc, char *argv[]) {
int w = 512, h = 384, samps = argc == 2 ? atoi(argv[1]) / 4 : 1; // # samples
Ray cam(Vector3(50, 52, 295.6), Vector3(0, -0.042612, -1).norm()); // cam pos, dir
Vector3 cx = Vector3(w*.5135 / h), cy = (cx%cam.d).norm()*.5135, r, *c = new Vector3[w*h];
#pragma omp parallel for schedule(dynamic, 1) private(r) // OpenMP
for (int y = 0; y<h; y++) { // Loop over image rows
fprintf(stderr, "\rRendering (%d spp) %5.2f%%", samps * 4, 100.*y / (h - 1));
for (unsigned short x = 0, Xi[3] = { 0,0,y*y*y }; x<w; x++) // Loop cols
for (int sy = 0, i = (h - y - 1)*w + x; sy<2; sy++) // 2x2 subpixel rows
for (int sx = 0; sx<2; sx++, r = Vector3()) { // 2x2 subpixel cols
for (int s = 0; s<samps; s++) {
double r1 = 2 * erand48(Xi), dx = r1<1 ? sqrt(r1) - 1 : 1 - sqrt(2 - r1);
double r2 = 2 * erand48(Xi), dy = r2<1 ? sqrt(r2) - 1 : 1 - sqrt(2 - r2);
Vector3 d = cx*(((sx + .5 + dx) / 2 + x) / w - .5) +
cy*(((sy + .5 + dy) / 2 + y) / h - .5) + cam.d;
r = r + radiance(Ray(cam.o + d * 140, d.norm()), 0, Xi)*(1. / samps);
} // Camera rays are pushed ^^^^^ forward to start in interior
c[i] = c[i] + Vector3(clamp(r.x), clamp(r.y), clamp(r.z))*.25;
}
}
FILE *fp;
fopen_s(&fp, "image.ppm", "w"); // Write image to PPM file.
fprintf(fp, "P3\n%d %d\n%d\n", w, h, 255);
for (int i = 0; i<w*h; i++)
fprintf(fp, "%d %d %d ", toInt(c[i].x), toInt(c[i].y), toInt(c[i].z));
}
First Ray structure:
struct Ray { Vector3 o, d; Ray(const Vector3 &o_, const Vector3 &d_) : o(o_), d(d_) {} };
Results in:
Second Ray structure:
struct Ray { Vector3 o, d; Ray(const Vector3 &o_, const Vector3 &d_, double tmin=0.0, double tmax=0.0) : o(o_), d(d_) {} };
Results in:
The last image has a noticeable white top border which is not present in the first image.
Edit:
I used
size_t n = sizeof(spheres) / sizeof(Sphere);
Now I obtain the same images, but I also checked if the original int(n) could differ from 9 which is never the case.
Ok this is from the Debug build, which is different from the Release build.
Sounds like a memory error, looking quickly at your code I'm sceptical of this line:
for (int i = int(n); i--;) if ((d = spheres[i].intersect(r)) && d<t)
I suspect accessing sphere[i] is out of bounds, perhaps you should try sphere[i-1]. You could also try compiling your code with a compiler that adds extra code for debugging/sanitising/checking memory addresses.

Continous angles in C++ (eq unwrap function in matlab)

I guess it is not that hard, but I have been stuck on that one for a while.
I have a joint that can rotate both direction. A sensor gives me the angle of the joint in the range -pi and +pi.
I would like to convert it in the range -infinity and +infinity. Meaning that if for example the joint rotate clockwise forever, the angle would start at 0 and then increase to infinity.
In matlab, the unwrap function does that very well:
newAngle = unwrap([previousAngle newAngle]);
previousAngle = newAngle;
Note: it is assumed the angle does not make big jump, nothing superior to PI for sure.
Note: I really looked hard before asking ...
Thanks !
The following function does the job, assuming the absolute difference between the input angles is less than 2*pi:
float unwrap(float previous_angle, float new_angle) {
float d = new_angle - previous_angle;
d = d > M_PI ? d - 2 * M_PI : (d < -M_PI ? d + 2 * M_PI : d);
return previous_angle + d;
}
If you need to unwrap an array, you can use this routine:
void unwrap_array(float *in, float *out, int len) {
out[0] = in[0];
for (int i = 1; i < len; i++) {
float d = in[i] - in[i-1];
d = d > M_PI ? d - 2 * M_PI : (d < -M_PI ? d + 2 * M_PI : d);
out[i] = out[i-1] + d;
}
}
After some work, came up with this. Seems to be working fine.
//Normalize to [-180,180):
inline double constrainAngle(double x){
x = fmod(x + M_PI,M_2PI);
if (x < 0)
x += M_2PI;
return x - M_PI;
}
// convert to [-360,360]
inline double angleConv(double angle){
return fmod(constrainAngle(angle),M_2PI);
}
inline double angleDiff(double a,double b){
double dif = fmod(b - a + M_PI,M_2PI);
if (dif < 0)
dif += M_2PI;
return dif - M_PI;
}
inline double unwrap(double previousAngle,double newAngle){
return previousAngle - angleDiff(newAngle,angleConv(previousAngle));
}
I used code from this post:
Dealing with Angle Wrap in c++ code
// wrap to [-pi,pi]
inline double angle_norm(double x)
{
x = fmod(x + M_PI, M_2PI);
if (x < 0)
x += M_2PI;
return x - M_PI;
}
double phase_unwrap(double prev, double now)
{
return prev + angle_norm(now - prev);
}
This works.