bool Sphere::intersect(const Ray& ray, Hit& hit) const { float a = (ray.direction.normalized()).dot(ray.direction.normalized()); float b = 2.0f * (ray.direction.normalized()).dot(ray.origin - mCenter); float c = (ray.origin - mCenter).dot(ray.origin - mCenter) - (mRadius * mRadius); float discriminant = b * b - 4 * a * c; if (discriminant > 0) { float s1 = (- b + sqrtf(discriminant)) / 2 * a; float s2 = (- b - sqrtf(discriminant)) / 2 * a; // Tester le chemin le plus court ? if (s1 > 0) { if (s1 < s2 && hit.t() > s1) { hit.setT(s1); hit.setIntersection(ray.direction * s1); } } if (s2 > 0) { if (s2 < s1 && hit.t() > s2) { hit.setT(s2); hit.setIntersection(ray.direction * s2); } } } else if (discriminant = 0) { float s = - b / 2 * a; if (hit.t() > s) { hit.setT(s); hit.setIntersection(ray.direction * s); } } else { return false; } return true; }
bool Cylindre::intersect(const Ray& ray, Hit& hit) const { /*bool top = topdisque()->intersect(ray,hit); bool bottom = bottomdisque()->intersect(ray,hit); return top || bottom; */ // Determination de l'intersection // J'appelle I le point intersection s'il existe, O le point d'origine du rayon, D sa direction, R1 et R1 les rayons du cylindre, h sa hauteur // C l'origine du repere basé à la base du cylindre // on place le ray dans le répère associé au cylindre Ray ray_associe_cylindre(frame().coordinatesOf(ray.start()), frame().transformOf(ray.direction())); Vec I; Vec O = ray_associe_cylindre.start(); const Vec& D = ray_associe_cylindre.direction(); float R1,R2; R1 = bottomradius_; R2 = topradius_; float h = height_; #ifdef DEBUG_INTERSEC std::cout << "TENTATIVE d'intersection du rayon :\n" << ray << "avec le cylindre\n" << *this << std::endl; #endif #ifdef DEBUG_INTERSEC std::cout << "rayon dans l'espace du cylindre : \n" << ray_associe_cylindre << std::endl; #endif // on vérifie que le ray pointe vers le cylindre if ( (frame().position() - ray.start())*ray.direction() < 0){ #ifdef DEBUG_INTERSEC std::cout << "PAS D'INTERSECTION : direction pointant en arrière" << std::endl; #endif return false; } // On se place dans les coordonnées du cylindre pour tous les calculs et on a les équations suivantes : // Ix = Ox + tDx // Iy = Oy + tDy // Iz = Oz + tDz // Ix² + Iy² = (Iz/h*(R2-R1)+R1)² <=> Ix² + Iy² = Iz²/h²*(R2-R1)² + R1² + 2*(R2-R1)*R1*Iz/h // on remplace Ix, Iy et Iz dans la 4e equation pour obtenir une equation du 2nd degré en t // (Dx² + Dy² - Dz²*(R2-R1)²/h²)*t² + (2OxDx + 2OyDy - (2OzDz)/h²*(R2-R1)² - 2Dz*(R2-R1)/h)*t + (Ox²+Oy²-Oz²/h²*(R2-R1)²-R1²-2*(R2-R1)*Oz/h = 0 float a, b ,c; if (R1>R2){ a = D.x*D.x + D.y*D.y -D.z*D.z*(R2-R1)*(R2-R1)/(h*h); b = 2*O.x*D.x+2*O.y*D.y - (2*O.z*D.z)*(R2-R1)*(R2-R1)/(h*h) - 2*D.z*R1*(R2-R1)/h; c = O.x*O.x+O.y*O.y-O.z*O.z*(R2-R1)*(R2-R1)/(h*h)-2*R1*(R2-R1)*O.z/h -R1*R1; }else { a = D.x*D.x + D.y*D.y -D.z*D.z*(R2-R1)*(R2-R1)/(h*h); b = 2*O.x*D.x+2*O.y*D.y - (2*O.z*D.z)*(R2-R1)*(R2-R1)/(h*h) - 2*D.z*R1*(R2-R1)/h; c = O.x*O.x+O.y*O.y-O.z*O.z*(R2-R1)*(R2-R1)/(h*h)-2*R1*(R2-R1)*O.z/h -R1*R1; } float delta = b*b-4*a*c; #ifdef DEBUG_INTERSEC cout << "a : " << a << endl << "b : " << b << endl << "c :" << c << endl << "delta : " << delta << endl; #endif if (delta < 0) { #ifdef DEBUG_INTERSEC cout << "delta est négatif, il n'y a pas de solution" << endl; #endif // pas de solution, il faut vérifier maintenant si on intersecte les disques supérieur ou inférieur bool inter_topdisque = topdisque_->intersect(ray,hit); bool inter_bottomdisque = bottomdisque_->intersect(ray,hit); return (inter_topdisque || inter_bottomdisque); } if (delta > 0) { #ifdef DEBUG_INTERSEC cout << "delta est positif, il y a deux solutions" << endl; #endif // deux solutions, le rayon entre et sort par la robe du cylindre float t; if (a == 0) { t = -c/b; } else{ t = (-b-sqrt(delta))/(2*a); } if ( t < 0 ){ #ifdef DEBUG_INTERSEC cout << "t < 0 " << endl; cout << "PAS D'INTERSECTION avec la robe du cylindre" << endl; #endif return false; } // on calcule le point d'intersection I.x = O.x + t*D.x; I.y = O.y + t*D.y; I.z = O.z + t*D.z; #ifdef DEBUG_INTERSEC cout <<"I : " << I << endl; #endif if (I.z < 0 || I.z > h) { #ifdef DEBUG_INTERSEC cout << "t vaut : " << t << endl << "I.z vaut : " << I.z << endl; //cout << "PAS D'INTERSECTION avec la robe du cylindre" << endl; #endif bool top = topdisque()->intersect(ray,hit); bool bottom = bottomdisque()->intersect(ray,hit); return top || bottom; } // on calcule la normale en ce point, dans les coordonnées du cylindre toujours float theta = 0; Vec normale; normale.x = I.x; normale.y = I.y; normale.z = 0; if (normale.norm() == 0) { cout << "la norme de la normale est nulle" << endl; } if (R1>=R2){ theta = atan((R1-R2)/h); normale = normale * (1/normale.norm())*cos(theta); normale = normale + Vec(0,0,sin(theta)); } else { theta = atan(h/(R2-R1)); normale = normale * (1/normale.norm())*sin(theta); normale = normale - Vec(0,0,cos(theta)); } if ( hit.time() > t){ hit.setTime(t); Vec locale = frame().inverseTransformOf(I); hit.setIntersection(locale); hit.setNormal(frame().inverseTransformOf(normale)); hit.setMaterial(material()); computeUV(hit); #ifdef DEBUG_INTERSEC displayIntersectionDebug(ray,hit,O,I,t); #endif topdisque()->intersect(ray,hit); bottomdisque()->intersect(ray,hit); return true; }else{ return false; } } if (delta ==0) { #ifdef DEBUG_INTERSEC cout << "delta est nul, il y a une solution" << endl; #endif // une solution, regarder si on intersecte les disques et comparer les time obtenu pour trouver le plus petit float t; if (a==0) { t = -c/b; } else { t=(-b/(2*a)); } if (t<0) { //l'objet est derrière moi #ifdef DEBUG_INTERSEC cout << "On est à l'intérieur de l'objet" << endl; #endif return false; } // on calcule le point d'intersection I.x = O.x + t*D.x; I.y = O.y + t*D.y; I.z = O.z + t*D.z; // on calcule la normale en ce point, dans les coordonnées du cylindre toujours float theta; Vec normale; theta = atan((R1-R2)/h); normale.x = I.x; normale.y = I.y; normale.z = 0; if (normale.norm() == 0) { cout << "la norme de la normale est nulle" << endl; } normale = normale * (1/normale.norm())*cos(theta); normale = normale + Vec(0,0,sin(theta)); if ( hit.time() > t){ hit.setTime(t); hit.setIntersection(frame().inverseCoordinatesOf(I)); hit.setNormal(frame().inverseCoordinatesOf(normale)); hit.setMaterial(material()); computeUV(hit); #ifdef DEBUG_INTERSEC displayIntersectionDebug(ray,hit,O,I,t); #endif topdisque()->intersect(ray,hit); bottomdisque()->intersect(ray,hit); return true; }else{ return false; } } #ifdef DEBUG cerr << "Erreur lors du calcul de l'intersection d'un objet avec un rayon" << endl; #endif return false; }
bool Plane::intersect(const Ray& ray, Hit& hit) const { #ifdef DEBUG_INTERSEC std::cout << "TENTATIVE d'intersection du rayon :\n" << ray << "avec le plan\n" << *this << std::endl; #endif // on place le ray dans le répère associé au plan Ray ray_associe_plan(frame().coordinatesOf(ray.start()), frame().transformOf(ray.direction())); #ifdef DEBUG_INTERSEC std::cout << "rayon dans l'espace du plan : \n" << ray_associe_plan << std::endl; #endif // on vérifie que le ray pointe vers le plan if ( ( ray_associe_plan.start().z <= 0 && ray_associe_plan.direction().z <= 0 ) || ( ray_associe_plan.start().z >= 0 && ray_associe_plan.direction().z >= 0 ) ) { #ifdef DEBUG_INTERSEC std::cout << "PAS D'INTERSECTION" << std::endl; #endif return false; } // on détermine le t de l'intersection dans la formule // zstart + t*zdir = 0 float t = -ray_associe_plan.start().z/ray_associe_plan.direction().z; if ( hit.time() < t ){ #ifdef DEBUG_INTERSEC std::cout << "PAS D'INTERSECTION : " << t << " > " << hit.time() << std::endl; #endif return false; } #ifdef DEBUG_INTERSEC std::cout << "Intersection au temps : " << t << std::endl; #endif // Calcul des coordonnées de l'intersection dans le plan et // rejet si on dépasse float u,v; u = ray_associe_plan.start().x+t*ray_associe_plan.direction().x; if ( width_ > 0 && (u > width_/2.0 || u < -width_/2.0) ){ #ifdef DEBUG_INTERSEC std::cout << "PAS D'INTERSECTION : u = " << u << ", width = " << width_ << std::endl; #endif return false; } v = ray_associe_plan.start().y+t*ray_associe_plan.direction().y; if ( height_ > 0 && (v > height_/2.0 || v < -height_/2.0) ){ #ifdef DEBUG_INTERSEC std::cout << "PAS D'INTERSECTION : v = " << v << ", height = " << height_ << std::endl; #endif return false; } qglviewer::Vec inter_plan = qglviewer::Vec(u,v,0); #ifdef DEBUG_INTERSEC std::cout << "INTERSECTION d'un rayon avec le plan \n"<< *this << "\tau temps : " << t << std::endl; #endif hit.setTime(t); hit.setIntersection(frame().inverseCoordinatesOf(inter_plan)); if ( ray_associe_plan.start().z <= 0 ){ hit.setNormal(frame().inverseTransformOf(qglviewer::Vec(0,0,-1))); }else{ hit.setNormal(frame().inverseTransformOf(qglviewer::Vec(0,0,1))); } hit.setMaterial(material()); // On envoie les coordonnées de la texture (entre 0 et 1 si la // partie est finie) if ( width_ > 0 ){ u = u/width_ + 0.5; } if ( height_ > 0 ){ v = v/height_ + 0.5; } #ifdef DEBUG_INTERSEC std::cout << "Coordonnées de textures ("<< u << "," << v << ")" << std::endl; #endif hit.setCoord(u,v); return true; #ifdef DEBUG cerr << "Erreur lors du calcul de l'intersection d'un objet avec un rayon" << endl; #endif return false; }