/////////////////////////////////////////////////////////////////////////////// /// private constant ObtenirFiltreDeSurface \n /// Description : Obtenir le filtre du matériau de la surface. Le coefficient de /// réfraction nous indique du même coup s'il y a transparence de la /// surface. /// /// @param [in, out] LumiereRayon CRayon & Le rayon de lumière (ou d'ombre) à tester /// /// @return const CCouleur Le filtre de couleur /// /// @author Olivier Dionne /// @date 13/08/2008 /// /////////////////////////////////////////////////////////////////////////////// const CCouleur CScene::ObtenirFiltreDeSurface( CRayon& LumiereRayon ) const { CCouleur Filter = CCouleur::BLANC; CIntersection LumiereIntersection; REAL Distance = CVecteur3::Norme( LumiereRayon.ObtenirDirection() ); LumiereRayon.AjusterDirection( LumiereRayon.ObtenirDirection() / Distance ); // TODO : À COMPLÉTER LORS DU VOLET 2... // Tester le rayon de lumière avec chaque surface de la scène // pour vérifier s'il y a intersection CIntersection Result; CIntersection Tmp; for (SurfaceIterator aSurface = m_Surfaces.begin(); aSurface != m_Surfaces.end(); aSurface++) { Tmp = (*aSurface)->Intersection(LumiereRayon); if (Tmp.ObtenirDistance() > EPSILON) { Filter *= Tmp.ObtenirSurface()->ObtenirCoeffRefraction() * Tmp.ObtenirSurface()->ObtenirCouleur(); } } // S'il y a une intersection appliquer la translucidité de la surface // intersectée sur le filtre return Filter; }
/////////////////////////////////////////////////////////////////////////////// /// public virtual Intersection \n /// Description : Effectue l'intersection Rayon/Triangle /// /// @param [in] Rayon const CRayon & Le rayon à tester /// /// @return Scene::CIntersection Le résultat de l'ntersection /// /// @author Olivier Dionne /// @date 13/08/2008 /// /////////////////////////////////////////////////////////////////////////////// CIntersection CTriangle::Intersection( const CRayon& Rayon ) { CIntersection Result; // Tomas Akenine-Moller and Eric Haines "Real-Time Rendering 2nd Ed." 2002, p.581 // http://jgt.akpeters.com/papers/MollerTrumbore97/code.html CVecteur3 Edge1 = m_Pts[ 1 ] - m_Pts[ 0 ]; CVecteur3 Edge2 = m_Pts[ 2 ] - m_Pts[ 0 ]; // Commencer par le calcul du déterminant - aussi utilisé pour calculer U CVecteur3 VecP = CVecteur3::ProdVect( Rayon.ObtenirDirection(), Edge2 ); // Si le déterminant est près de 0, le rayon se trouve dans le PLAN du triangle REAL Det = CVecteur3::ProdScal( Edge1, VecP ); if( Abs<REAL>( Det ) < EPSILON ) return Result; else { REAL InvDet = RENDRE_REEL( 1.0 ) / Det; // Calculer la distance entre point0 et l'origine du rayon CVecteur3 VecS = Rayon.ObtenirOrigine() - m_Pts[ 0 ]; // Calculer le paramètre U pour tester les frontières REAL u = CVecteur3::ProdScal( VecS, VecP ) * InvDet; if ( u < 0 || u > 1 ) return Result; else { CVecteur3 VecQ = CVecteur3::ProdVect( VecS, Edge1 ); // Calculer le paramètre V pour tester les frontières REAL v = CVecteur3::ProdScal( Rayon.ObtenirDirection(), VecQ ) * InvDet; if( v < 0 || u + v > 1 ) return Result; else { Result.AjusterSurface( this ); Result.AjusterDistance( CVecteur3::ProdScal( Edge2, VecQ ) * InvDet ); Result.AjusterNormale( m_Normale ); } } } return Result; }
/////////////////////////////////////////////////////////////////////////////// /// public virtual Intersection \n /// Description : Effectue l'intersection Rayon/Triangle /// /// @param [in] Rayon const CRayon & Le rayon à tester /// /// @return Scene::CIntersection Le résultat de l'ntersection /// /// @author Olivier Dionne /// @date 13/08/2008 /// /////////////////////////////////////////////////////////////////////////////// CIntersection CTriangle::Intersection(const CRayon& Rayon) { CIntersection Result; // À COMPLÉTER ... // Voici deux références pour acomplir le développement : // 1) Tomas Akenine-Moller and Eric Haines "Real-Time Rendering 2nd Ed." 2002, p.581 // 2) Son article: http://www.graphics.cornell.edu/pubs/1997/MT97.pdf // Notez que la normale du triangle est déjà calculée lors du prétraitement // il suffit que de la passer à la structure d'intersection. CVecteur3 edge1 = m_Pts[1] - m_Pts[0]; CVecteur3 edge2 = m_Pts[2] - m_Pts[0]; CVecteur3 pvec = CVecteur3::ProdVect(Rayon.ObtenirDirection(), edge2); REAL det = CVecteur3::ProdScal(edge1, pvec); if (det < EPSILON) return Result; CVecteur3 tvec = Rayon.ObtenirOrigine() - m_Pts[0]; REAL U = CVecteur3::ProdScal(tvec, pvec); if ((U < EPSILON) || (U > det)) return Result; CVecteur3 qvec = CVecteur3::ProdVect(tvec, edge1); REAL V = CVecteur3::ProdScal(Rayon.ObtenirDirection(), qvec); if ((V < EPSILON) || (U + V > det)) return Result; REAL t = CVecteur3::ProdScal(edge2, qvec) / det; Result.AjusterDistance(t); Result.AjusterSurface(this); if (CVecteur3::ProdScal(m_Normale, Rayon.ObtenirDirection()) > EPSILON) { Result.AjusterNormale(-m_Normale); } else { Result.AjusterNormale(m_Normale); } return Result; }
/////////////////////////////////////////////////////////////////////////////// /// private constant ObtenirCouleurSurIntersection \n /// Description : Obtient la couleur à un point d'intersection en particulier /// Calcule les contributions colorées de toutes les lumières, avec /// les modèles de Phong et de Gouraud. Aussi, dépendemment des /// propriétés de la surface en intersection, on réfléchi ou in /// réfracte le rayon courant. /// current ray. /// /// @param [in] Rayon const CRayon & Le rayon à tester /// @param [in] Intersection const Scene::CIntersection & L'ntersection spécifiée /// /// @return const CCouleur La couleur à l'intersection donnée /// /// @author Olivier Dionne /// @date 13/08/2008 /// /////////////////////////////////////////////////////////////////////////////// const CCouleur CScene::ObtenirCouleurSurIntersection( const CRayon& Rayon, const CIntersection& Intersection ) const { CCouleur Result = Intersection.ObtenirSurface()->ObtenirCouleur() * Intersection.ObtenirSurface()->ObtenirCoeffAmbiant(); CVecteur3 IntersectionPoint = Rayon.ObtenirOrigine()+ Intersection.ObtenirDistance() * Rayon.ObtenirDirection(); // Calculer les contribution colorées des toutes les lumières dans la scène CCouleur LumiereContributions = CCouleur::NOIR; CRayon LumiereRayon; for( LumiereIterator uneLumiere = m_Lumieres.begin(); uneLumiere != m_Lumieres.end(); uneLumiere++ ) { // Initialise le rayon de lumière (ou rayon d'ombre) LumiereRayon.AjusterOrigine( IntersectionPoint ); LumiereRayon.AjusterDirection( ( *uneLumiere )->GetPosition() - IntersectionPoint ); LumiereRayon.AjusterEnergie( 1 ); LumiereRayon.AjusterIndiceRefraction( 1 ); if( CVecteur3::ProdScal( LumiereRayon.ObtenirDirection(), Intersection.ObtenirNormale() ) > 0 ) { // Obtenir la couleur à partir de la lumière CCouleur Filter = ObtenirFiltreDeSurface( LumiereRayon ); CCouleur LumiereCouleur = ( *uneLumiere )->ObtenirCouleur() * Filter; // Ajouter la contribution de Gouraud REAL GouraudFactor = ( *uneLumiere )->GetIntensity() * Intersection.ObtenirSurface()->ObtenirCoeffDiffus() * CVecteur3::ProdScal( Intersection.ObtenirNormale(), LumiereRayon.ObtenirDirection() ); Result += Intersection.ObtenirSurface()->ObtenirCouleur() * GouraudFactor * LumiereCouleur; // ... } } return Result; }
/////////////////////////////////////////////////////////////////////////////// /// private constant ObtenirCouleur \n /// Description : Obtenir la couleur du pixel pour un rayon donné /// /// @param [in] Rayon const CRayon & Le rayon à tester /// /// @return const CCouleur La couleur du pixel /// /// @author Olivier Dionne /// @date 13/08/2008 /// /////////////////////////////////////////////////////////////////////////////// const CCouleur CScene::ObtenirCouleur( const CRayon& Rayon ) const { CIntersection Result; CIntersection Tmp; for( SurfaceIterator aSurface = m_Surfaces.begin(); aSurface != m_Surfaces.end(); aSurface++ ) { Tmp = ( *aSurface )->Intersection( Rayon ); if( Tmp.ObtenirDistance() > EPSILON && ( Tmp.ObtenirDistance() < Result.ObtenirDistance() || Result.ObtenirDistance() < 0 ) ) Result = Tmp; } // S'il n'y a aucune intersection, retourner la couleur de l'arrière-plan // Sinon, retourner la couleur à l'intersection return ( Result.ObtenirDistance() < 0 ) ? m_CouleurArrierePlan : ObtenirCouleurSurIntersection( Rayon, Result ); }
bool CTriangleMesh::Hit(CRay const& ray, CIntersection & intersection) const { // Вычисляем обратно преобразованный луч (вместо вполнения прямого преобразования объекта) CRay invRay = Transform(ray, GetInverseTransform()); CVector3d const& invRayStart = invRay.GetStart(); CVector3d const& invRayDirection = invRay.GetDirection(); ////////////////////////////////////////////////////////////////////////// // Здесь следует выполнить проверку на пересечение луча с ограничивающим // объемом (bounding volume) полигональной сетки. // При отсутствии такого пересечения луч гарантированно не будет пересекать // ни одну из граней сетки, что позволит избежать лишних проверок: // if (!ray_intesects_bounding_volume) // return false; ////////////////////////////////////////////////////////////////////////// // Получаем информацию о массиве треугольников сетки CTriangle const* const triangles = m_pMeshData->GetTriangles(); const size_t numTriangles = m_pMeshData->GetTriangleCount(); // Массив найденных пересечений луча с гранями сетки. std::vector<FaceHit> faceHits; ////////////////////////////////////////////////////////////////////////// // Используется поиск пересечения луча со всеми гранями сетки. // Данный подход является очень неэффективным уже на полигональных сетках, // содержащих более нескольких десятков граней, а, тем более, сотни и тысячи граней. // // Для эффективного поиска столкновений следует представить полигональную сетку // не в виде массива граней, а в виде древовидной структуры (Oct-Tree или BSP-Tree), // что уменьшит вычислительную сложность поиска столкновений с O(N) до O(log N) ////////////////////////////////////////////////////////////////////////// FaceHit hit; for (size_t i = 0; i < numTriangles; ++i) { CTriangle const& triangle = triangles[i]; // Проверка на пересечение луча с треугольной гранью if (triangle.HitTest(invRayStart, invRayDirection, hit.hitTime, hit.hitPointInObjectSpace, hit.w0, hit.w1, hit.w2)) { // Сохраняем индекс грани и добавляем информацию в массив найденных пересечений hit.faceIndex = i; if (faceHits.empty()) { // При обнаружени первого пересечения резервируем // память сразу под 8 пересечений (для уменьшения количества операций выделения памяти) faceHits.reserve(8); } faceHits.push_back(hit); } } // При отсутствии пересечений выходим if (faceHits.empty()) { return false; } ////////////////////////////////////////////////////////////////////////// // Упорядочиваем найденные пересечения по возрастанию времени столкновения ////////////////////////////////////////////////////////////////////////// size_t const numHits = faceHits.size(); std::vector<FaceHit const *> hitPointers(numHits); { // Инициализируем массив указателей на точки пересечения for (size_t i = 0; i < numHits; ++i) { hitPointers[i] = &faceHits[i]; } // Сортируем массив указателей по возрастанию времени столкновения // Сортируются указатели, а не сами объекты, чтобы сократить // издержки на обмен элементов массива: // На 32 битной платформе размер структуры FaceHit равен 64 байтам, // а размер указателя - всего 4 байта. if (numHits > 1) { std::sort(hitPointers.begin(), hitPointers.end(), HitPointerComparator()); // Теперь указатели в массиве hitPointers отсортированы // в порядке возрастания времени столкновения луча с гранями } } ////////////////////////////////////////////////////////////////////////// // Возвращаем информацию о найденных пересечениях ////////////////////////////////////////////////////////////////////////// for (size_t i = 0; i < numHits; ++i) { // Получаем информацию о столкновении FaceHit const& faceHit = *hitPointers[i]; // Сохраняем информацию только о первом пересечении if (i == 0) { ExtendedHitData *hd = (ExtendedHitData*)&m_lastHitData; hd->faceIndex = faceHit.faceIndex; hd->vc[0] = static_cast<float>(faceHit.w0); hd->vc[1] = static_cast<float>(faceHit.w1); hd->vc[2] = static_cast<float>(faceHit.w2); } // Точка столкновения в мировой системе координат CVector3d hitPoint = ray.GetPointAtTime(faceHit.hitTime); // Грань, с которой произошло столкновение CTriangle const& triangle = triangles[faceHit.faceIndex]; // Нормаль "плоской грани" во всех точках столкновения равна нормали самой грани CVector3d normalInObjectSpace = triangle.GetPlaneEquation(); if (!triangle.IsFlatShaded()) { // Для неплоских граней выполняется интерполяция нормалей вершин треугольника // с учетом их весовых коэффициентов в точке пересечения Vertex const& v0 = triangle.GetVertex0(); Vertex const& v1 = triangle.GetVertex1(); Vertex const& v2 = triangle.GetVertex2(); // Взвешенный вектор нормали normalInObjectSpace = faceHit.w0 * v0.normal + faceHit.w1 * v1.normal + faceHit.w2 * v2.normal; } // Нормаль в мировой системе координат CVector3d normal = GetNormalMatrix() * normalInObjectSpace; // Добавляем информацию о точке пересечения в объект intersection intersection.AddHit( CHitInfo( faceHit.hitTime, *this, hitPoint, faceHit.hitPointInObjectSpace, normal, normalInObjectSpace ) ); } return true; }
/////////////////////////////////////////////////////////////////////////////// /// public virtual Intersection \n /// Description : Effectue l'intersection Rayon/Quadrique /// /// @param [in] Rayon const CRayon & Le rayon à tester /// /// @return Scene::CIntersection Le résultat de l'ntersection /// /// @author Olivier Dionne /// @date 13/08/2008 /// /////////////////////////////////////////////////////////////////////////////// CIntersection CQuadrique::Intersection( const CRayon& Rayon ) { CIntersection Result; // algorithme d'intersection tiré de ... // Eric Haines, Paul Heckbert "An Introduction to Rayon Tracing", // Academic Press, Edited by Andrw S. Glassner, pp.68-73 & 288-293 CVecteur3 RayonDirection = Rayon.ObtenirDirection(); CVecteur3 RayonOrigin = Rayon.ObtenirOrigine(); const REAL ACoeff = RayonDirection.x * ( m_Quadratique.x * RayonDirection.x + m_Mixte.z * RayonDirection.y + m_Mixte.y * RayonDirection.z ) + RayonDirection.y * ( m_Quadratique.y * RayonDirection.y + m_Mixte.x * RayonDirection.z ) + RayonDirection.z * ( m_Quadratique.z * RayonDirection.z ); const REAL BCoeff = RayonDirection.x * ( m_Quadratique.x * RayonOrigin.x + RENDRE_REEL( 0.5 ) * ( m_Mixte.z * RayonOrigin.y + m_Mixte.y * RayonOrigin.z + m_Lineaire.x ) ) + RayonDirection.y * ( m_Quadratique.y * RayonOrigin.y + RENDRE_REEL( 0.5 ) * ( m_Mixte.z * RayonOrigin.x + m_Mixte.x * RayonOrigin.z + m_Lineaire.y ) ) + RayonDirection.z * ( m_Quadratique.z * RayonOrigin.z + RENDRE_REEL( 0.5 ) * ( m_Mixte.y * RayonOrigin.x + m_Mixte.x * RayonOrigin.y + m_Lineaire.z ) ); const REAL CCoeff = RayonOrigin.x * ( m_Quadratique.x * RayonOrigin.x + m_Mixte.z * RayonOrigin.y + m_Mixte.y * RayonOrigin.z + m_Lineaire.x ) + RayonOrigin.y * ( m_Quadratique.y * RayonOrigin.y + m_Mixte.x * RayonOrigin.z + m_Lineaire.y ) + RayonOrigin.z * ( m_Quadratique.z * RayonOrigin.z + m_Lineaire.z ) + m_Cst; if( ACoeff != 0.0 ) { REAL Ka = -BCoeff / ACoeff; REAL Kb = CCoeff / ACoeff; REAL Delta = Ka * Ka - Kb; if( Delta > 0 ) { Delta = sqrt( Delta ); REAL T0 = Ka - Delta; REAL T1 = Ka + Delta; REAL Distance = Min<REAL>( T0, T1 ); if( Distance < EPSILON ) Distance = Max<REAL>( T0, T1 ); if( !( Distance < 0 ) ) { Result.AjusterDistance( Distance ); Result.AjusterSurface( this ); // Calcule la normale de surface CVecteur3 HitPt = RayonOrigin + Distance * RayonDirection; CVecteur3 Normal; Normal.x = RENDRE_REEL( 2.0 ) * m_Quadratique.x * HitPt.x + m_Mixte.y * HitPt.z + m_Mixte.z * HitPt.y + m_Lineaire.x; Normal.y = RENDRE_REEL( 2.0 ) * m_Quadratique.y * HitPt.y + m_Mixte.x * HitPt.z + m_Mixte.z * HitPt.x + m_Lineaire.y; Normal.z = RENDRE_REEL( 2.0 ) * m_Quadratique.z * HitPt.z + m_Mixte.x * HitPt.y + m_Mixte.y * HitPt.x + m_Lineaire.z; Result.AjusterNormale( CVecteur3::Normaliser( Normal ) ); } } } else { Result.AjusterSurface ( this ); Result.AjusterDistance( -RENDRE_REEL( 0.5 ) * ( CCoeff / BCoeff ) ); Result.AjusterNormale ( CVecteur3::Normaliser( m_Lineaire ) ); } return Result; }
/////////////////////////////////////////////////////////////////////////////// /// public virtual Intersection \n /// Description : Effectue l'intersection Rayon/Quadrique /// /// @param [in] Rayon const CRayon & Le rayon à tester /// /// @return Scene::CIntersection Le résultat de l'ntersection /// /// @author Olivier Dionne /// @date 13/08/2008 /// /////////////////////////////////////////////////////////////////////////////// CIntersection CQuadrique::Intersection( const CRayon& Rayon ) { CIntersection Result; float a, b, c, d, e, f, g, h, i, j; float A, B, C; float t0, t1, tf; const CVecteur3 rd = Rayon.ObtenirDirection(); const CVecteur3 r0 = Rayon.ObtenirOrigine(); /*a = m_Quadratique[0]; e = m_Quadratique[1]; h = m_Quadratique[2]; b = m_Mixte[0]; c = m_Mixte[1]; f = m_Mixte[2]; d = m_Lineaire[0]; g = m_Lineaire[1]; i = m_Lineaire[2]; j = m_Cst; A = a * rd.x * rd.x + 2 * b * rd.x * rd.y + 2 * c * rd.x * rd.z + e * rd.y * rd.y + 2 * f * rd.y * rd.z + h * rd.z * rd.z; B = 2 * (a * r0.x * rd.x + b * (r0.x * rd.y + rd.x * r0.y) + c * (r0.x * rd.z + rd.x * r0.z) + d * rd.x + e * r0.y * rd.y + f * (r0.y * rd.z + rd.y * r0.z) + g * rd.y + h * r0.z * rd.z + i * rd.z); C = a * r0.x * r0.x + 2 * b * r0.x * r0.y + 2 * c * r0.x * r0.z + 2 * d * r0.x + e * r0.y * r0.y + 2 * f * r0.y * r0.z + 2 * g * r0.y + h * r0.z * r0.z + 2 * i * r0.z + j;*/ a = m_Quadratique[0]; b = m_Quadratique[1]; c = m_Quadratique[2]; d = m_Mixte[0]; e = m_Mixte[1]; f = m_Mixte[2]; g = m_Lineaire[0]; h = m_Lineaire[1]; i = m_Lineaire[2]; j = m_Cst; A = a * rd.x * rd.x + b * rd.y * rd.y + c * rd.z * rd.z + d * rd.x * rd.y + e * rd.x * rd.z + f * rd.y * rd.z; B = 2 * a * r0.x * rd.x + 2 * b * r0.y * rd.y + 2 * c * r0.z * rd.z + d * (r0.x * rd.y + r0.y * rd.x) + e * (r0.x * rd.z) + f * (r0.y * rd.z + rd.y * r0.z) + g * rd.x + h * rd.y + i * rd.z; C = a * r0.x * r0.x + b * r0.y * r0.y + c * r0.z * r0.z + d * r0.x * r0.y + e * r0.x * r0.z + f * r0.y * r0.z + g * r0.x + h * r0.y + i * r0.z + j; if (A == 0.f) { tf = -C / B; } else { float discriminant = B * B - 4 * A * C; if (discriminant < 0) { return Result; } else { tf = (-B - sqrt(discriminant)) / (2 * A); if (tf < 0) { tf = (-B + sqrt(discriminant)) / (2 * A); if (tf < 0) return Result; } } } CVecteur3 intersection(r0 + rd * tf); Result.AjusterDistance(CVecteur3::Norme(intersection - r0)); CVecteur3 normale; normale.x = (2 * a * intersection.x + d * intersection.y + e * intersection.z) + g; normale.y = (d * intersection.x + 2 * b * intersection.y + f * intersection.z) + h; normale.z = (e * intersection.x + f * intersection.y + 2 * c * intersection.z) + i; normale = CVecteur3::Normaliser(normale); Result.AjusterNormale(CVecteur3::ProdScal(normale, rd) > 0 ? -normale : normale); Result.AjusterSurface(this); return Result; }