如何在Shader中绘制类似虚线的折线
实例代码可以参考shadertoy中的[例子](line with animation (shadertoy.com))
核心思想
绘制折线
在shader中无法通过顺序绘制的方式绘制折线,在参考现有的shadertoy中的例子后,决定采用距离场的方式绘制折线,即计算屏幕上每个像素到给定折线的距离,通过给定线宽来选取折线的范围。
绘制虚线
通过距离场明确线的范围后,可以将屏幕中每个像素投影到折线上,通过投影到折线上的距离来对线段进行分段,以实现间隔绘制效果。
效果图如下:
详细代码
sdLine_t
方法用于绘制线段,方法中通过向量点乘的方式计算,给定点到线段的最短距离,其中c
为计算得到的p
距离向量ab
的最短距离。t_lxs
为p
在向量ab
上的投影的距离。通过对c
进行截取,仅保留给定线宽的计算结果,线宽范围外的赋值为0,即可得到折线的绘制范围。
通过对t_lxs
取模,可以实现分段绘制,距离加时间可以实现流动效果。
最后cDistance_t
方法实现多段线段的绘制,方法中使用max
来混和多段线。
const float ELIPSE=.005;
const float LINEWIDTH=0.01;
float sdLine_t(in vec2 p,in vec2 a, in vec2 b){
vec2 pa=p-a,ba=b-a;
float t=dot(pa,ba)/dot(ba,ba);
//p在ba上投影距离
float t_lxs=dot(pa,ba)/length(ba);
float c=length(pa-ba*clamp(t,0.0,1.0));
c=1.-smoothstep(LINEWIDTH,LINEWIDTH+ELIPSE,c);
return c*mod(t_lxs-iTime/10.,.1);
}
const vec2 p0 = vec2( 0.6, 0.1);
const vec2 p1 = vec2( 0.4, 0.3);
const vec2 p2 = vec2(-0.2, 0.5);
const vec2 p3 = vec2(-0.6, 0.4);
const vec2 p4 = vec2(-0.8, 0.1);
const vec2 p5 = vec2(-0.7,-0.1);
const vec2 p6 = vec2( 0.0,-0.2);
const vec2 p7 = vec2( 0.7,-0.2);
float cDistance_t( in vec2 v )
{
float d0 = sdLine_t( v, p0, p1 );
float d1 = sdLine_t( v, p1, p2 );
float d2 = sdLine_t( v, p2, p3 );
float d3 = sdLine_t( v, p3, p4 );
float d4 = sdLine_t( v, p4, p5 );
float d5 = sdLine_t( v, p5, p6 );
float d6 = sdLine_t( v, p6, p7 );
float d7 = sdLine_t( v, p7, p0 );
//线宽范围外均为0,所以这里取最大值
float t=max(d0,max(d1,max(d2,max(d3,max(d4,max(d5,d6))))));
return t*8.;
}
void mainImage(out vec4 fragColor,in vec2 fragCoord){
float m=min(iResolution.x,iResolution.y);
vec2 p=(-iResolution.xy+2.*fragCoord.xy)/m;
float c_lxs=cDistance_t(p);
fragColor=vec4(vec3(c_lxs),1.0);
}