SwiftRay

CéroceCéroce Membre, Modérateur
Ciao ragazzi !

J'ai pris un mois sans contrat pour travailler sur des études perso.
Voici l'une de ces études:

https://github.com/Ceroce/SwiftRay

Il s'agit d'un Ray-tracer (logiciel pour produire des images de synthèse).
L'algorithme utilisé est le Path-tracing. ça reproduit la dispersion des photons, mais c'est très lent: presque deux heures pour l'image en illustration.

Réponses

  • Excellent ! 


     


    Ton expérience est intéressante en tout point :


     


    - As-tu eu des difficultés à  convertir le code de Shirley du C++ vers Swift ?


    - Swift 3 ?


    - As-tu une idée de l'efficacité du code généré relativement à  la version C++ ? C'est vraisemblablement un excellent bench de l'efficacité de Swift !

  • klogklog Membre
    novembre 2016 modifié #3

    Pour info, ça fonctionne parfaitement sur mon MacPro...


    J'ai considérablement réduit la taille du bitmap généré du nombre de samples par pixel et de la profondeur des rebonds pour aller plus vite :



    let Width = 200 // Width of the generated image
    let Height = 133 // Height of the generated image
    let Samples = 10 // Number of rays for each pixel
    let DepthMax = 5 // Maximum number of scattered rays


  • CéroceCéroce Membre, Modérateur
    novembre 2016 modifié #4

    - As-tu eu des difficultés à  convertir le code de Shirley du C++ vers Swift ?

    Non. D'une part, parce qu'il a cherché à  rester simple, d'autre part parce que Swift est un langage plus expressif. Parfois, j'ai quand même eu du mal à  suivre son code; la séparation des différents cas n'est pas toujours claire.

    En ce qui concerne Swift, les tuples et les optionnels m'ont simplifié la vie. Par exemple:
     
    func hit(ray: Ray, distMin: Float, distMax: Float) -> HitIntersection?
    Cette méthode détermine l'intersection entre la sphère et un rayon. La méthode renvoie donc une HitIntersection? (puisqu'il n'y a pas forcément d'intersection).

    À noter que j'ai utilisé principalement des structs, ce qui simplifiera énormément le passage en multithread.
     

    - Swift 3 ?

    Oui. Je pense que ça m'a aidé dans Bitmap.swift pour l'accès bas-niveau à  la mémoire.
     

    - As-tu une idée de l'efficacité du code généré relativement à  la version C++ ? C'est vraisemblablement un excellent bench de l'efficacité de Swift !

    Non, je n'ai pas essayé le code C++. Mais je suis d'accord, ça ferait un bon test, parce que j'ai assez suivi l'implémentation C++.
  • CéroceCéroce Membre, Modérateur
    novembre 2016 modifié #5

    J'ai considérablement réduit la taille du bitmap généré du nombre de samples par pixel et de la profondeur des rebonds pour aller plus vite.

    Quel chrono a fait le Mac Pro ?

    D'après mes essais, DepthMax joue assez peu (10% ?), parce que c'est un maximum; le rayon a pas mal de chances de toucher le ciel avant d'arriver au maximum.
    Les autres paramètres augmentent tous la complexité de façon linéaire. Avec 100 échantillons, c'est 10 x plus lent qu'avec 10.


  • Quel chrono a fait le Mac Pro ?




     


    Avec les paramètres réduits que j'ai indiqués au-dessus : 31,76 secs (en mode release & debug désactivé).


    Au premier regard, je pense que le code est facilement threadable.


     


     




    D'après mes essais, DepthMax joue assez peu (10% ?), parce que c'est un maximum; le rayon a pas mal de chances de toucher le ciel avant d'arriver au maximum.




     


    Oui c'est un paramètre dont l'incidence dépend beaucoup de la configuration de la scène (type de matériaux, occlusion ...) 


     


     




    Les autres paramètres augmentent tous la complexité de façon linéaire. Avec 100 échantillons, c'est 10 x plus lent qu'avec 10.




     


     


    C'est logique...

  • CéroceCéroce Membre, Modérateur
    novembre 2016 modifié #7

    Au premier regard, je pense que le code est facilement threadable.

    Oui. L'auteur propose de créer un thread par échantillonnage.
    Je me demande s'il ne serait pas plus performant de découper plutôt par lignes, afin de mieux exploiter la prédiction de cache.

    Mais son approche a un gros avantage: on peut améliorer l'image finale au fur et à  mesure: générer une passe, produire le png, générer la seconde passe, moyenner, produire le png, etc. L'utilisateur peut donc arrêter quand ça lui convient.

    Je ferai ça quand j'aurai le temps... j'ai d'autres chantiers à  présent !
  • klogklog Membre
    novembre 2016 modifié #8


    Oui. L'auteur propose de créer un thread par échantillonnage.




     


    Je ne sais pas s'il y a une hiérarchie pour accélérer les calculs d'intersections (je suppose que non), mais dans le cas de scènes complexes avec une structure hiérarchique associée et des matériaux sophistiqués, un thread par échantillon c'est en général assez logique.


     


     




    Je me demande s'il ne serait pas plus performant de découper plutôt par lignes, afin de mieux exploiter la prédiction de cache.




     


    L'idéal, c'est de parcourir l'image selon une courbe de Hilbert qui maximise les voisinages cohérent.


  • CéroceCéroce Membre, Modérateur
    décembre 2016 modifié #9

    J'ai passé mon code en multi-thread.


     


    Dans le principe, le raytracer fonctionne ainsi: 


    - l'image est rendue avec un rayon par pixel, décalé aléatoirement dans un rayon de 1 pixel


    - le rendu est effectué plusieurs fois (paramètre Samples)


    - les images sont moyennées, puis on applique un tone mapping (conversion de l'image RGB Float 32 en RGB 8), et on enregistre.


    De fait, l'image s'améliore progressivement, le moyennage la rendant de moins en moins bruitée.


     


    Pour multithreader, j'ai fait ceci:


    - un thread par Sample. Dans la version sur github, je laisse GCD décider du nombre de threads. Il en crée 12.


    - l'accumulation des images se fait dorénavant par une moyenne pondérée (dans une queue série)


    - on enregistre à  chaque sample, ce qui est moins performant que de ne le faire qu'à  la fin, mais permet de voir l'évolution du rendu, et de l'arrêter à  tout moment.


     


    Et bizarrement, le code était devenu PLUS LENT qu'avec un seul ! C'est idiot, mon ordi ayant 4 coe“urs, je m'attendais à  un x3 voire x4 !


    Ne voyant pas de bêtise évidente de ma part (les threads ne partagent pas leurs ressources, seul l'accumulateur est partagé), j'ai voulu profiler avec Instruments, et là  l'application se ferme au bout de 3 s.


     


    J'ai cru qu'elle avait planté, mais non, elle avait fini le rendu !!! Quand on profile, Xcode compile en mode Release. Alors, je suis au courant que ça tourne plus vite en Release qu'en Debug, mais en ObjC, c'est genre 30% plus rapide... pas 400% comme en Swift !


     


    Du coup, existe-t-il un moyen rapide de compiler en Release avec Xcode ? (Sans devoir archiver)


  • On peut voir quelques images plutôt que des lignes de codes ?

  • CéroceCéroce Membre, Modérateur

    Du coup, ça fonctionne, et l'image d'illustration est générée en 8'43" en 400 x 266 / 100 échantillons sur mon Mac Book Pro.


  • CéroceCéroce Membre, Modérateur
    décembre 2016 modifié #12


    On peut voir quelques images plutôt que des lignes de codes ?




    Va voir sur la page github.


    Pour l'instant, seules les sphères sont gérées, alors toutes les images ressemblent un peu à  ça.


  • DrakenDraken Membre
    décembre 2016 modifié #13

    T'aimais jouer aux billes, plus jeune ?


    Ce serait plus sympa avec des textures variées (métaux, minerais, bois, pierres, etc ..)


    Oui je sais, c'est une démo technique pas une épreuve artistique ..

  • Bonjour Céroce, 


     


    Tout d'abord je tiens à  vous féliciter pour votre travail. Seulement j'ai une question est je vous prie d'excuser mon ignorance. En fait je me demande à  quoi peut servir ce genre de projet, concrètement qu'elles en sont les applications . Est-ce une sorte de moteur 3D ?

  • MalaMala Membre, Modérateur


    Du coup, existe-t-il un moyen rapide de compiler en Release avec Xcode ? (Sans devoir archiver)




    Moi j'ai pris l'habitude de me créer un Sheme en mode Release pour les projets qui nécessitent des benchs réguliers.

  • CéroceCéroce Membre, Modérateur
    décembre 2016 modifié #16

    Tout d'abord je tiens à  vous féliciter pour votre travail.



    Merci, ça me fait plaisir!

     



    Seulement j'ai une question et je vous prie d'excuser mon ignorance. En fait je me demande à  quoi peut servir ce genre de projet, concrètement qu'elles en sont les applications . Est-ce une sorte de moteur 3D ?



    En gros, on distingue deux choses:

    - la 3D temps réel, celle que l'on trouve principalement dans les jeux vidéos, mais en fait tout ce qui est interactif, par exemple les logiciels de CAO. Quand on parle de moteur 3D, on est là -dedans.

    - la 3D précalculée ou " offline ", qui est utilisée dans les films.


    En gros, la différence est que dans le premier cas, on dispose d'un temps limité (1/30 ou 1/60 de seconde) pour rendre une image, et dans le second cas, on dispose de plusieurs minutes, voire plusieurs heures.


    Les modèles physiques (équation de la lumière) sont aujourd'hui bien connus, le problème est que si on voulait les appliquer, il faudrait calculer tous les photons qui partent des sources lumineuses et leurs dispersions quand ils touchent des objets, jusqu'à  ce qu'ils n'aient plus d'énergie et ça prendrait un temps dingue. Donc, on ne le fait pas ! Tout l'art consiste à  trouver des compromis entre la qualité d'image et le temps de calcul. Vous comprendrez donc que les compromis choisis pour la 3D temps réel sont différents de ceux choisis pour la 3D précalculée.


    SwiftRay fait évidemment de la 3D précalculée. Il utilise un algorithme, le Path tracing, qui est lent, mais qui reproduit assez fidèlement les modèles physiques. Dans l'industrie, les Path tracers ne sont pas utilisés pour la production de films, mais servent à  créer les images de référence, dont on va essayer de se rapprocher avec les autres algorithmes.



    Pour finir, le matériel s'améliore, les algorithmes évoluent, on trouve de nouvelles astuces, si bien que la 3D temps-réel n'a jamais que quelques années de retard sur le nec plus ultra de la 3D précalculée. Cet article dissèque le rendu de Doom 2016, j'ai trouvé ça passionnant.


  • DrakenDraken Membre
    décembre 2016 modifié #17


     


    En fait je me demande à  quoi peut servir ce genre de projet, concrètement qu'elles en sont les applications . Est-ce une sorte de moteur 3D ?




    C'est effectivement un moteur 3D, mais pas comme ceux des jeux vidéo. Le ray-tracing est la méthode la plus efficace pour dessiner des scènes en 3D, mais elle est lente, vraiment très lente. En gros cela consiste à  calculer la trajectoire et les rebonds de CHAQUE photon illuminant la pièce. C'est pourquoi les amateurs de ray-tracing aiment beaucoup les images avec des objets se réfléchissant les uns dans les autres (miroirs, sphères en verres, verres d'eau, carafes, etc..).


     


    On appelle aussi ça du "lancer de rayon", puisqu'on "lance" des rayons lumineux dans l'espace d'une scène 3D.


     


    Chaque fois que l'on a besoin d'images photo-réalistes (quasiment réelles) sans contrainte de temps réel on utilise du ray-tracing pour dessiner des objets 3D : pubs tv, les effets spéciaux du cinéma, etc .. Même sur un ordinateur puissant, on a parfois besoin de PLUSIEURS HEURES pour avoir un beau rendu.


     


    EDIT : Grilled by Ceroce ..


  • CéroceCéroce Membre, Modérateur


    Moi j'ai pris l'habitude de me créer un Sheme en mode Release pour les projets qui nécessitent des benchs réguliers.




    Ah oui, c'est là  que ça se trouve ! J'ai mes vieilles habitudes, à  les chercher dans les menus. Merci bien.

  • Je n'aurai qu'un geste: 


    Ceci dit je me rappelle de logiciesl de RayTracing des années 92 .. 96 et j'avais trouvé à  l'époque que si les images étaient belles, les temps de calcul étaient prohibitifs !


  • Bonsoir à  tous, 


     


    Merci pour vos réponse , cela à  vraiment été constructif , et grâce a vous je dormirai moins bête ce soir.


  • CéroceCéroce Membre, Modérateur
    février 2017 modifié #21

    Je viens de publier une nouvelle version, qui implémente un algorithme de "Bounding Volume Hierarchy". Il s'agit d'un algo qui classe les primitives (sphères) dans un arbre binaire selon leur position dans l'espace. Ainsi, on peut rapidement savoir qu'un rayon ne touche pas d'objet ou déterminer quel objet est touché.


     


    J'ai eu un vrai problème de performances, que j'ai réglé en utilisant une enum. Vous pouvez trouver les détails ici: 


    Speeding up Swift code using an enum


     


    Au final, il y a une grosse amélioration de la vitesse. Par exemple, x10 sur la scène témoin.


    Je passe de 8'43" en 400 x 266 / 100 échantillons sur mon Mac Book Pro à  seulement 48".


  • C'est impressionnant de voir ce que peut donner une bonne optimisation, dans une opération de rendu graphique.

Connectez-vous ou Inscrivez-vous pour répondre.