Article: un raytracer de base en C++

Article: un raytracer de base en C++ - Algo - Programmation

Marsh Posté le 05-08-2003 à 11:37:59    

Voici le code d'un raytraceur basique, j'ai essayé de réduire les fonctionalités au maximum tout en essayant de le conserver intéressant.
 
Update :  
Les articles ont été remis à jours et sont disponibles sur mon site web:
 
premiers pas:
http://www.massal.net/article/raytrace/page1.html
éclairage spéculaire (blinn-phong), post processing et antialiasing:
http://www.massal.net/article/raytrace/page2.html
textures (Perlin noise, cubic environment mapping, bump mapping)
http://www.massal.net/article/raytrace/page3.html
Flou (depth of field), Fresnel, blobs (isosurfaces):
http://www.massal.net/article/raytrace/page4.html
HDR, loi de beer, aberration chromatique:
http://www.massal.net/article/raytrace/page5.html
Global ilumination, photon mapping:
http://www.massal.net/article/raytrace/page6.html  
 
Le code et les commentaires y sont plus récents. Vous pouvez continuer à utiliser ce topic pour les questions
et commentaires.
 
Voilà, si vous voulez l'historique du sujet, vous pouvez continuer à lire la suite.
Fin de l'Update  
 
Le source
 
No comment sur le source, les commentaires suivent.
 

Code :
  1. #include "Raytrace.h"
  2. bool init(char* inputName, scene &myScene) {
  3.   int nbMat, nbSphere, nbLight;
  4.   int i;
  5.   ifstream sceneFile(inputName);
  6.   if (!sceneFile)
  7.     return false;
  8.   sceneFile >> myScene.sizex >> myScene.sizey;
  9.   sceneFile >> nbMat >> nbSphere >> nbLight ;
  10.   myScene.matTab.resize(nbMat);
  11.   myScene.sphTab.resize(nbSphere);
  12.   myScene.lgtTab.resize(nbLight);
  13.   for (i=0; i<nbMat; ++i)
  14.     sceneFile >> myScene.matTab[i];
  15.   for (i=0; i<nbSphere; ++i)
  16.     sceneFile >> myScene.sphTab[i];
  17.   for (i=0; i<nbLight; ++i)
  18.     sceneFile >> myScene.lgtTab[i];
  19.   return true;
  20. }
  21. bool hitSphere(const ray &r, const sphere& s, float &t) {
  22.   // intersection rayon/sphere
  23.   vecteur dist = s.pos - r.start;
  24.   float B = r.dir * dist;
  25.   float D = B*B - dist * dist + s.size * s.size;
  26.   if (D < 0.0f) return false;
  27.   float t0 = B - sqrtf(D);
  28.   float t1 = B + sqrtf(D);
  29.   bool retvalue = false;
  30.   if ((t0 > 0.1f ) && (t0 < t)) {
  31.     t = t0;
  32.     retvalue = true;
  33.   }
  34.   if ((t1 > 0.1f ) && (t1 < t)) {
  35.     t = t1;
  36.     retvalue = true;
  37.   }
  38.   return retvalue;
  39. }
  40. bool draw(char* outputName, scene &myScene) {
  41.   ofstream imageFile(outputName,ios_base::binary);
  42.   if (!imageFile)
  43.     return false;
  44.   // Ajout du header TGA
  45.   imageFile.put(0).put(0);
  46.   imageFile.put(2);                  /* RGB non compresse */
  47.   imageFile.put(0).put(0);
  48.   imageFile.put(0).put(0);
  49.   imageFile.put(0);
  50.   imageFile.put(0).put(0);           /* origine X */
  51.   imageFile.put(0).put(0);           /* origine Y */
  52.   imageFile.put((myScene.sizex & 0x00FF)).put((myScene.sizex & 0xFF00) / 256);
  53.   imageFile.put((myScene.sizey & 0x00FF)).put((myScene.sizey & 0xFF00) / 256);
  54.   imageFile.put(24);                 /* 24 bit bitmap */
  55.   imageFile.put(0);
  56.   // fin du header TGA
  57.   // balayage
  58.   for (int y = 0; y < myScene.sizey; ++y) {
  59.     for (int x = 0 ; x < myScene.sizex; ++x) {
  60.       float red = 0, green = 0, blue = 0;
  61.       float coef = 1.0f;
  62.       int level = 0;
  63.       // lancer de rayon
  64.       ray viewRay = { {float(x), float(y), -10000.0f}, { 0.0f, 0.0f, 1.0f}};
  65.       do {
  66.         // recherche de l'intersection la plus proche
  67.         float t = 20000.0f;
  68.         int currentSphere=-1;
  69.         for (unsigned int i = 0; i < myScene.sphTab.size() ; ++i) {
  70.           if (hitSphere(viewRay, myScene.sphTab[i], t)) {
  71.             currentSphere = i;
  72.           }
  73.         }
  74.         if (currentSphere == -1)
  75.           break;
  76.         point newStart  = viewRay.start + t * viewRay.dir;
  77.         // la normale au point d'intersection
  78.         vecteur n = newStart - myScene.sphTab[currentSphere].pos;
  79.         float temp = n * n;
  80.         if (temp == 0.0f)
  81.           break;
  82.         temp = 1.0f / sqrtf(temp);
  83.         n = temp * n;
  84.         material currentMat = myScene.matTab[myScene.sphTab[currentSphere].material];
  85.         // calcul de la valeur d'éclairement au point
  86.         for (unsigned int j = 0; j < myScene.lgtTab.size() ; ++j) {
  87.           light current = myScene.lgtTab[j];
  88.           vecteur dist = current.pos - newStart;
  89.           if ( n * dist <= 0.0f )
  90.             continue;
  91.           float t = sqrtf(dist * dist);
  92.           if ( t <= 0.0f )
  93.             continue;
  94.           ray lightRay;
  95.           lightRay.start = newStart;
  96.           lightRay.dir = (1/t) * dist;
  97.           // calcul des ombres
  98.           bool inShadow = false;
  99.           for (unsigned int i = 0; i < myScene.sphTab.size() ; ++i) {
  100.             if (hitSphere(lightRay, myScene.sphTab[i], t)) {
  101.               inShadow = true;
  102.               break;
  103.             }
  104.           }
  105.           if (!inShadow) {
  106.             // lambert
  107.             float lambert = (lightRay.dir * n) * coef;
  108.             red += lambert * current.red * currentMat.red;
  109.             green += lambert * current.green * currentMat.green;
  110.             blue += lambert * current.blue * currentMat.blue;
  111.           }
  112.         }
  113.         // on itére sur la prochaine reflexion
  114.         coef *= currentMat.reflection;
  115.         float reflet = 2.0f * (viewRay.dir * n);
  116.         viewRay.start = newStart;
  117.         viewRay.dir = viewRay.dir - reflet * n;
  118.         level++;
  119.       } while ((coef > 0.0f) && (level < 10)); 
  120.       imageFile.put(min(blue*255.0f,255.0f)).put(min(green*255.0f, 255.0f)).put(min(red*255.0f, 255.0f));
  121.     }
  122.   }
  123.   return true;
  124. }
  125. int main(int argc, char* argv[]) {
  126.   if (argc < 3)
  127.     return -1;
  128.   scene myScene;
  129.   if (!init(argv[1], myScene))
  130.     return -1;
  131.   if (!draw(argv[2], myScene))
  132.     return -1;
  133.   return 0;
  134. }


 
Le rendu
 
Voici l'output de ce programme:
http://www.massal.net/article/raytrace/page1.html
(note dans cette image on utilise du supersampling
pour lisser les bords, j'expliquerai si j'ai le temps)
 
à partir d'un fichier scene.txt comme ceci (sans les commentaires):

Code :
  1. 640 480 // taille du viewport  
  2. 3 3 2 // nbre de materiel, de spheres et de lumieres  
  3. 1.0 1.0 0.0 0.5 // premier materiel: rouge vert bleu et coef de reflexion  
  4. 0.0 1.0 1.0 0.5 // deuxieme materiel  
  5. 1.0 0.0 1.0 0.5 // troisiem materiel  
  6. 233.0 290.0 0.0 100 0 // sphere 1: posx, posy, posz, rayon, materiel id  
  7. 407.0 290.0 0.0 100 1 // sphere 2  
  8. 320.0 140.0 0.0 100 2 // sphere 3  
  9. 0.0 240.0 -100.0 1.0 1.0 1.0 // light 1 : posx, posy, posz, intensité rouge, vert et bleu  
  10. 640.0 240.0 -10000.0 0.6 0.7 1.0 // light 2


 
Que fais le raytracer ?
 
le lancer de rayon ou raycasting/raytracing est l'une des méthodes de rendu les plus simples qu'on puisse imaginer.
Pour chaque point de l'écran, on envoie un rayon (virtuellement)
et l'on teste pour chaque objet de la scene s'il se trouve sur le chemin du rayon ou pas. Le point visible est le point d'intersection le plus proche.
A partir de ce point on fait des calculs d'éclairages, avec raffinements ou pas (ombres, reflexion, lambert, phong, bump, etc..)
 
L'essentiel du code est dans la fonction draw.
 
Le balayage
 
Ca correspond a la boucle

Code :
  1. for (int y = 0; y < myScene.sizey; ++y) {
  2. for (int x = 0 ; x < myScene.sizex; ++x) {


on parcourt chaque point de l'écran. et ça se termine systématiquement par l'écriture d'une valeur de couleur dans le framebuffer (ici directement sur le disque).
 

Code :
  1. imageFile.put(min(blue*255.0f,255.0f)).put(min(green*255.0f, 255.0f)).put(min(red*255.0f, 255.0f));


Il est important de noter que le min() pour chaque couleur est une approche "naive", j'y reviendrai.
 
Le lancer de rayon proprement dit
 
On commence a initialiser le rayon en fonction du point de l'écran.  

Code :
  1. ray viewRay = { {float(x), float(y), -10000.0f}, { 0.0f, 0.0f, 1.0f}};


ici c'est un rayon qui rentre dans l'écran et qui est perpendiculaire au point de départ. Pas de fancy projection conique, juste la bonne vieille projection orthographique; il faut bien se rendre compte que ça ne CHANGE RIEN AU CODE QUI SUIT.
 
Le point de départ du rayon est arbitrairement loin derriére l'observateur, l'idéal c'est qu'il n'y ait pas de clipping donc que ce point de départ dépende de la scéne. En pratique il suffit de le prendre suffisamment grand.
NOTA: le probleme est différent en projection conique, les positions derriere l'observateur sont illégales.
 
L'intersection la plus proche
 
Le rayon est orienté et paramétré par t, distance arithmétique au point de départ.
On ne se déplace que dans un seul sens, donc on prend le t de départ derriére l'écran le plus loin possible (valeur arbitraire 20000 ce qui englobera toute la scène en tenant compte du point de départ du rayon).
 
Le code qui détecte les collisions est réduit au minimum.
Pour chaque sphère de la scène on calcule les points d'intersection rayon/sphère qui sont au nombre de deux. On prend le plus proche et l'on compare sa distance au précédent point d'intersection trouvé (ou à la distance de clipping).
 
La sphère qui a le point d'intersection le plus proche est la sphère courante.
 
Intersection rayon sphere
 
C'est la plus simple des intersection (c'est la raison pour laquelle je n'ai pas inclu de cube dans la scène).
Les maths ne sont pas très intéressantes. Cela revient á résoudre une équation polynomiale du second degré (niveau première).
On calcule le delta et si le delta est inférieur à zéro il n'y a aucune solution, le rayon n'intersecte pas la sphère.
 

Code :
  1. if (D < 0.0f) return false;


 
Si le delta est positif on a deux distances,  

Code :
  1. float t0 = B - sqrtf(D);
  2. float t1 = B + sqrtf(D);


on les compare à la distance de clipping ou celle de la précédente intersection la plus proche.
 
Calcul de la valeur d'éclairement au point
 
L'idée c'est d'ajouter la contribution de chaque lumière à l'éclairement final.
 
Une première élimination rapide, on se débarasse des lumières qui sont du mauvais coté de la surface. (on considère les surfaces à un seul coté, le coté "sortant" ).

Code :
  1. if ( n * dist <= 0.0f )
  2.   continue;


 
Ensuite on détermine si le point est dans l'ombre d'un autre objet de la scène pour cette lumière là.
 
Le code est similaire à la détection de collision.  Le rayon part du point d'intersection avec la sphère et a pour
direction le vecteur qui pointe vers la lumière. Par contre ici on se fiche de savoir a quelle distance on intersecte les autres sphères, on quitte dès qu'une des sphères est touchée par le rayon: on est dans l'ombre, cette lumière ne contribuera pas à l'éclairement total du point.
 
Lambert
 
La valeur d'éclairement de Lambert (du nom du Français qui a décrit cette équation) est égale à la quantité de lumière incidente modulo le cosinus de l'angle entre le rayon de lumière incidente et le vecteur de surface.  
C'est un modèle valable pour les surface diffuse.
 
les modèle utilisés ici (Lambert + reflexion parfaite) sont bidon
mais peuvent être complexifiés à l'envi pour representer des matériaux réels. (Phong, Blinn, Anisotropique, BRDF..)
 
On calcule le cosinus de l'angle par un simple produit scalaire
entre vecteur direction du rayon lumineux et vecteur normal à la surface.
 

Code :
  1. float lambert = (lightRay.dir * n) * coef;


 
La reflexion
 
Que serait le raytracing sans une bonne vieille reflexion parfaite (mais irréaliste).
 
Chaque matériel a un coéfficient de réflexion. Si celui-ci est égal à zéro, la surface n'est pas du tout reflexive et on passe au point suivant. Sinon, on réitère tout le processus précédent mais cette fois  
avec un point de départ = le point de la sphère courant et  
la direction = la direction courante "reflechie" par le vecteur normal à la surface.
 

Code :
  1. float reflet = 2.0f * (viewRay.dir * n);
  2. viewRay.start = newStart;
  3. viewRay.dir = viewRay.dir - reflet * n;


 
Pour éviter les boucles infinies, on limite le nombre d'itérations par point à dix.
 
Plus loin
 
Voilà ce qu'on peut attendre d'un raytracing basique. Le but de ce raytracer c'est de sortir des images sympas de sphères reflexives. Aucun des modèles utilisés n'approche suffisamment de la réalité pour faire du photoréalisme!
 
Il n'y a aucune optimisation, ce qui rend le code simplissime  
(vous êtes juges) mais sur des scènes où il y a plus que trois sphères on peut s'attendre à une galère sévère.
 
Les pistes pour l'optimisation:
- subdivision de l'espace pour améliorer la détermination
du point d'intersection le plus proche.
- réutilisation des informations calculés sur des points proches, puisqu'il y a une forte cohérence spatiale de ces infos.
- répartition sur plusieurs processeurs/ parallélisme ou utilisation d'un GPU/VPU.
- etc..
 
Comments ? Questions ? Pas de critique sur le code j'ai autre
chose a foutre.
 
LeGreg


Message édité par LeGreg le 29-02-2008 à 20:09:01

---------------
voxel terrain render engine | animation mentor
Reply

Marsh Posté le 05-08-2003 à 11:37:59   

Reply

Marsh Posté le 05-08-2003 à 12:00:59    

Merci pour ce topic très interessant. Je m'étais un peu interessé à la 3D en DESS ( on a eu un cours dessus pour aller avec les cours de C/C++ ) et je m'étais bien amuser en faisant un paysage de montagne fractale.  
Ce serait interessant si tu pouvais aussi expliquer un peu plus de truc sur les lumières, par exemple c'est quoi "Phong, Blinn, Anisotropique" ?  
J'avais gardé aussi quelques images en ray-tracing faites par des pote de DESS, ça peut donner une idée de ce qu'on peut faire sans beaucoup s'y connaitre en C/C++ ( on a eu juste un module de 15heures je crois ) : elles sont là tomlameche.free.fr/V2/fractale.htm
( je peux pas donner l'url exact des images car j'ai pas accès à mon site d'où je suis ... )    


---------------
Gérez votre collection de BD en ligne ! ---- Electro-jazzy song ---- Dazie Mae - jazzy/bluesy/cabaret et plus si affinité
Reply

Marsh Posté le 05-08-2003 à 12:15:49    

heu sinon un truc qui ma tjs etonne c la generation de textures
 
http://theproduct.de/text1.html
 
qd on voit ce quil arrivent a faire... a partir de rien
 
sinon jai mit dans un zip tout les outils necessaires ici
http://users.skynet.be/fa218598/tools.zip
 
 
si qqn arrive a faire qqch   :D  

Reply

Marsh Posté le 06-08-2003 à 11:18:58    

Voici quelques variations sur les fonctions d'éclairement:
 
Phong
C'est une méthode empirique développée par un étudiant français (Phong) dans les labos de l'université de l'Utah.
Elle permet de rendre des objets qui refletent la lumière dans une direction privilégiée, dans ce cas la direction privilégiée est celle du vecteur lumière réfléchi par le vecteur normal à la surface.
 
On parle de terme spéculaire, contrairement au terme de diffusion de Lambert, les termes spéculaires varient en fonction de la position de l'observateur(ce qui se voit dans l'équation qui fait intervenir viewRay.dir).
 
On ajoute le code suivant au code de calcul de l'éclairement :

Code :
  1. float reflet = 2.0f * (lightRay.dir * n);
  2. vecteur phongDir = lightRay.dir - reflet * n;
  3. float phong = _MAX(phongDir * viewRay.dir, 0.0f) ;
  4. phong = currentMat.specvalue * powf(phong, currentMat.specpower) * coef;
  5. red += phong * current.red ;
  6. green += phong * current.green ;
  7. blue += phong * current.blue;


 
Et on obtient une image comme celle-ci:
http://www.massal.net/article/raytrace/page2.html
 
Blinn/Phong
Blinn était l'un des professeurs de Phong (depuis il travaille chez Microsoft). Il a modifié légèrement le modèle spéculaire de Phong pour rendre compte de certains phénomènes physiques.
 
Voici le code à ajouter au calcul du terme de Lambert:

Code :
  1. vecteur blinnDir = lightRay.dir - viewRay.dir;
  2. float temp = sqrtf( blinnDir * blinnDir);
  3. if (temp != 0.0f )
  4. {
  5.   blinnDir = (1.0f / temp) * blinnDir;
  6.   float blinn = _MAX(blinnDir *  n, 0.0f);
  7.   blinn = currentMat.specvalue * powf(blinn, currentMat.specpower) * coef;
  8.   red += blinn * current.red ;
  9.   green += blinn * current.green ;
  10.   blue += blinn * current.blue;
  11. }


 
Visuellement ils sont assez proches comme on peut le voir sur l'image ci dessous:
http://www.massal.net/article/raytrace/page2.html
 
Anti-aliasing
 
Une méthode simple d'antialiasing est le super sampling.
Elle consiste à calculer pour chaque pixel, x fractions de pixels qui contribueront à l'éclairement final.  
Le coût de la méthode "naïve" est strictement linéaire au nombre de fractions de pixel à calculer par pixel.
 
On peut effectuer un supersampling basique (sans jittering, sans overlapping) en ajoutant ces lignes-là dans la boucle de rendu:

Code :
  1. for (int y = 0; y < myScene.sizey; ++y) {
  2. for (int x = 0 ; x < myScene.sizex; ++x) {
  3.   float red = 0, green = 0, blue = 0;
  4.   for(float fragmentx = x; fragmentx < x + 1.0f; fragmentx += 0.5f)
  5.   for(float fragmenty = y; fragmenty < y + 1.0f; fragmenty += 0.5f)
  6.   {
  7.     // la contribution de chaque rayon est diminuée au quart.
  8.     float coef = 0.25f;
  9.     // on continue les traitements comme si de rien n'était
  10.   }
  11.   // puis on écrit la contribution globale de tous les fragments de pixels dans le frame buffer
  12. }


 
Voici le résultat très zoomé du supersampling:
http://www.massal.net/article/raytrace/page2.html
 
Fonction gamma
 
Voici le code qui modifie la sortie via une fonction gamma de base:
 

Code :
  1. float gamma = 0.66;
  2. blue = powf(blue, gamma);
  3. red = powf(red, gamma);
  4. green = powf(green, gamma);
  5. imageFile.put(min(blue*255.0f,255.0f)).put(min(green*255.0f, 255.0f)).put(min(red*255.0f, 255.0f));


 
On peut donc à loisir obtenir une image plus contrastée mais plus sombre ou une image plus claire mais aux teintes plates ou délavées (si l'on pousse trop).
 
voici le résultat avec un gamma de 0.66 respectivement et 1.5:
http://www.massal.net/article/raytrace/page2.html
 
Exposition photo
 
Rajoutons une fonction de calcul d'exposition:

Code :
  1. float exposure = - 0.66f;
  2. blue = 1.0f - expf(blue * exposure);
  3. red = 1.0f - expf(red * exposure);
  4. green = 1.0f - expf(green * exposure);


 
Elle rend compte du fait que le noircissement du papier photo est une fonction non linéaire de la quantité de lumière qui arrive en chacun de ses points: très rapide au départ car il y a beaucoup de réactif disponible, elle devient plus difficile ensuite lorsque la majorité du réactif a migré.
 
L'intérêt de la fonction d'exposition c'est qu'il n'y a pas d'effet de saturation comme avec la fonction "min naïve".
 
http://www.massal.net/article/raytrace/page2.html
http://www.massal.net/article/raytrace/page2.html
La première est corrigée par la fonction "autolevels" de Photoshop. (j'avais la flemme de coder une fonction similaire dans le raytracer).
 
En variant K, le facteur d'exposition on peut arriver à gérer toutes sortes de conditions d'éclairement. En calculant les valeurs d'éclairement sous forme de flottants, on peut évacuer le problème de l'exposition à un calcul final dépendant de l'éclairement global de l'image et certains paramètres plus subjectifs (auto exposition).
 
Voici le résultat d'une image traitée par saturation (opérateur min) sans fonction d'exposition mais avec une lumière trop forte:
http://www.massal.net/article/raytrace/page2.html
 
La même scène qui utilise la fonction d'exposition:
http://www.massal.net/article/raytrace/page2.html
 
Un inconveniant de ces méthodes de correction d'image en post processing c'est qu'elles interférent avec la méthode d'antialiasing par super sampling. Mais c'est un autre probleme..
 
Un autre jour peut-être..
 
LeGreg


Message édité par LeGreg le 29-02-2008 à 20:11:01

---------------
voxel terrain render engine | animation mentor
Reply

Marsh Posté le 06-08-2003 à 11:29:38    

Euh sinon les idées elles me viennent comme ça
vu que c'est mon premier raytracer  
donc si vous avez des envies particulières.
 
Je pense aborder (si j'ai le courage):
- le postprocessing (convolution, etc..)
- les textures (lecture, generation), géneration des coordonnées, bump mapping/horizon mapping.
- image based rendering.
 
LeGreg

Reply

Marsh Posté le 06-08-2003 à 12:06:17    

legreg a écrit :

Euh sinon les idées elles me viennent comme ça
vu que c'est mon premier raytracer  
donc si vous avez des envies particulières.
 
Je pense aborder (si j'ai le courage):
- le postprocessing (convolution, etc..)
- les textures (lecture, generation), géneration des coordonnées, bump mapping/horizon mapping.
- image based rendering.
 
LeGreg


 
C'est pas trop mal, mais dans l'ordre les trucs à ajouter seraient je pense :
- Autre chose que des sphères
- Des sources de lumière étendues en volume pour des ombres plus naturelles
- Des transformations affines à appliquer aux objets.
- Des matériaux plus évolués : refraction, atténuation suivant la distance ...

Reply

Marsh Posté le 06-08-2003 à 12:20:27    

Kristoph a écrit :


 
C'est pas trop mal, mais dans l'ordre les trucs à ajouter seraient je pense :
- Autre chose que des sphères


En commençant par des plans, comme ça on peut déjà faire pas mal de chose.
Et ensuite des cones ( mais c'est un peu plus compliqué niveau math ).
Je crois qu'à partir de sphère, cones, plan, on peut déjà faire pas mal de truc sympa.


---------------
Gérez votre collection de BD en ligne ! ---- Electro-jazzy song ---- Dazie Mae - jazzy/bluesy/cabaret et plus si affinité
Reply

Marsh Posté le 06-08-2003 à 12:31:30    

tomlameche a écrit :


En commençant par des plans, comme ça on peut déjà faire pas mal de chose.
Et ensuite des cones ( mais c'est un peu plus compliqué niveau math ).
Je crois qu'à partir de sphère, cones, plan, on peut déjà faire pas mal de truc sympa.  


 
Plus que le cone, le cube je dirais. Mais sans les déformations affines, c'est encore un peu difficile de faire des scènes variées. C'est juste du calcul matriciel très bourrin non :D
 
Un autre truc vraiment bien et pas difficile à faire c'est la CSG : Constructive Solid Geometry.

Reply

Marsh Posté le 06-08-2003 à 12:33:36    

Kristoph a écrit :


 
Plus que le cone, le cube je dirais. Mais sans les déformations affines, c'est encore un peu difficile de faire des scènes variées. C'est juste du calcul matriciel très bourrin non :D
 
Un autre truc vraiment bien et pas difficile à faire c'est la CSG : Constructive Solid Geometry.


Voui, puis peut être encore avant le cone, le cylindre.
C'est quoi la  Constructive Solid Geometry ?


---------------
Gérez votre collection de BD en ligne ! ---- Electro-jazzy song ---- Dazie Mae - jazzy/bluesy/cabaret et plus si affinité
Reply

Marsh Posté le 06-08-2003 à 12:36:10    

tomlameche a écrit :


Voui, puis peut être encore avant le cone, le cylindre.
C'est quoi la  Constructive Solid Geometry ?


 
Opération booléaines sur les objets. Des unions, des intersections et des soustractions.

Reply

Marsh Posté le 06-08-2003 à 12:36:10   

Reply

Marsh Posté le 06-08-2003 à 13:01:45    

Mouai c'est pas mal.
Bah je rigole biensûr, c'est très beau :jap: :jap: :jap:
Perso ça serait cool si tu pouvais poster un code compilable (le .h entre autre) afin qu'on test. Si tu pouvais aussi montrer comment on fait une boule style chrome (c'est quoi les couleurs ?).
A partir du travail d'un pote qui a fait un raytracing en VTK, on a essayé de faire quelques trucs. Mais c'est très lent, surtout pour les sphères. Car ca test les collisions avec les polygones. En revanche on peut mettre n'importe quoi : cube, cône, ... j'ai lancé un calcul hier soir et voilà ce que ça donne. C'est pas full quality, surtout la sphère, car ca met hyper longtemps à calculer. Je sais pas combien de temps ça a mis, mais surement plusieurs heures (celeron 600). Voici l'image d'origine :
http://www.chez.com/regatbar/vtk/origin.htm
résultat:
http://www.chez.com/regatbar/vtk/result.htm
 
Plus y'a de polygones, plus c'est lent. Donc la sphere ca fait mal. On va étudier ton code et essayer d'améliorer.
 
 
Au fait, ce code fait du ray tracing avec projection conique.


Message édité par HelloWorld le 06-08-2003 à 13:04:34

---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 06-08-2003 à 14:22:20    

HelloWorld a écrit :

Mouai c'est pas mal.
Bah je rigole biensûr, c'est très beau :jap: :jap: :jap:
Perso ça serait cool si tu pouvais poster un code compilable (le .h entre autre) afin qu'on test. Si tu pouvais aussi montrer comment on fait une boule style chrome (c'est quoi les couleurs ?).
A partir du travail d'un pote qui a fait un raytracing en VTK, on a essayé de faire quelques trucs. Mais c'est très lent, surtout pour les sphères. Car ca test les collisions avec les polygones. En revanche on peut mettre n'importe quoi : cube, cône, ... j'ai lancé un calcul hier soir et voilà ce que ça donne. C'est pas full quality, surtout la sphère, car ca met hyper longtemps à calculer. Je sais pas combien de temps ça a mis, mais surement plusieurs heures (celeron 600). Voici l'image d'origine :
http://www.chez.com/regatbar/vtk/origin.htm
résultat:
http://www.chez.com/regatbar/vtk/result.htm
 
Plus y'a de polygones, plus c'est lent. Donc la sphere ca fait mal. On va étudier ton code et essayer d'améliorer.
 
 
Au fait, ce code fait du ray tracing avec projection conique.


 
Dans POV ray et dans tout bon raytracer, les sphères sont de vrai sphères, les cubes sont de vrai cubes et les cylindres de vrai cylindres. Pas de poly ça fait moche.
En plus, une vrai sphère doit être plus rapide à dessiner qu'un simple triangle ;)

Reply

Marsh Posté le 06-08-2003 à 14:53:13    

Bah ouai. VTK c'est du volume rendering. Ca c'est le truc de base. La sphère je peux la bidouiller genre les CSG, en extraire des tanches.
C'est clair que VTK c'est pas fait pour. Ca permet comme ça de bien se rendre compte des perfs et limites de chaque technique.
Au fait, ca met combien de temps à calculer tes images legreg ?


---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 06-08-2003 à 15:11:32    

HelloWorld a écrit :

Bah ouai. VTK c'est du volume rendering. Ca c'est le truc de base. La sphère je peux la bidouiller genre les CSG, en extraire des tanches.
C'est clair que VTK c'est pas fait pour. Ca permet comme ça de bien se rendre compte des perfs et limites de chaque technique.
Au fait, ca met combien de temps à calculer tes images legreg ?


 
Une image parreil, je lui donne qq secondes pour le calcul. J'ai un ami qui a fait tourner un raytracer en temps réel avec 1-2 sphères reflechissantes ( 1 reflexion max ), une source de lumière et un plan pour 30 FPS en 160x100, ceci sur un Pentium 90 :D Il l'aurait codé en assembleur que ça ne m'étonnerais pas.


Message édité par Kristoph le 06-08-2003 à 15:11:51
Reply

Marsh Posté le 06-08-2003 à 18:45:46    

le temps de rendu est de l'ordre de moins d'une seconde pour la premiere image. trois spheres, deux lumières pas de quoi peter
trois pattes a un canard.
Tu multiplies ça par le nombre de pixels supplémentaires en super sampling et tu ajoutes le cout de calcul des exposants, terme speculaire et tu arrives a quelques secondes.
 
Sinon pour vos suggestions, j'etudierai ça mais bon le but de cet article ce n'est pas d'arriver a un raytracer complet (puisqu'il y a pov ray..). Mais d'aborder les problemes de façon simple(iste) et d'introduire quelques notions utilisées en synthèse d'image.
 
LeGreg

Reply

Marsh Posté le 06-08-2003 à 19:14:58    

legreg a écrit :

le temps de rendu est de l'ordre de moins d'une seconde pour la premiere image. trois spheres, deux lumières pas de quoi peter
trois pattes a un canard.
Tu multiplies ça par le nombre de pixels supplémentaires en super sampling et tu ajoutes le cout de calcul des exposants, terme speculaire et tu arrives a quelques secondes.
 
Sinon pour vos suggestions, j'etudierai ça mais bon le but de cet article ce n'est pas d'arriver a un raytracer complet (puisqu'il y a pov ray..). Mais d'aborder les problemes de façon simple(iste) et d'introduire quelques notions utilisées en synthèse d'image.
 
LeGreg


 
Il y a 2 sujets qui me plairaient personnellement : la CSG et la génération de textures 3D. Il y a bien sur beaucoup d'autres sujets passionants mais je pense qu'il est possible de traiter ces 2 là de façon assez simple.

Reply

Marsh Posté le 06-08-2003 à 20:08:32    

Vu que le programme semble encore au stade du "c'est joli mais ça fait pas grand chose", je suggère, juste comme ça, une syntaxe moins limitée et plus lisible, parceque là c'est pas vraiment manipulable.
 
Je trouve la syntaxe de PovRay assez claire, et vu l'état d'avancement de ce raytracer, y'a de quoi faire sans avoir à faire la moindre modification à la syntaxe.
 
Pour ceux qui ne connaissent pas povray :
 
http://www.povray.org
 
Les sources sont libres, ça peut donner des idées pour certaines fonctions/optimisations.

Reply

Marsh Posté le 06-08-2003 à 20:14:50    

Sinon, une autre chose qui pourrait être intéressante, serait de rendre multi-thread ce programme. En effet, ce qui pose de gros problèmes sur la plupart des raytracers (notamment povray) c'est l'absence de support pour les architectures multi-processeurs.
 
Une approche assez simple pour faire du multi-processeur, c'est de faire générer une ligne sur deux par deux threads différents, ou plus (paramétrable). L'avantage de l'architecture multi-thread dans ce cas, par rapport au multi-processus, c'est :
1) support aussi bon sous Windows que sous Linux (Windows gère très mal le multi-process)
2) la scène, le fichier, et l'index des lignes à générer sont communs à tous les threads, donc l'architecture multi-thread simplifie les échanges d'informations entre les threads fils et le thread père.
3) support, sans code particulier de plusieurs processeurs


Message édité par MagicBuzz le 06-08-2003 à 20:15:19
Reply

Marsh Posté le 06-08-2003 à 20:18:58    

MagicBuzz a écrit :

Vu que le programme semble encore au stade du "c'est joli mais ça fait pas grand chose", je suggère, juste comme ça, une syntaxe moins limitée et plus lisible, parceque là c'est pas vraiment manipulable.


 
Le programme ne sera jamais "utile",
comme je le disais il y a pov ray pour ceux qui veulent faire des images.
C'est juste une introduction du genre :
"regardez on peut faire un raytracer en 200 lignes de C++".
 
Pour info, je ponds ces lignes de code en rentrant du travail (où je code aussi..) le plus souvent assez tard et il y a une limite à ce que je peux pondre à cette période; il n'y a aucune pretention d'utilité comme je le disais à part l'introduction de notions aléatoires comme les calculs de réflexion, l'intersection rayon/sphere, les différentes équations d'éclairage.
Le format de fichier je n'ai vraiment pas envie de passer du temps dessus donc il restera sommaire et illisible :).
 
LeGreg

Reply

Marsh Posté le 06-08-2003 à 20:36:56    

MagicBuzz a écrit :

Sinon, une autre chose qui pourrait être intéressante, serait de rendre multi-thread ce programme. En effet, ce qui pose de gros problèmes sur la plupart des raytracers (notamment povray) c'est l'absence de support pour les architectures multi-processeurs.
 
Une approche assez simple pour faire du multi-processeur, c'est de faire générer une ligne sur deux par deux threads différents, ou plus (paramétrable). L'avantage de l'architecture multi-thread dans ce cas, par rapport au multi-processus, c'est :
1) support aussi bon sous Windows que sous Linux (Windows gère très mal le multi-process)
2) la scène, le fichier, et l'index des lignes à générer sont communs à tous les threads, donc l'architecture multi-thread simplifie les échanges d'informations entre les threads fils et le thread père.
3) support, sans code particulier de plusieurs processeurs


 
La version multithread va nécessiter quelques modifications
la premiere c'est que l'on ne traite qu'une seule frame dans ce bout de programme: on ne peut pas écrire directement dans le fichier, il faut donc passer par un buffer indépendant.
J'avais pensé a passer par un buffer pour le post processing mais ce n'était pas forcément celui de l'image que j'avais en tête.
 
pour etre efficace en multithread il faudrait qu'il soit dejà aussi efficace que possible sur chacun des processeurs indépendants, ce qui n'est pas encore le cas.  
Si l'on peut obtenir un gain de x2 sur deux proc (ce qui optimiste dans le cas general), il est probable que je pourrais deja atteindre un gain plus important si je m'attachais a la rapidité d'execution des algorithmes utilisés.
 
Bon on verra, de toute façon le rythme des updates se fera en fonction de ma charge de boulot au bureau..
 
LeGreg

Reply

Marsh Posté le 06-08-2003 à 20:50:40    

Je ne vois vraiment pas l'interet de perdre du temps sur une gestion multithread. Dans un raytracer classique, une simple scene ne necessite que très rarement une vitesse de calcul gigantesque. Ce qui coute *vraiment* c'est les animations à générer et il est très facile de parallèliser ce genre de calcul et laissant chaque process le role de calculer une image séparée.
 
De plus, si on arrive à une scène dont la complexité justifie de mettre en place du calcul distribué, alors le temps de création d'un process deviens complètement négligeable, même sous windows. Il suffit juste de lancer 4 instances du raytracer chargées chacunes de calculer un quart de la scène finale ( très facile à faire en manipulant la caméra de rendu ).

Reply

Marsh Posté le 06-08-2003 à 22:03:42    

MagicBuzz a écrit :


OK, d'accord, donc en une ligne "ça sert à rien", c'est ça ? :)
 
Nan, parceque je m'amuse à transcrire le prog de legreg en javascript pour montrer que le javascript n'est pas aussi nul que ça ;)
 
Mais vu que j'y connais rien en C, bah j'ai un peu de mal :D


 
En un sens oui. Mais il y a des cas ou cela sert.
 
Sinon, je maitiens ma position : le multithread ce n'est vraiment pas utile à coder pour un raytracer car il suffit de calculer 2 moitiés de l'image en parallèle puis de les coller après coup et on obtient le même résultat pour un temps de dev bien moindre.
 
PS : le javascript c'est petit joueur, fais moi ça en RPL sur HP 48. Je dois avoir un programme de comparaison par ici :D

Reply

Marsh Posté le 06-08-2003 à 22:10:35    

à propos du mutli-thread: on va pas se lancer dans des histoires win/linux (meme si les pthread sont mieux foutus) => utilisation des thread de boost, jetez un oeil
 
apres une ligne sur deux, ça me parait pas terrible, et surtout pas bien réguler: c'est evident que certaines lignes vont être traiter plus vite que d'autres. alors faut faire une répartition dynamique. une file d'attente avec des paquets par exemple
 
anyway, http://boost.org/libs/thread/doc/index.html

Reply

Marsh Posté le 06-08-2003 à 22:17:50    

Taz a écrit :

à propos du mutli-thread: on va pas se lancer dans des histoires win/linux (meme si les pthread sont mieux foutus) => utilisation des thread de boost, jetez un oeil
 
apres une ligne sur deux, ça me parait pas terrible, et surtout pas bien réguler: c'est evident que certaines lignes vont être traiter plus vite que d'autres. alors faut faire une répartition dynamique. une file d'attente avec des paquets par exemple
 
anyway, http://boost.org/libs/thread/doc/index.html


oui oui, quand je parlais de "une ligne sur deux", je pensais à "chacun sa ligne", avec une file d'attente évidement, parceque sinon un thread risque de prendre de l'avance, et on se retrouve à devoir stocker toute l'image en mémoire au lieu de la flusher au fur et à mesure :)

Reply

Marsh Posté le 06-08-2003 à 22:19:26    

Le raytracing distribué c'est le sujet de these
d'un collègue dans le cube d'à coté donc je lui demanderai son avis si jamais j'ai besoin. (Photon4D pour ceux qui connaissent)
 
En attendant: pas nécessaire et pas d'amélioration visible du rendu donc en queue de liste des explications à venir.
 
LeGreg

Reply

Marsh Posté le 06-08-2003 à 22:32:12    

MagicBuzz a écrit :


OK, d'accord, donc en une ligne "ça sert à rien", c'est ça ? :)


 
Il y a des fois ou le compilateur a du mal à inférer le type, va générer des warnings lors d'une perte de précision possible, donc j'ai pris l'habitude de le le préciser.
 
Ici il va peut-etre générer des warnings pour des conversions float/int int/float mais c'est bien ce qu'on veut dans ce cas.
 

MagicBuzz a écrit :

Nan, parceque je m'amuse à transcrire le prog de legreg en javascript pour montrer que le javascript n'est pas aussi nul que ça ;)


 
Ca ne prouvera rien. Il est aussi possible de faire un raytracer en BASIC..
 
sinon le langage de choix pour les raytracer c'est OCAML.
C'est pas moi qui le dit. http://www.cs.cornell.edu/icfp/contest_results.htm
 
LeGreg

Reply

Marsh Posté le 07-08-2003 à 09:38:52    

Vi. Post un code complet compilable stp.
Et pour le multithread, je suis pas d'accord. C'est pas le sujet. On pourrait aussi dessiner dans une fenêtre, avec possibilité de sauvegarder en JPEG, etc ... encore que ça se révèlera + utile que le multithread sur un monoproc, ce qui est je pense le cas d'à peu près tout le monde ici.
Je préfère découvrir comment on fait une boule chromée.


---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 07-08-2003 à 12:10:50    

no update today.
 
Pour le .h il s'agit de definition des types vecteur, point, sphere, ray etc..
 
J'utilise une bibliotheque toute prete c'est la lib std du C++.
(pour les ouvertures de fichier et les fonctions de math de base et les vector (le conteneur))
 
Si je poursuis ce que j'ai prévu il est possible que j'ajoute
d'autres librairies, comme une lib de fft ou bien un source de tone mapping.
 
A+
LeGReg

Reply

Marsh Posté le 07-08-2003 à 22:31:49    

Argh :) La convertion en JavaScript est "presque" terminée :D
 
http://perso.wanadoo.fr/magicbuzz/ray.htm
 
Seul soucis :
 
1) C'est très lent, donc je me limite à une image en 32 * 20 (avec des pixels de 5 x 5 :D) C'est l'affichage qui plombe tout. Sans l'affichage, ça met moins d'une seconde pour du 64 * 40
 
2) Doit y avoir un bug, c'est tout noir :D
 
-- Edit: J'ai fais pas mal de corrections --
 
Maintenant, il semblerait qu'il n'y ait plus de "bug". C'est à dire que tout le code se comporte de la façon escomptée par rapport à ce que j'ai écrit.
 
Par contre, j'ai des valeurs extrêment petites, ce qui fait que je me retrouve avec une image toute noire... Pas terrible en somme.
 
Voici la scène que j'utilise :
 


16 12 // taille du viewport  
3 3 2 // nbre de materiel, de spheres et de lumieres  
1.0 1.0 0.0 0.5 // premier materiel: rouge vert bleu et coef de reflexion  
0.0 1.0 1.0 0.5 // deuxieme materiel  
1.0 0.0 1.0 0.5 // troisiem materiel  
5.825 7.25 0.0 2.5 0 // sphere 1: posx, posy, posz, rayon, materiel id  
10.175 7.25 0.0 2.5 1 // sphere 2  
8 3.5 0.0 2.5 2 // sphere 3  
0.0 6 -100 1.0 1.0 1.0 // light 1 : posx, posy, posz, intensité rouge, vert et bleu  
16 6 -10000 0.6 0.7 1.0 // light 2


 
En somme j'ai tout divisé par 40 pour que ça soit rapide à calculer en JS.
 
Si lors de l'affichage je force à blanc dès qu'une des trois couleur est différente de 0, j'obtiens bien mes trois boules. J'ai noté d'ailleurs qu'elles ne sont pas rondes... Il semblerait que j'ai un problème avec la recherche d'ombre (en plus du reste)
 
En fait, ce que je n'arrive pas à vérifier, c'est le calcul de la normale, et la variable "lambert".
 
En dehors de ça, ça semble tourner comme il faut.
 
Juste une question à legreg au fait :
-> Tes tableaux (de matérieux, de sphères et de lumières), ils commencent bien à 0 ? Parceque je suis en train de me dire que j'ai forcément une coquille aussi grosse que ça quelquepart, parceque je ne comprends vraiment pas où ça merde...
 
PS: pour voir mon source, il suffit de faire view source sur la page que j'ai mis en lien.
 
-- Enorme optimisation : j'ai abandonné le modèle objet lors des calculs. C'est infiniment plus rapide. Par contre le code est carrément moins lisible :D --


Message édité par MagicBuzz le 08-08-2003 à 03:00:37
Reply

Marsh Posté le 08-08-2003 à 09:52:19    

quick update today: je suis rentré a dix heures du boulot
et je vais probablement faire des heures sup ce week-end.. je sais vous vous fichez de ma vie :o  
 
Bon j'ai pondu (pour pas dire ch..) une méthode empirique d'autoexposition pour éviter d'avoir a taper la valeur à la main
 
voici le code de calcul:
 

Code :
  1. float exposure;
  2. float accufacteur = max(myScene.sizex, myScene.sizey);
  3. accufacteur = accufacteur / ACCUMULATION_SIZE;
  4. couleur mediumPoint = {0.0f, 0.0f, 0.0f};
  5. const float mediumPointWeight = 1.0f /(ACCUMULATION_SIZE*ACCUMULATION_SIZE);
  6. for (int y = 0; y < ACCUMULATION_SIZE; ++y) {
  7. for (int x = 0 ; x < ACCUMULATION_SIZE; ++x) {
  8.   ray viewRay = { {float(x)*accufacteur, float(y) * accufacteur, -10000.0f}, { 0.0f, 0.0f, 1.0f}};
  9.   couleur currentCouleur = addRay (viewRay, myScene);
  10.   mediumPoint = mediumPoint + mediumPointWeight * ((*currentCouleur) * (*currentCouleur));
  11. }
  12. }
  13. float mediumLuminance = sqrtf(0.2126f * mediumPoint.red + 0.715160f * mediumPoint.green + 0.072169 * mediumPoint.blue);
  14. if (mediumLuminance > 0.001f)
  15. {
  16.   exposure = logf(0.6f) / mediumLuminance;
  17. }
  18. else
  19. {
  20.   exposure = -1.0f;
  21. }


 
L'idée (empirique et probablement pas très rationnelle)
est que l'on mesure la quantité d'énergie moyenne par pixel.
Comme on a juste besoin d'une valeur moyenne, on travaille en basse résolution (128x128) par exemple. on somme le carré des intensités lumineuse (sensé représenter l'énergie reçue par pixel). Puis à la fin on calcule l'intensité correspondante à cette énergie moyenne. Puis on mappe cette intensité calculée
à une valeur de gris intérmédiaire (en applicant l'inverse de la fonction d'exposition à dans cet exemple 0.6f).
 
C'est bidon mais ça marche à peu près:
http://www.massal.net/article/raytrace/page3.html
 
LeGreg
ps pour magicbuzz: oui en C comme en C++ tout tableau
est numéroté à partir de zéro.


Message édité par LeGreg le 29-02-2008 à 20:11:43

---------------
voxel terrain render engine | animation mentor
Reply

Marsh Posté le 08-08-2003 à 15:55:59    

MagicBuzz a écrit :

Argh :) La convertion en JavaScript est "presque" terminée :D
 
http://perso.wanadoo.fr/magicbuzz/ray.htm


 
Heu, franchement, j'veux pas te vexer.  
Mais, l'intéret de la convertion en Javascript, il est où ?
C pas vraiment fait pr ce genre de trucs le javascript qd même...

Reply

Marsh Posté le 10-08-2003 à 20:31:39    

update pour l'apéro du dimanche avant le méchoui:
http://www.massal.net/article/raytrace/page3.html
 
Je posterai le code plus tard.
En gros c'est une technique de génération de coordonnées de texture appelée "cube environment mapping".
Le raytracer va lire dans six tampons représentants un arrière plan fictif mais en ne tenant compte que de la direction (C'est une simplification sinon il aurait aussi besoin de la position absolue et de la position du cube dans la scène ce qui dépasse le cadre de notre mapping).
Ici on utilise la direction du rayon vue modifié par reflection par rapport à la normale à la surface. En pratique on ne modifie rien au code du raytracer proprement dit mais au cas où un rayon vue "réfléchi" ne rencontre aucune sphère, alors cela se termine par un texture lookup (lecture du fichier texture).
 
LeGreg
PS: le fond gris privient de la cubemap aussi. Je vous laisse deviner pourquoi il est uniforme.


Message édité par LeGreg le 29-02-2008 à 20:12:08

---------------
voxel terrain render engine | animation mentor
Reply

Marsh Posté le 11-08-2003 à 09:01:44    

Voici le code:
 

Code :
  1. int currentSphere=-1;
  2. for (unsigned int i = 0; i < myScene.sphTab.size() ; ++i) {
  3.   if (hitSphere(viewRay, myScene.sphTab[i], t)) {
  4.     currentSphere = i;
  5.   }
  6. }
  7. if (currentSphere == -1)
  8. {
  9.   output = output + coef * readCubemap(viewRay);
  10.   break;
  11. }


 
Comme je l'avais dit, en cas de non intersection du rayon vue avec une sphere, on fait un texture lookup dans la cubemap avec le rayon vue courant.
 
Le code pour lire dans la cubemap est "straightforward": on a six cas pour les six faces du cube, et on différencie chacun des six cas par des conditions "if":
 

Code :
  1. couleur readCubemap(ray myRay)
  2. {
  3.     cubemap &cm = cubemap::getInstance();
  4.     couleur * currentCouleur ;
  5.     couleur outputCouleur = {0.0f,0.0f,0.0f};
  6.     if ((fabsf(myRay.dir.x) >= fabsf(myRay.dir.y)) && (fabsf(myRay.dir.x) >= fabsf(myRay.dir.z)))
  7.     {
  8.         if (myRay.dir.x > 0.0f)
  9.         {
  10.             currentCouleur = cm.texture + cubemap::right * 512 * 512;
  11.             outputCouleur = readTexture(currentCouleur,
  12.                 1.0f - (myRay.dir.z / myRay.dir.x+ 1.0f) * 0.5f,
  13.                 (myRay.dir.y / myRay.dir.x+ 1.0f) * 0.5f, 512,512);
  14.         }
  15.         else if (myRay.dir.x < 0.0f)
  16.         {
  17.             currentCouleur = cm.texture + cubemap::left * 512 * 512;
  18.             outputCouleur = readTexture(currentCouleur,
  19.                 1.0f - (myRay.dir.z / myRay.dir.x+ 1.0f) * 0.5f,
  20.                 1.0f - ( myRay.dir.y / myRay.dir.x + 1.0f) * 0.5f,
  21.                 512,512);
  22.         }
  23.     }
  24.     else if ((fabsf(myRay.dir.y) >= fabsf(myRay.dir.x)) && (fabsf(myRay.dir.y) >= fabsf(myRay.dir.z)))
  25.     {
  26.         if (myRay.dir.y > 0.0f)
  27.         {
  28.             currentCouleur = cm.texture + cubemap::up * 512 * 512;
  29.             outputCouleur = readTexture(currentCouleur,
  30.                 (myRay.dir.x / myRay.dir.y + 1.0f) * 0.5f,
  31.                 1.0f - (myRay.dir.z/ myRay.dir.y + 1.0f) * 0.5f, 512,512);
  32.         }
  33.         else if (myRay.dir.y < 0.0f)
  34.         {
  35.             currentCouleur = cm.texture + cubemap::down * 512 * 512;
  36.             outputCouleur = readTexture(currentCouleur,
  37.                 1.0f - (myRay.dir.x / myRay.dir.y + 1.0f) * 0.5f,
  38.                 (myRay.dir.z/myRay.dir.y + 1.0f) * 0.5f, 512,512);
  39.         }
  40.     }
  41.     else if ((fabsf(myRay.dir.z) >= fabsf(myRay.dir.x)) && (fabsf(myRay.dir.z) >= fabsf(myRay.dir.y)))
  42.     {
  43.         if (myRay.dir.z > 0.0f)
  44.         {
  45.             currentCouleur = cm.texture + cubemap::forward * 512 * 512;
  46.             outputCouleur = readTexture(currentCouleur,
  47.                 (myRay.dir.x / myRay.dir.z + 1.0f) * 0.5f,
  48.                 (myRay.dir.y/myRay.dir.z + 1.0f) * 0.5f, 512,512);
  49.         }
  50.         else if (myRay.dir.z < 0.0f)
  51.         {
  52.             currentCouleur = cm.texture + cubemap::backward * 512 * 512;
  53.             outputCouleur = readTexture(currentCouleur,
  54.                 (myRay.dir.x / myRay.dir.z + 1.0f) * 0.5f,
  55.                 1.0f - (myRay.dir.y /myRay.dir.z+1) * 0.5f, 512,512);
  56.         }
  57.     }
  58.     return outputCouleur;
  59. }


 
Comme vous pouvez le voir dans ce code, la texture du cube est simplement chargé dans un tableau où les six faces se suivent dans un ordre défini par les valeurs des énum "up, down, left, right, forward, backward". La taille de chaque face est hardcodée dans cet exemple à 512x512. On utilise ensuite la fonction readtexture qui fait une interpolation bilinéaire pour lire chaque valeur de la texture.
 

Code :
  1. couleur readTexture(const couleur* tab, float u, float v, int sizeU, int sizeV)
  2. {
  3.     u = fabsf(u);
  4.     v = fabsf(v);
  5.     int umin = sizeU * u;
  6.     int vmin = sizeV * v;
  7.     float ucoef = fabsf(sizeU * u - umin);
  8.     float vcoef = fabsf(sizeV * v - vmin);
  9.     couleur output =
  10.         (1.0f - vcoef) *
  11.         ((1.0f - ucoef) * tab[umin % sizeU + sizeU * (vmin % sizeV)]
  12.         + ucoef * tab[(umin + 1) % sizeU + sizeU * (vmin % sizeV)])
  13.         +   vcoef *
  14.         ((1.0f - ucoef) * tab[umin % sizeU + sizeU * ((vmin+1) % sizeV)]
  15.         + ucoef * tab[(umin + 1) % sizeU + sizeU * ((vmin+1) % sizeV)]);
  16.     return output;
  17. }


 
Le rectangle original de la texture est remappées à [0,1] et le motif est simplement répété pour les coordonnées qui sont plus grandes que 1. on peut raffiner en proposant plusieurs modes d'adressages : clamp (les valeurs supérieures à un sont ramenées a 1), mirror (les valeurs sont périodiquement inversées pour les valeurs supérieures à un), repeat (c'est le mode qu'on utilise).
 
Je ne cherche pas à finauder spécialement dans ce code, ça fait ce qu'on lui demande pour l'instant, j'améliorerai en temps nécessaire.
 
Note secondaire: on utilise un filtrage bilinéaire pour l'interpolation des valeurs intermédiaires de la texture. Ce filtrage a beaucoup de sens pour des textures codées sur 8 bits à précision fixe. Mais quand on travaille sur des flottants cela perd beaucoup de son sens, surtout quand on postprocess le tout  
pour ramener a un intervalle à precision fixe codé sur 8 bits.. J'y reviendrai peut-etre..
 
LeGreg
 
ps:Voici un exemple de cubemap avec les six textures du cube mises bout à bout:
 
http://www.massal.net/article/raytrace/page3.html


Message édité par LeGreg le 29-02-2008 à 20:12:35

---------------
voxel terrain render engine | animation mentor
Reply

Marsh Posté le 12-08-2003 à 09:32:17    

On continue sur notre lancée.
 
Aujourd'hui un peu de textures procédurales pour obtenir quelque chose dans ce genre là:
 
http://www.massal.net/article/raytrace/page3.html
 
Ces textures ne sont stockées nulle part en mémoire, elles sont calculées à la volèe.
 
Les intérêts sont multiples. Le premier c'est qu'on peut définir des matériaux avec une précision qui n'est pas fixé par la taille du fichier texture. Ainsi on peut approcher une structure fractale avec des textures aux détails quasiment infinis.
 
Le deuxième intéret provient de la méthode utilisée qui repose sur le bruit de Perlin. Cette fonction de bruit est simple à mettre en oeuvre et est réellement tridimensionnelle (ou bidimensionnelle ou monodimensionnelle). Cela évite notamment d'avoir à se prendre la tête sur le mapping de la sphère; pas de distorsion, pas de pôles. C'est comme si l'objet était directement taillé dans un volume texturé.
 
Voici le code adapté du site web de Ken Perlin:
 

Code :
  1. #include "perlin.h"
  2. static int permutation[] = { 151,160,137,91,90,15,
  3.    131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
  4.    190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
  5.    88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
  6.    77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
  7.    102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
  8.    135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
  9.    5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
  10.    223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
  11.    129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
  12.    251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
  13.    49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
  14.    138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
  15.    };
  16. static double fade(double t)
  17. {
  18.     return t * t * t * (t * (t * 6 - 15) + 10);
  19. }
  20. static double lerp(double t, double a, double b) {
  21.     return a + t * (b - a);
  22. }
  23. static double grad(int hash, double x, double y, double z) {
  24.     int h = hash & 15;                      // CONVERT LO 4 BITS OF HASH CODE
  25.     double u = h<8||h==12||h==13 ? x : y,   // INTO 12 GRADIENT DIRECTIONS.
  26.             v = h<4||h==12||h==13 ? y : z;
  27.     return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
  28. }
  29.  
  30. double noise(double x, double y, double z) {
  31.     perlin & myPerlin = perlin::getInstance();
  32.     int X = (int)floor(x) & 255,                  // FIND UNIT CUBE THAT
  33.         Y = (int)floor(y) & 255,                  // CONTAINS POINT.
  34.         Z = (int)floor(z) & 255;
  35.     x -= floor(x);                                // FIND RELATIVE X,Y,Z
  36.     y -= floor(y);                                // OF POINT IN CUBE.
  37.     z -= floor(z);
  38.     double u = fade(x),                                // COMPUTE FADE CURVES
  39.             v = fade(y),                                // FOR EACH OF X,Y,Z.
  40.             w = fade(z);
  41.     int A = myPerlin.p[X  ]+Y, AA = myPerlin.p[A]+Z, AB = myPerlin.p[A+1]+Z,      // HASH COORDINATES OF
  42.         B = myPerlin.p[X+1]+Y, BA = myPerlin.p[B]+Z, BB = myPerlin.p[B+1]+Z;      // THE 8 CUBE CORNERS,
  43.     return lerp(w, lerp(v, lerp(u, grad(myPerlin.p[AA  ], x  , y  , z   ),  // AND ADD
  44.                                     grad(myPerlin.p[BA  ], x-1, y  , z   )), // BLENDED
  45.                             lerp(u, grad(myPerlin.p[AB  ], x  , y-1, z   ),  // RESULTS
  46.                                     grad(myPerlin.p[BB  ], x-1, y-1, z   ))),// FROM  8
  47.                     lerp(v, lerp(u, grad(myPerlin.p[AA+1], x  , y  , z-1 ),  // CORNERS
  48.                                     grad(myPerlin.p[BA+1], x-1, y  , z-1 )), // OF CUBE
  49.                             lerp(u, grad(myPerlin.p[AB+1], x  , y-1, z-1 ),
  50.                                     grad(myPerlin.p[BB+1], x-1, y-1, z-1 ))));
  51. }
  52. perlin::perlin (void)
  53. {
  54.     for (int i=0; i < 256 ; i++) {
  55.         p[256+i] = p[i] = permutation[i];
  56.     }
  57. }


 
Le code du bruit de Perlin est livré comme cela, je ne l'expliquerai pas: Pour les explications, référez vous au site web de Perlin ( http://mrl.nyu.edu/~perlin/ ).
 
Le point d'entrée est la fonction noise(double, double, double), le reste sont des fonctions auxiliaires.
 
La sphère en haut à gauche utilise une variation entre deux couleurs de diffusion modulées par un coéficient de "turbulence".
 

Code :
  1. case material::turbulence:
  2. {
  3.   for (int level = 1; level < 10; level ++)
  4.   {
  5.     noiseCoef += (1.0f / level )
  6.        * fabsf(noise(level * 0.05 * double(newStart.x),
  7.                      level * 0.05 * double(newStart.y),
  8.                      level * 0.05 * double(newStart.z)));
  9.   };
  10.   output = output +  coef * (lambert * current.col)
  11.        * (noiseCoef * currentMat.col1 + (1.0f - noiseCoef) * currentMat.col2);
  12.   break;
  13. }


 
Le coté "dur" de la turbulence est créé par l'irrégularité de la dérivabilité apportée par la valeur absolue autour de zéro. Son côté fractal est apporté par l'addition de plusieurs termes du bruit de perlin mais à des niveaux de détails de plus en plus fins (la variable level).
 
La sphère en bas utilise une fonction similaire pour moduler l'argument d'une fonction sinus. La périodicité de la fonction sinus simule les bandes de matière d'un marbre et le bruit "turbulent" simule la contamination "naturelle" des zones des différents types de marbre.
 

Code :
  1. case material::marble:
  2. {
  3.   for (int level = 1; level < 10; level ++)
  4.   {
  5.     noiseCoef +=  (1.0f / level)
  6.       * fabsf(noise(level * 0.05 * double(newStart.x),
  7.                     level * 0.05 * double(newStart.y),
  8.                     level * 0.05 * double(newStart.z)));
  9.   };
  10.   noiseCoef = 0.5f * sinf( (newStart.x + newStart.y) * 0.05 + noiseCoef) + 0.5f;
  11.   output = output +  coef * (lambert * current.col)
  12.       * (noiseCoef * currentMat.col1 + (1.0f - noiseCoef) * currentMat.col2);
  13.   break;
  14. }


 
La sphère en haut a droite est une illustration d'une forme de bump mapping. Le vecteur normal à la surface subit une perturbation modulée par la fonction de bruit différente selon chaque axe. Le résultat est un vecteur normal perturbé que l'on renormalise. Puis ce vecteur est utilisé pour calculer les fonctions d'éclairage et le reflexions ultérieures. L'image refletée par cette sphère est très déformée. En fait il suffit d'une petit variation de toute surface plane pour provoquer un très grand déplacement dans l'image réfléchie.
 

Code :
  1. float noiseCoefx = float(noise(0.1 * double(newStart.x), 0.1 * double(newStart.y),0.1 * double(newStart.z)));
  2. float noiseCoefy = float(noise(0.1 * double(newStart.y), 0.1 * double(newStart.z),0.1 * double(newStart.x)));
  3. float noiseCoefz = float(noise(0.1 * double(newStart.z), 0.1 * double(newStart.x),0.1 * double(newStart.y)));
  4. n.x = (1.0f - currentMat.bump ) * n.x + currentMat.bump * noiseCoefx;
  5. n.y = (1.0f - currentMat.bump ) * n.y + currentMat.bump * noiseCoefy;
  6. n.z = (1.0f - currentMat.bump ) * n.z + currentMat.bump * noiseCoefz;
  7. temp = n * n;
  8. if (temp == 0.0f)
  9.   break;
  10. temp = 1.0f / sqrtf(temp);
  11. n = temp * n;


 
Voilà. C'est tout pour ce soir.
 
LeGreg


Message édité par LeGreg le 29-02-2008 à 20:12:53

---------------
voxel terrain render engine | animation mentor
Reply

Marsh Posté le 12-08-2003 à 11:48:44    

sinon personne n'a repondu a ma question ... comment ils arrivent a faire leurs textures de ouf dans les demo 4k  :??:  (voir lien premiere page)

Reply

Marsh Posté le 12-08-2003 à 12:26:23    

par équations.
 
un bel exemple ici, tout est équation dans cette démo: http://www.theproduct.de/

Reply

Marsh Posté le 12-08-2003 à 18:27:33    

legreg a écrit :


 
Bah tu t'attaches vraiment a des details, le programme il aurait pu etre en C, c'est pas le point sur lequel je veux insister..
 
LeGreg


 
Je sais pas trop. Il me semble que la notion objet est très adaptée dans ce cas alors ça me gène de m'en passer. Plustot que de vois un paquet d'effets spécifiques j'aurais bien aimé voir une archi globale en C++ qu'il serait facile d'étendre pour ajouter de nouvelles formes et de nouveaux materiaux.
 
Tiens par exemple, un truc très bateau mais assez joli : le materiaux "damier" qui alterne 2 autres materiaux sous forme de cases en 3D.

Reply

Marsh Posté le 12-08-2003 à 18:44:08    

Kristoph a écrit :


Je sais pas trop. Il me semble que la notion objet est très adaptée dans ce cas alors ça me gène de m'en passer. Plustot que de vois un paquet d'effets spécifiques j'aurais bien aimé voir une archi globale en C++ qu'il serait facile d'étendre pour ajouter de nouvelles formes et de nouveaux materiaux.


 
Ben désolé ce n'est vraiment pas mon but de fournir un framework..
 
Le but initial comme je l'ai expliqué c'est d'introduire de façon régulière une nouvelle technique liée à la synthèse d'image. Avec un code si possible qui tient en quelque lignes pour chaque "nouvelle feature".
 
En fait j'avais commencé à programmer en C "pur" puis j'ai décidé d'utiliser des features comme la surcharge d'opérateurs pour avoir un code à l'écriture plus compact c'est tout.
 
LeGreg

Reply

Marsh Posté le 14-08-2003 à 10:34:24    

http://www.massal.net/article/raytrace/page3.html
 
Me demandez pas comment compiler le truc,
référez vous à la doc de votre compilateur.
 
Deux trois trucs qui ne vont pas marcher chez vous:
- min et max peuvent ne pas avoir le meme sens, en principe ce sont des fonctions template dans <algorithm>. Elles ont peut-etre été renommées _MIN et _MAX.. à vous de voir.
- si #pragma once n'est pas supporté par votre compilateur
 utilisez les #ifdef classiques.
- d'autres auxquels je n'ai pas pensé (AMD64 bits, char de moins de 8 bits, machine quantique etc..)
 
Le programme a été "désoptimisé" afin d'être je l'espère plus facilement compréhensible. J'ai ajouté des commentaires inutiles et modifié des noms de variables, ajouté des opérations intermédiaires supplémentaires etc.. Le résultat c'est qu'il est beaucoup plus lent.
 
C'est tout pour aujourd'hui.
 
LeGreg
ps: la cubemap est maintenant dans un zip séparé:
http://www.massal.net/article/raytrace/page3.html


Message édité par LeGreg le 29-02-2008 à 20:13:21

---------------
voxel terrain render engine | animation mentor
Reply

Marsh Posté le 14-08-2003 à 10:49:43    

J'ai pensé que les gens auraient envie de faire joujou
avec les cubemap donc j'ai inclu 6 fichiers TGA de 512x512.
 
Eheh c'était soit ça soit inclure le code de chargement des jpg dans le source qui fait exactement deux mégas :P
 
LeGreg
ps: ca y'est j'ai modifié les liens, il n'y a plus que 347 Ko (dont l'executable et le fichier scene).


Message édité par LeGreg le 14-08-2003 à 10:54:16
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed