/////////////////////////////////////////////////////////////////////////////// /// 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; }
/////////////////////////////////////////////////////////////////////////////// /// 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 ); // ... // ... return Filter; }
/////////////////////////////////////////////////////////////////////////////// /// 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; }
/////////////////////////////////////////////////////////////////////////////// /// 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; }
/////////////////////////////////////////////////////////////////////////////// /// public LancerRayons \n /// Description : Lancement des rayons (raytrace) dans la scène courante /// /// @return None /// /// @author Olivier Dionne /// @date 13/08/2008 /// /////////////////////////////////////////////////////////////////////////////// void CScene::LancerRayons( void ) { Initialiser(); // Distance entre la camera et la scene const float distanceCameraScene = CVecteur3::Distance(m_Camera.PointVise, m_Camera.Position); // Simple trigonometrie entre la distance entre la camera et la scene et l'angle de la camera const float hauteur = 2 * (tan(Deg2Rad(m_Camera.Angle)) * distanceCameraScene); // Regle de trois pour trouver la largeur a partir de la hauteur const float largeur = hauteur * m_ResLargeur / (float)m_ResHauteur; // On ne prend pas compte d'une rotation/orientation speciale de la camera ici, elle est prise en compte plus loin // La position Hauteur Min de la scene est la somme des vecteurs de distance en -Z et la moitie de la hauteur en -Y CVecteur3 positionHauteurMin = CVecteur3(0, -hauteur / 2, -distanceCameraScene); // Logique semblable pour la Largeur Min CVecteur3 positionLargeurMin = CVecteur3(-largeur / 2, 0, -distanceCameraScene); for (int y = 0; y < m_ResHauteur; ++y) { //Calculer la position pixelY dans la scene CVecteur3 pixelY = positionHauteurMin + CVecteur3(0, (y / (float)m_ResHauteur) * hauteur, 0); for (int x = 0; x < m_ResLargeur; ++x) { //Calculer la position pixelX dans la scene CVecteur3 pixelX = positionLargeurMin + CVecteur3((x / (float)m_ResLargeur) * largeur, 0, 0); CRayon rayon; //Ajuster l'origine du rayon rayon.AjusterOrigine(m_Camera.Position); //Ajuster la direction du rayon CVecteur3 direction = CVecteur3::Normaliser(pixelX + pixelY); //Ajuster la direction selon la matrice d'orientation de la camera rayon.AjusterDirection(direction * m_Camera.Orientation); //Initialiser les autres caracteristiques du rayon rayon.AjusterEnergie(Math3D::REAL(1)); rayon.AjusterNbRebonds(0); rayon.AjusterIndiceRefraction(Math3D::REAL(1)); //Lancer le rayon et obtenir la couleur CCouleur couleur = CScene::ObtenirCouleur(rayon); //Enregistrer les couleurs m_InfoPixel[(x + y * m_ResLargeur) * 3] = couleur.r; m_InfoPixel[(x + y * m_ResLargeur) * 3 + 1] = couleur.g; m_InfoPixel[(x + y * m_ResLargeur) * 3 + 2] = couleur.b; } } // Créer une texture openGL glGenTextures ( 1, &m_TextureScene ); glBindTexture ( GL_TEXTURE_2D, m_TextureScene ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexImage2D ( GL_TEXTURE_2D, 0, GL_RGBA8, m_ResLargeur, m_ResHauteur, 0, GL_RGB, GL_FLOAT, m_InfoPixel ); }
/////////////////////////////////////////////////////////////////////////////// /// public LancerRayons \n /// Description : Lancement des rayons (raytrace) dans la scène courante /// /// @return None /// /// @author Olivier Dionne /// @date 13/08/2008 /// /////////////////////////////////////////////////////////////////////////////// void CScene::LancerRayons( void ) { Initialiser(); REAL HalfH = tan( Deg2Rad<REAL>( m_Camera.Angle * RENDRE_REEL( 0.5 ) ) ); REAL HalfW = ( RENDRE_REEL( m_ResLargeur ) / m_ResHauteur ) * HalfH; REAL InvResWidth = RENDRE_REEL( 1.0 ) / m_ResLargeur; REAL InvResHeight = RENDRE_REEL( 1.0 ) / m_ResHauteur; CRayon Rayon; CCouleur PixelColor; int PixelIdx = 0; #pragma omp parallel for private(Rayon, PixelColor, PixelIdx) for( int PixY = 0; PixY < m_ResHauteur; PixY++ ) { for( int PixX = 0; PixX < m_ResLargeur; PixX++ ) { // Prépare le rayon Rayon.AjusterOrigine( m_Camera.Position ); Rayon.AjusterDirection( CVecteur3( ( 2 * PixX * InvResWidth - 1 ) * HalfW, ( 2 * PixY * InvResHeight - 1 ) * HalfH, -1 ) ); Rayon.AjusterDirection( CVecteur3::Normaliser( Rayon.ObtenirDirection() * m_Camera.Orientation ) ); Rayon.AjusterEnergie( 1 ); Rayon.AjusterNbRebonds( 0 ); Rayon.AjusterIndiceRefraction( 1 ); PixelColor = ObtenirCouleur( Rayon ); PixelIdx = ( PixX + ( PixY * m_ResLargeur ) ) * 3; m_InfoPixel[ PixelIdx + 0 ] = PixelColor.r; m_InfoPixel[ PixelIdx + 1 ] = PixelColor.g; m_InfoPixel[ PixelIdx + 2 ] = PixelColor.b; } } // Créer une texture openGL glGenTextures ( 1, &m_TextureScene ); glBindTexture ( GL_TEXTURE_2D, m_TextureScene ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexImage2D ( GL_TEXTURE_2D, 0, GL_RGBA8, m_ResLargeur, m_ResHauteur, 0, GL_RGB, GL_FLOAT, m_InfoPixel ); }
/////////////////////////////////////////////////////////////////////////////// /// 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; }