Skip to content

2D SDF(2): Wave and Segment

Bonsai edited this page Apr 4, 2024 · 1 revision

Shader Ripple

Firstly, Add a beautiful ripple to the circle for displaying $distance$

$f(x) = e^x$ exp(x)can make value approach 1.0

$f(x) = sin(n \times x)$ the sin function can generate regular waves. In GLSLShading, n typically represents the frequency or wavenumber. the frequency controls the number of wave peaks wihtin a cycle. effectively make the wavelength shorter and the wave peaks more compact.

$f(x) = sin(n \times x - m * uTime)$ m*uTime represents a dynamic phase shift over time. enabling the creation of animated wave patterns.

Segment

Assuming there's a line segement(A, B) in space, the shortest distance from all points in space to this line segment can be categorized into three scenarios:

  1. For a point $P$ lying between A and B, the distance is hte pependicular distance from the point to he line AB.
  2. For a point $P$ outside of AB and closter to the point B, the shortest distance is the distance from $P$ to the point $B$
  3. For a point $P$ outside of AB and closer to the point A, the shortest distance is the distance from $P$ to hte point A

An intuitive method to determine this is to calculate the lengths of the three vectors $\overrightarrow{PB}$ $\overrightarrow{PA}$ $\overrightarrow{PC}$ . And the shorest of these lengths can be taken as the solution .

  1. for the point $P1$ , The length of the vetor $\overrightarrow{PC}$ can be using the angle $\theta$ between $\overrightarrow{PA}$ $\overrightarrow{PB}$ ,and then calculating $sin(\theta) \times ||\overrightarrow{PA}||$. $\theta$ can be determined via the dot product of the vectors.Note the parameter $hh$ in the figure,whic represents the radito of the length of the projection of $P$ onto $AB$ to the length of $AB$ . Only if this value $hh$ lies within the interval $(0, 1)$
  2. For a point $P2$ $P3$ outside AB, Directly take the minimum of the two distance
float sd_segment(vec2 pct, vec2 pa, vec2 pb) {
    vec2 v1 = pct - pa;
    vec2 v2 = pb - pa;
    vec2 v3 = pct - pb;
    
    
    float hh = dot(v1, v2) / length(v2) / length(v2);
    
    if (0. < hh && hh < 1.0) {
        float theta = acos(dot(v1 ,v2) /length(v1)/length(v2));
        float d1 = sin(theta) * length(v1);
        return d1;
    } else {
        return min(length(v1), length(v3)) ;
    }
}

Here, we can optimize the code. The length of the vetor $\overrightarrow{PC}$ is actually $\overrightarrow{PA} - \overrightarrow{AC}$ . The $\overrightarrow{AC}$ can be determined by $hh$, We already know the value of $hh$. Moreover, when $hh&lt;0$,The distance is $Length(\overrightarrow{PA})$,and when $hh&gt;1$, The distance is $Length(\overrightarrow{PB})$. Therefore, the code can be optimized as follows

float sd_segment2(vec2 pct, vec2 pa, vec2 pb) {
    vec2 v1 = pct - pa;
    vec2 v2 = pb - pa;
    vec2 v3 = pct - pb;
    
    
    float hh = dot(v1, v2) / length(v2) / length(v2);

    if (hh < 0.0) {      
      return length(v1 - 0. * v2);
    } else if (0. < hh && hh < 1.0) {
        return length(v1 - hh * v2);
    } else {
        // v3 = v1 - v2 = pct -pb;
        return length(v1 - v2);
    }
}

Through the optimization mentioned above, one can notice the similarity between the three conditionals. By introducing the clamp function, we eliminate the need for if conditions, thus optimizing performance. Furthermore, by using dot(v2, v2). We reduce the calculation of length by on instance. Ultimatly we arrive at hte final result

float sd_segment3(vec2 pct, vec2 pa, vec2 pb) {
    vec2 v1 = pct - pa;
    vec2 v2 = pb - pa;
    float h = clamp(dot(v1,v2)/dot(v2,v2), 0.0, 1.0);
    return length(v1-h*v2); 
}