2016-02-10 4 views
3

Я смотрел на и пытаюсь понять следующий фрагмент кодаGLSL куб подписанное расстояние поле реализация описание?

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)); 
} 

Я понимаю, что length(d) обрабатывает случай, SDF, где точка едет в «углу» (то есть. Все компоненты d являются положительный) и что max(d.x, d.y, d.z) дает нам правильное расстояние во всех остальных случаях. Я не понимаю, как эти два сочетаются здесь без использования инструкции if для проверки признаков компонентов d.

Когда все d компонент положительны, то выражение возвращаемого может быть сведен к length(d) из-за способом min/max будет оценивать - и когда все d компонент являются отрицательными, мы получаем max(d.x, d.y, d.z). Но как я должен понимать промежуточные случаи? Те, в которых компоненты d имеют смешанные знаки?

Я пытался показать это безрезультатно. Я был бы очень признателен, если бы кто-то мог объяснить это мне геометрически/математически. Благодарю.

ответ

0

я понял это некоторое время назад, и писал об этом подробно в блоге здесь: http://fabricecastel.github.io/blog/2016-02-11/main.html

Вот выдержка (полный пост для полного объяснения):

Рассмотрим четыре точки: A, B, C и D. Давайте грубо уменьшим функцию расстояния, чтобы попытаться избавиться от функций min/max, чтобы понять их эффект (поскольку это то, что озадачивает эту функцию). Обозначения ниже немного небрежны, я использую квадратные скобки для обозначения двумерных векторов.

// 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] 

Хорошо, ничего особенного. Когда A находится внутри квадрата, мы фактически получаем нашу первую функцию расстояния, основанную на плоскостях/линиях, и когда B находится в области, где наша первая функция расстояния неточна, она обнуляется и мы получаем вторую функцию расстояния (длину). Трюк лежит в двух других случаях C и D. Давайте их рассмотрим.

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] 

Если вы посмотрите на график выше, вы увидите C 'и D'. Эти точки имеют координаты [0,1] и [1,0] соответственно. Этот метод использует тот факт, что оба поля расстояния пересекаются по осям - что D и D 'лежат на одном и том же расстоянии от квадрата.

Если мы обнулим всю отрицательную составляющую вектора и возьмем его длину, мы получим правильное расстояние между точкой и квадратом (только для точек вне квадрата). Это то, что делает max (d, 0.0); макс. Пока вектор имеет хотя бы одну положительную составляющую, min (max (d.x, d.y), 0.0) разрешит 0, оставив нам только вторую часть уравнения. Если точка находится внутри квадрата, мы хотим вернуть первую часть уравнения (поскольку она представляет нашу первую функцию расстояния). Если все компоненты вектора отрицательны, легко увидеть, что наше условие будет выполнено.

Это понимание должно транслироваться обратно в 3D без проблем, как только вы обернете вокруг себя голову. Возможно, вам может понадобиться несколько графиков, чтобы действительно «получить» это - я знаю, что сделал, и призываю вас сделать это, если вы недовольны моим объяснением.

Работа этой реализации в собственный код, мы получаем следующее:

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)); 
} 

И там у вас есть.