Reputation: 11
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
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.
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
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