Aller au menu - Aller au contenu

Icône Les isosurfaces (2/2)

Mise à jour : 01/07/2010
Difficulté : Difficile Difficile Creative Commons BY-NC-SA
525 visites depuis 7 jours, dont 10 sur ce chapitre classé 218/786
Maintenant que vous avez compris le principe des isosurfaces, nous allons pouvoir nous attaquer à la partie la plus intéressante...
Ici, nous allons intégrer dans les équations de nos isosurfaces des pigments !!
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Pigment ?

Comme je vous l'ai dit plus haut, nous allons ici intégrer à nos isosurfaces des pigments...
Des pigments ? Mais... On sait déjà faire ça, non ? Donner une couleur à notre objet...

Bien sûr ! :D Vous savez donner un motif à un objet. Mais ici, on ne va pas utiliser les pigments comme ça...
On va utiliser ces pigments pour "sculpter" nos isosurfaces !
En fait, les pigments sont tout simplement des fonctions qui donnent à chaque point de l'espace une valeur, que l'on code en couleur avec un colormap (vous vous en souvenez encore ?...). Mais il est possible de ne s'intéresser qu'à la valeur, et d'utiliser le pigment comme n'importe quelle fonction ! o_O Incroyable, n'est-ce pas ?

Pour vous mettre l'eau à la bouche, voici ce que vous allez pouvoir faire :
Sphère sculptée
Mouais, je sais faire ça avec des normales moi ! Et c'est plus simple !

Oui et non...
Si vous regardez bien, ici, la sphère est réellement sculptée : son ombre et sa silhouette sont irrégulières, et le tout est quand même nettement plus réaliste qu'avec de simples normales, qui, elles, se contentent de simuler un relief.
Et encore, ici, j'ai simplement ajouté une petite fonction de type wrinkles à ma sphère... En combinant plusieurs autres fonctions, les possibilités sont presque infinies !

Une fonction pigment ?

Le seul problème est donc de transformer notre pigment en une fonction, que l'on pourra ensuite utiliser comme bon nous semble.
Pour cela, on va définir une fonction un peu spéciale : une fonction de pigment :
Code : C
1
2
3
4
5
#declare ma_fonction = function {
    pigment {
        // ...
    }
}

Il ne reste plus qu'à mettre le type de motif que l'on veut à l'intérieur, comme si c'était un pigment classique. Par exemple, avec wrinkles,
Code : C
1
2
3
4
5
#declare ma_fonction_wrinkles = function {
    pigment {
        wrinkles
    }
}


Mais il y a un os : cette fonction donne à chaque point de l'espace une couleur, et non une simple valeur.
Une simple solution consiste à convertir la couleur obtenue en une teinte de gris, ce qui est très facile :
Code : C
1
ma_fonction_wrinkles(x,y,z).gray

Ainsi, on obtiendra bien une valeur, que l'on pourra alors utiliser dans nos autres fonctions. :)
A noter qu'il existe également les fonctions .red, .green et .blue, qui permettent respectivement de ne prendre que la composante rouge, verte et bleue d'une couleur. Mais ici, dans ce cas, .gray est plus pratique ! ;)

Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#declare wr = function { 
    pigment {
        wrinkles
    }
}

#declare fn = function {
    x*x + y*y + z*z + 0.5*wr(x, y, z).gray - 1
}

isosurface {
    function { fn(x, y, z) }
    contained_by { sphere { 0, 1.2 } }
    threshold 0
    accuracy 0.000001
    max_gradient 6
    pigment { rgb 1 }
}


Et voilà un bloc de pierre ! ^^
Vous pouvez essayer d'augmenter l'effet, comme cela par exemple :

Code : C
1
2
3
#declare fn = function {
    x*x + y*y + z*z + wr(2*x, 2*y, 2*z) - 1
}

(Au passage, augmentez max_gradient jusqu'à 10, cela marchera peut-être mieux)
Ici, la sphère est de moins en moins visible, puisque le pigment prend plus d'importance.

Combinaison !

Jusque-là, nous n'avons utilisé que des motifs par défaut (un simple wrinkles). Mais on pourrait utiliser n'importe quelle combinaison de pigment ! Et en particulier des pigments de pigments... (si vous ne savez plus ce que c'est, relisez ce chapitre). Voici un simple exemple :

Code : C
1
2
3
4
5
6
7
8
9
#declare wr = function { 
    pigment {
        crackle
        pigment_map {
            [0.1 granite]
            [0.6 rgb 0]
        }
    }
}

Cette fonction va utiliser le pigment granite à certains endroits, et rien du tout (rgb 0 : tout noir !) à d'autres.
Voici alors le résultat :
Image utilisateur
Vous voyez bien qu'au centre, la sphère est plus "lisse" qu'ailleurs...
Maintenant, si on donne en plus à cet objet le pigment crackle, on pourra clairement voir où le pigment granite est utilisé :
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#declare wr = function { 
    pigment {
        crackle
        pigment_map {
          [0.1 granite]
          [0.6 rgb 0]
        }
    }
}

#declare fn = function {
    x*x + y*y + z*z + 0.2*wr(x, y, z).gray - 1
}

isosurface {
    function { fn(x, y, z) }
    contained_by { sphere { 0, 1.2 } }
    threshold 0
    accuracy 0.000001
    max_gradient 10
    pigment { crackle }
}

Image utilisateur
La grosse zone blanche au milieu correspond bien à la zone lisse de notre sphère ! :)

Cette application est assez moche :p , mais on peut imaginer de nombreuses combinaisons entre la fonction de l'isosurface et le pigment utilisé pour la colorer...

Application

Bon, vous devriez maintenant mourir d'envie d'appliquer ce nouveau pouvoir :D . C'est justement ce que nous allons faire !

Voici une scène assez simple, qui manque un peu de charme :
Image utilisateur
Voici la même, en utilisant cette fois des isosurfaces et des fonctions pigment :
Image utilisateur
Bon, elle n'est toujours pas exceptionnelle, mais elle a déjà un plus, vous ne trouvez pas ?
Voici quelques indices pour vous aider à faire la même :
  • L'équation d'un tore :
    Code : Autre
    1
    
    (x² + y² + z² + a² - b²)² - 4 * a² *(x² + z²) = 0

    Où a est le grand rayon et b le petit.
    Fallait pas l'inventer celle-là ! :p
  • L'équation d'un cylindre (vous devriez connaître quand même) :
    Code : Autre
    1
    
    x² + z² - r² = 0
  • Vous pourrez utiliser la fonction pow de POV-Ray :
    Code : C
    1
    pow(x, y) = x ^ y
    


Voici le code de la scène simple :
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
plane {
  y, 0
  pigment { color rgb <0.7,0.5,0.3> }
}


#declare pilier = union {
    difference {
        box {
            <-0.5,0,-0.5>
            <0.5,1,0.5>
        }
        torus {
            0.7, 0.4
            translate y
        }
    }
    cylinder {
        y, 3*y, 0.3
    }
    pigment { rgb 1 }
}

#declare arche = union {
    object { pilier translate -1.5*x }
    object { pilier translate  1.5*x }
    difference {
        torus { 1.5, 0.3 rotate x*90 }
        box { <-5,-5,-5>, <5,0,5> }
        translate 3*y
    }
    pigment { rgb 1 }
}   

object { arche }


Et voici le code partiel de la scène avec les isosurfaces :
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#declare isopilier = union {
    difference {
        object { isocube }
        object { isotorus1 translate y }
    }
    object { isocylindre }
    pigment { rgb 1 }
}

#declare isoarche = union {
    object { isopilier translate -1.5*x }
    object { isopilier translate  1.5*x }
    difference {
        object { isotorus2 rotate x*90 }
        box { <-5,-5,-5>, <5,0,5> }
        translate 3*y
    }
    pigment { rgb 1 }
}

object { isoarche pigment { rgb 1 } }
object { isoplane pigment { color rgb <0.7,0.5,0.3> } }


A vous de trouver les isocube, isocylindre, isotorus1 et isotorus2 ! ;)

Bon... Si vous ne trouvez vraiment pas, voici la correction :
Secret (cliquez pour afficher)
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#declare fGranite = function {
    pigment {
        granite
    }
}
#declare fWrinkles = function {
    pigment {
        wrinkles
    }
}

#declare isocube = isosurface {
    function { max(abs(x), abs(y), abs(z)) - 0.025*fGranite(x, y, z).gray - 0.5 }
    threshold 0
    contained_by { box { -1.2, 1.2 } }
    accuracy 0.000001
    max_gradient 10
    all_intersections
    translate 0.5*y
}

#declare isotorus1 = isosurface {
    #declare a = 0.7;
    #declare b = 0.4;
    function { pow(pow(x, 2) + pow(y, 2) + pow(z, 2) + pow(a, 2) - pow(b, 2), 2) - 0.025*fGranite(x, y, z).gray - 4 * pow(a, 2)*(pow(x, 2) + pow(z, 2)) }//- fGranite(x, y, z).gray - 0.4*0.4 }
    threshold 0
    max_gradient 10
    all_intersections
    accuracy 0.000001
    contained_by { sphere { 0, 1.3 } }
}

#declare isotorus2 = isosurface {
    #declare a = 1.5;
    #declare b = 0.3;
    function { pow(pow(x, 2) + pow(y, 2) + pow(z, 2) + pow(a, 2) - pow(b, 2), 2) + pow(0.05*fGranite(x, y, z).gray, 0.5) - 4 * pow(a, 2)*(pow(x, 2) + pow(z, 2)) }//- fGranite(x, y, z).gray - 0.4*0.4 }
    threshold 0
    max_gradient 20
    all_intersections
    accuracy 0.000001
    contained_by { sphere { 0, 2 } }
}

#declare isocylindre = isosurface {
    function { pow(x,2) + pow(z, 2) + 0.025 * fGranite(x, y, z).gray - pow(0.3, 2) }
    threshold 0
    max_gradient 10
    all_intersections
    accuracy 0.000001
    contained_by { box { <-0.5, 0, -0.5>, <0.5, 3, 0.5> } }
}

#declare isopilier = union {
    difference {
        object { isocube }
        object { isotorus1 translate y }
    }
    object { isocylindre }
    pigment { rgb 1 }
}

#declare isoarche = union {
    object { isopilier translate -1.5*x }
    object { isopilier translate  1.5*x }
    difference {
        object { isotorus2 rotate x*90 }
        box { <-5,-5,-5>, <5,0,5> }
        translate 3*y
    }
    pigment { rgb 1 }
}

#declare isoplane = isosurface {
    function { y - 0.3 * (0.2 * fGranite(3*x, 3*y, 3*z).gray + fWrinkles(x*2, y*2, z*2).gray) }
    threshold 0
    contained_by { box { <-5, -0.5, -5>, <5, 0.5, 5> } }
    max_gradient 10
    accuracy 0.000001
}

object { isoarche pigment { rgb 1 } }
object { isoplane pigment { color rgb <0.7,0.5,0.3> } }


Vous devriez déjà sentir un *léger* ralentissement... ^^
Les bidouilles avec les pigments n'étaient pas faciles à trouver, je l'admets :-° ...

Si vous le souhaitez, vous pouvez encore améliorer cette scène...
Par exemple, le sol est ici très limité : on pourrait choisir de le faire plus grand, ou changer l'angle de vue pour ne plus voir les bords. On pourrait également colorer l'arche, ou la complexifier un peu... Vous êtes libre d'expérimenter ! :)

Q.C.M.

Pourquoi utilise-t-on .gray après une fonction de pigment ?

Statistiques de réponses au QCM

Toujours sous le choc ? :p C'est normal !
Je vous l'avais dit, ces isosurfaces sont réellement impressionnantes... Vous pouvez faire quasiment n'importe quoi avec, du moment que vous connaissez l'équation (et que vous avez le temps :-° )

D'ailleurs, je vous recommande ce site qui contient de nombreuses équations dont vous pourrez vous servir... Indispensable quand on souhaite faire des formes un peu compliquées !

En conclusion, on peut dire que les isosurfaces forment une partie très importante de POV-Ray...
L'intérêt est que l'on peut grâce à elles créer des surfaces assez complexes ; cependant, il faut être prêt à subir une augmentation du temps de calcul...
Il faut donc savoir quand utiliser des isosurfaces au lieu de simples normales : lors de vues de loin, une normale devrait suffire pour donner bonne impression. De près, en revanche, une isosurface qui utilise une fonction de pigment fractal (comme wrinkles, granit, ... ) sera le meilleur moyen d'avoir un maximum de détails.
Enfin, si vous connaissez l'équation d'un objet, il est parfois plus simple de faire une isosurface que de tenter de re-créer l'objet avec les solides de base (sphère, cube, cylindre, ...)
Chapitre précédent Sommaire Chapitre suivant

Partager

Il n'y a pas encore de commentaire pour ce tuto.