2016-02-10 1 views
3

J'ai regardé et essayer de comprendre le bit de code suivantExplication de la mise en œuvre du champ de distance signé GLSL cube?

float sdBox(vec3 p, vec3 b) 
{ 
    vec3 d = abs(p) - b; 
    return min(max(d.x,max(d.y,d.z)),0.0) + 
     length(max(d,0.0)); 
} 

Je comprends que length(d) traite le cas de SDF où le point est hors du « coin » (c.-à. Tous les composants de d sont positif) et que max(d.x, d.y, d.z) nous donne la distance appropriée dans tous les autres cas. Ce que je ne comprends pas, c'est comment ces deux sont combinés ici sans l'utilisation d'une instruction if pour vérifier les signes des composants d.

Lorsque tous les composants d sont positifs, l'expression de retour peut être réduit à length(d) en raison de la façon min/max évaluera - et lorsque tous les composants d sont négatifs, nous obtenons max(d.x, d.y, d.z). Mais comment suis-je censé comprendre les cas intermédiaires? Ceux où les composants de d ont des signes mixtes? J'ai essayé de tracer un graphique en vain. J'apprécierais vraiment que quelqu'un m'explique cela en termes géométriques/mathématiques. Merci.

Répondre

0

I figured it out il y a un certain temps et écrit à ce sujet largement dans un billet de blog ici: http://fabricecastel.github.io/blog/2016-02-11/main.html

Voici un extrait (voir le post complet pour une explication complète):

Considérons les quatre points, A, B, C et D. Réduisons grossièrement la fonction de distance pour essayer de se débarrasser des fonctions min/max afin de comprendre leur effet (puisque c'est ce qui dérange dans cette fonction). La notation ci-dessous est un peu bâclée, j'utilise des crochets pour désigner les vecteurs 2D.

// 2D version of the function 
d(p) = min(max(p.x, p.y), 0) 
     + length(max(p, 0)) 

--- 

d(A) = min(max(-1, -1), 0) 
     + length(max([-1, -1], 0)) 

d(A) = -1 + length[0, 0] 

--- 

d(B) = min(max(1, 1), 0) 
     + length(max([1, 1], 0)) 

d(B) = 0 + length[1, 1] 

Ok, jusqu'à présent rien de spécial. Lorsque A est à l'intérieur du carré, nous obtenons essentiellement notre première fonction de distance basée sur des plans/lignes et lorsque B est dans la zone où notre première fonction de distance est inexacte, elle est remise à zéro et nous obtenons la deuxième fonction de distance. L'astuce réside dans les deux autres cas C et D. Mettons-nous au point.

d(C) = min(max(-1, 1), 0) 
     + length(max([-1, 1], 0)) 

d(C) = 0 + length[0, 1] 

--- 

d(D) = min(max(1, -1), 0) 
     + length(max([-1, 1], 0)) 

d(D) = 0 + length[1, 0] 

Si vous regardez de nouveau le graphique ci-dessus, vous noterez C 'et D'. Ces points ont des coordonnées [0,1] et [1,0], respectivement. Cette méthode utilise le fait que les deux champs de distance se croisent sur les axes - que D et D 'se trouvent à la même distance du carré.

Si nous mettons à zéro toute la composante négative d'un vecteur et prenons sa longueur, nous obtiendrons la distance appropriée entre le point et le carré (pour les points situés en dehors du carré uniquement). C'est ce que fait max (d, 0.0); une opération max par composant. Tant que le vecteur a au moins une composante positive, min (max (d.x, d.y), 0.0) se résoudra à 0, nous laissant seulement avec la deuxième partie de l'équation. Dans le cas où le point est à l'intérieur du carré, nous voulons retourner la première partie de l'équation (puisqu'elle représente notre première fonction de distance). Si tous les composants du vecteur sont négatifs, il est facile de voir que notre condition sera remplie.

Cette compréhension devrait être transférée en 3D de façon transparente une fois que vous l'aurez enveloppée. Vous pouvez ou ne devez pas dessiner quelques graphiques à la main pour vraiment «l'obtenir» - je sais que je l'ai fait et je vous encourage à le faire si vous n'êtes pas satisfait de mon explication.

Travailler cette mise en œuvre dans notre propre code, nous obtenons ceci:

float distanceToNearestSurface(vec3 p){ 
    float s = 1.0; 
    vec3 d = abs(p) - vec3(s); 
    return min(max(d.x, max(d.y,d.z)), 0.0) 
     + length(max(d,0.0)); 
} 

Et là vous l'avez.