NotADuck
NotADuck

Reputation: 11

Distance from a line -- Shadertoy

So I'm new to shadertoy, and I've been playing around with some stuff, but one thing I can't figure out is how to calculate the distance of a point to a line. I can easily do it myself with a pencil and paper, but somehow I keep messing something up when I actually try to apply it to shadertoy. Here's what I have:

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy;

    uv.x -= 0.5; //Puts the origin at the center of the screen

    float r; //Red value
    float g; //Green value
    float b; //Blue value

    float lm = 1.1; //slope
    float lb = 0.5; //intercept

    //Slope/intercept declarations manipulate line
    //second line
    float lmp = 0.0-(1.0/lm); //calculates the slope of the perpendicular line
    float lbp = lb + uv.y + lmp*(uv.x); //and the intercept

    //Intersection
    float ix = (lbp-lb)/(lm-lmp); //Intersection Y
    float iy = lm*(ix)+lb; //Intersection X based off of the first equation
    //distance
    float dist = sqrt(pow((uv.x - ix),2.0)+pow((uv.y - iy),2.0));

    if (dist < 0.05){

        r = 1.0;
        g = 1.0;
        b = 1.0;
    }

    fragColor = vec4(r,g,b,1.0); //supposed to draw a line
}

Now here's something from the project "Neontoy" by Flyguy that not only works, but it's a lot shorter than mine.

float dfLine(vec2 start, vec2 end, vec2 uv)
{
    start *= scale;
    end *= scale;

    vec2 line = end - start;
    float frac = dot(uv - start,line) / dot(line,line);
    return distance(start + line * clamp(frac, 0.0, 1.0), uv);
}

Upvotes: 1

Views: 2211

Answers (2)

Rabbid76
Rabbid76

Reputation: 210998

If you have a line, given by a point (O) and an direction (D), then the nearest point on the line, to a point p can be calculated as follows

X = O + D * dot(P-O, D);

The dot product of 2 vectors is equal the cosine of the angle between the 2 vectors multiplied by the magnitude (length) of both vectors.

enter image description here

dot( A, B ) == | A | * | B | * cos( alpha ) 

The dot product of V and D is equal the cosine of the angle between the line (O, D) and the vector V = P - O, multiplied by the amount (length) of V, because D is a unit vector (the lenght of D is 1.0),

In your case the line is given by a Linear equation in the form:

f(x) = lb + lmp * x;

A point on the line is (0.0, lb) and the direction is (1.0, lmp).

Applying this to your code results in the following fragment shader:

float dfLine(vec2 O, vec2 dir, vec2 P)
{
    vec2 D = normalize(dir);
    vec2 X = O + D * dot(P-O, D);

    return distance(P, X);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy;
    uv.x -= 0.5; //Puts the origin at the center of the screen

    float lm = 1.1; //slope
    float lb = 0.5; //intercept

    float dist = dfLine(vec2(0.0, lb), vec2(1.0, lmp), uv);

    float onLine = step(dist, 0.05);   // 1.0 if on line, else 0.0
    vec3  color  = onLine * vec3(1.0); 

    fragColor = vec4(color, 1.0);
}

Upvotes: 6

Twenkid
Twenkid

Reputation: 955

@Rabbid

Thanks, but the code is missing the P in the call to dfLine(), so it could be https://www.shadertoy.com/view/3tyyDR :

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
 vec2 uv = fragCoord/iResolution.xy;
 uv.x -= 0.5; //Puts the origin at the center of the screen

 float lm = 2.1; //slope
 float lb = 0.5; //intercept    
 vec2 P = vec2(uv.x, uv.y); //Current scanned point
 float dist = dfLine(vec2(0.0, lb), vec2(1.0, lm),  P);    
 //vec3  color  = dist * vec3(1.0);  //gradient
 float onLine = step(dist, 0.01);   // 
 vec3  color  = onLine * vec3(1.0); 
 fragColor = vec4(color, 1.0);    
}

Or like that with a simple animation and a smooth line:

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
 vec2 uv = fragCoord/iResolution.xy;
 uv.x -= 0.5; //Puts the origin at the center of the screen

 float lm = sin(iTime); // animated slope, 1.1
 float lb = mix(0.5, 1.0, sin(iTime)); //0.5; //intercept   0.5-1.0 to keep it in the view port 
 vec2 P = vec2(uv.x, uv.y);
 //float dist = dfLine(vec2(0.0, lb), vec2(1.0, lm),  vec2(uv.x,uv.y));

 float dist = dfLine(vec2(0.0, lb), vec2(1.0, lm),  P);
 float onLine = 1.0 - smoothstep(0.0, 0.02, dist);

 vec3  color  = onLine * vec3(1.0);
 fragColor = vec4(color, 1.0);   
}

Upvotes: 1

Related Questions