dimanche 21 juin 2020

Dans l'espace

Je suis d'une humeur très spatiale, ces temps-ci. Voici deux rendus, tout d'abord d'une planète de type Saturne, avec des anneaux de gaz et de roche, et ensuite d'un astéroïde construit sur le même modèle que ceux de l'anneau, mais avec plus de détails.

L’astéroïde est fait à partir d'un cube très lourdement subdivisé, auquel on a ajouté un modificateur "displace" avec une texture Voronoi. Tout d'abord, on créé une texture avec une taille assez élevée, afin d'avoir un objet avec pas trop d'arêtes, qui soit bien découpé. Ensuite, l'on peut ajouter des détails. Je trouve que le 4ème niveau de poids donne un effet particulièrement intéressant. Et finalement, on rajoute une texture normale avec un bête bruit très détaillé.

Pour le fond étoilé, voici mes nœuds "world". Je créé une texture de bruit très fine, que je passe à travers une color ramp pour ne garder que des points éparpillés. Voici mes étoiles. Ensuite, j'aimerais bien leur donner des couleurs différentes, alors je créé une autre texture de bruit, fine aussi, mais moins, histoire que des étoiles proches aient des teintes proches, parce que pourquoi pas, et je la passe à travers une color ramp qui contient toutes les teintes qui m'intéressent : du rouge, du jaune, du blanc, du bleu. Ensuite, je combine ces deux textures à travers un nœud "hue saturation value", pour utiliser les couleurs de la deuxième texture, mais avec la luminosité contrôlée par la première texture. Et voilà, des étoiles gentiment colorées !

Notez également dans la deuxième image, un "glare" assez fort, contrôlé via le compositeur, et qui donne un aspect "spatial". Les lumières sont fortes (100 000 W pour la lumière mon soleil) pour avoir de beaux contrastes.

L'on notera à quel point on peut faire de fort jolies choses avec Eevee : c'est typiquement le genre de scènes où ce moteur temps réel donne de très belles choses. En revanche, quelques tests d'animation montrent ses limites: avec une forme compliquée comme celle de mon astéroïde, les ombres sont finalement assez imprécises, ce qui n'est pas gênant pour une image seule, mais provoque des sauts intempestifs quand elles sont animées.

Maintenant, reste à améliorer les vaisseaux, et à animer le tout un minimum, histoire de profiter des jeux de lumière.

jeudi 11 juin 2020

C++14 - std::string_view veut jouer avec std::set

Petite expérience du matin (chagrin ?) : Comment faire un find sur un std::set en utilisant string_view ?

Revenons en arrière. Tout d'abord, qu'est-ce que std::string_view, et pourquoi voudrait-on l'utiliser pour faire un find ?

Vous avez peut-être remarqué que l'objet std::string est loin d'être gratuit. Non seulement les copies peuvent être chères (modulo COW et SSO, m'enfin bref), mais en plus le reste du monde conspire contre nous entre les chaînes codées en dur qui sont des const char *, et de nombreuses bibliothèques ne veulent pas en entendre parler. Voyez par exemple cette recherche dans un std::set : en passant un paramètre const char *, il se cache en fait la construction d'un objet std::string, et donc une allocation.

#include <set>
#include <string>
#include <iostream>

#include <stdlib.h>

int number_of_allocs = 0;

void* operator new(std::size_t size) {
  ++number_of_allocs;
  void *p = malloc(size);
  if(!p) throw std::bad_alloc();
  return p;
}

void* operator new  [](std::size_t size) {
  ++number_of_allocs;
  void *p = malloc(size);
  if(!p) throw std::bad_alloc();
  return p;
}

void* operator new  [](std::size_t size, const std::nothrow_t&) throw() {
  ++number_of_allocs;
  return malloc(size);
}
void* operator new   (std::size_t size, const std::nothrow_t&) throw() {
  ++number_of_allocs;
  return malloc(size);
}


void operator delete(void* ptr) throw() { free(ptr); }
void operator delete (void* ptr, const std::nothrow_t&) throw() { free(ptr); }
void operator delete[](void* ptr) throw() { free(ptr); }
void operator delete[](void* ptr, const std::nothrow_t&) throw() { free(ptr); }

int main()
{
  std::cout << number_of_allocs << std::endl;
  
  std::set<std::string> str({"a", "b", "c"});
  
  std::cout << number_of_allocs << std::endl;
  
  // Longue chaine pour éviter le SSO
  str.find("abceuhaoeuhaotuhaoutnohusaocamhamknomhucra,huh");
  
  std::cout << number_of_allocs << std::endl;

  return 0;
}

La punition est immédiate, le programme affiche 0, 3 et 4, parce que la chaîne dans le find a été convertie en std::string et a provoqué une allocation.

Si l'on s'amuse plutôt à passer un std::string_view, dans ce cas, le programme ne compile pas, car il n'y a pas de création implicite de std::string d'après une std::string_view.

  str.find(std::string_view("abceuhaoeuhaotuhaoutnohusaocamhamknomhucra,huh"));

Entre en scène la nouvelle surcharge de la fonction find, datant du C++14, qui prend un paramètre template qui doit être comparable avec une clé "de manière transparente", sans conversion nécessaire. Ah ah! Pile ce qu'il nous faut. Il faut juste donner au std::set un comparateur transparent, comme std::less, et cela fonctionne.

[...]
int main()
{
  std::cout << number_of_allocs << std::endl;
  
  std::set<std::string, std::less<> > str({"a", "b", "c"});
  
  std::cout << number_of_allocs << std::endl;
  
  str.find(std::string_view("abceuhaoeuhaotuhaoutnohusaocamhamknomhucra,huh"));
  
  std::cout << number_of_allocs << std::endl;

  return 0;
}

Victoire, le programme affiche 0, 3, 3, et a donc économisé l'allocation ! Mieux encore, la version où l'on passe juste la chaîne en const char * a également économisé son allocation, le std::string_view n'étant là que pour empêcher la conversion implicite.

Première conclusion : ajoutez des std::less<> à vos std::set et std::map prenant des chaînes !

Deuxième conclusion : pensez à creer un comparateur manuel si vous faites ce genre de magie avec vos propres objets

Troisème conclusion : passez à C++20 pour faire la même chose avec std::unordered_set et std::unordered_map. Je vous en recauserai peut-être quand j'aurai mis à jour mon compilo.

Références:
https://en.cppreference.com/w/cpp/string/basic_string_view
https://en.cppreference.com/w/cpp/container/unordered_set/find
https://stackoverflow.com/questions/9927856/how-to-use-operator-new-to-count-number-of-times-of-dynamic-memory-allocation
https://stackoverflow.com/questions/35525777/use-of-string-view-for-map-lookup