我报名参加金石计划1期应战——分割10万奖池,这是我的第3篇文章,点击查看活动概况
便是照着葫芦画瓢,以此来娴熟shader绘图的基本模式。 原比如是shader toy上的,传送门,
截图,我不想录制GIF了算了 ,反正不用我的流量,截图作用不太好.
以下是原始作用图
拆分图形
通过前面的造型函数,再来仿写这个就不是那么困难了。动手之前,先拆分一下。
用shader作图,就只能是画家算法了,先画最下面的图层。
静态的,有四个同心圆,只不过颜色和半径不同算了,可归为一类。
一个叉叉,其实便是两条线段。
第四层圆弧
第五层圆弧
扫描的部分能够分为一个线段(射线)和一个扇形区域
四个三角形
方针物体及其分散光波
随机漂动的实心圆点
就这么多,咱们一个个来制作。按照掩盖次序,肯定是最终画 扫描作用和热门分散作用。 其他的没有遮挡关系
画圆
我自己也写了个简略的造型函数画圆,可是,在宽度不行的状况下作用不是太好。 所以,又去解析实例代码中的画法。 由于我的坐标和shadertoy的坐标是有些不同的,我是纯uv坐标,所以不能直接用它的,而且,这样也是学习的进程。关于两个smoothstep相减,有不解的当地请参阅造型函数的弥补
#define SMOOTH(r,R) (1.0-smoothstep(R-.001,R+.001, r))
float circle (float r, vec2 st, float w) {
// return step(-w,-abs(length(st)-r) ) ; 画圆仍是得用平滑
// return smoothstep(w,0.0, abs(length(st) - r)) ;//仍是不行平滑
return smoothstep(w,0.0, abs(sqrt(dot(st,st) )- r)) ;// 不行
}
//看来用两个stepsmooth 相减的作用要优于直接用肯定值
float circle2(vec2 uv, float radius, float width)
{
float r = length(uv );
return SMOOTH(r-width,radius)-SMOOTH(r+width,radius);
}
我最终发现,这个宽度不行就会出问题是必定的,算了,我也不是什么完美主义者(主要是火力缺乏啊)。 这样咱们就能够完成前四个同心圆。
void main(){
vec4 colorT = texture2D(NAME, vec2(.5));
float t = u_Time/1000. ;
float ratio = u_CanvasSize.x/u_CanvasSize.y;
vec2 st =gl_FragCoord.xy / u_CanvasSize ;
// 仍是先把坐标处理到中心方便点
st-= vec2(.5) ;
st.x*=ratio ;
vec3 color =vec3(0.0) ;
float gap = .03;
vec3 color1= vec3(1) ;
vec3 color2= vec3(0.3) ;
// 漏了一个小圆
color = mix( color, blue1, circle2( st,0.03, 0.0011)) ;
color = mix( color, blue1, circle2( st,0.15, 0.001)) ;
color = mix( color, blue1, circle2( st,0.25, 0.001)) ;
color += vec3(circle2( st,0.35, 0.002)) ;
对称的圆弧
第五个圆弧,不再是完好的圆了。 能够弄一个通用的从起始到结束弧度的圆弧函数,可是这儿我就偷个懒, 咱们要画的是两段对称的圆弧,反过考虑便是, 只要在x轴周围的一个区域不画就能够了。 这个规模便是 [-, ]和[-, +]. 咱们肯定是要靠反三角函数才干得出视点,可是这儿不必要,咱们仅仅比较大小,直接拿正弦值进行比较即可,刚好正弦又是关于 x= /2 对称的,所以咱们只需要判断一边即可。
最终加上动画.
float circle3(vec2 uv, float radius, float width, float sinval)
{
float r = length(uv );
// sinval = y/r 对边比斜边
return abs(uv.y/r) < sinval ? 0.0:SMOOTH(r-width,radius)-SMOOTH(r+width,radius);
}
....
// 画第五层圆弧
color = mix( color, blue3, circle3( st,0.38, 0.001, 0.5 + 0.2*sin(t))) ;
后边的第五层也是如法炮制,只不过还要写一个和上面函数反着制作函数,便是之前放弃的弧度区域是现在要制作的区域,最简略的办法是直接1 – 去之前的函数,可是仍是有点不太合适。 所以还得来一个。
// 没看懂他那个逻辑 ,我直接用视点判断吧 用正弦的规模 刚好符合这个轴对称 【-0.1,0.1】的都不画
float circle4(vec2 uv, float radius, float width, float min,float max)
{
float r = length(uv );
// sinval = y/r 对边比斜边
float sinval = uv.y / r ;
return sinval > min && sinval < max ? SMOOTH(r-width,radius)-SMOOTH(r+width,radius):0.0;
}
.....
// 六层层要分4次画
color = mix( color, blue1, circle4( st,0.45, 0.003, sin(gap), sin(pi/4. -gap))) ;
color = mix( color, blue1, circle4( st,0.45, 0.003, -sin(pi/4.-gap), -sin(gap))) ;
// 想要视点上的均匀就得用视点。
color = mix( color, blue3 -0.1, circle4( st,0.45, 0.003, sin(pi/4. +gap), sin(pi/2. -gap))) ;
color = mix( color, blue3 -0.1, circle4( st,0.45, 0.003, - sin(pi/2. -gap),-sin(pi/4. +gap))) ;
圆弧到这儿就画完了。 补上十字,直线应该没啥好说了的吧。
float crossAngle(float x , float y , float radius){
float r = sqrt(x*x + y*y) ;// 点乘再开平方会比直接length或者distance要好吗
return r<radius && (abs(x-y)<0.001 || abs(x+y)<0.001 )? 1.0:0.0;
}
....
color = mix(color, color2,crossAngle(st.x,st.y,0.35)) ;
扫描
先来个简略的射线扫描。
float line(vec2 st, float k, float b,float w){
return smoothstep(w,0., abs(st.y -st.x * k -b) );
}
// 改直线为射线 这次的思路是先画一条九点钟方向的射线 然后偏移 这儿的偏移仅仅是旋转
// 先把当前坐标 逆偏移到 三点钟方向
float ray(vec2 st, float angle ,float r, float w){
st *= rotate(-angle) ;
return length(st)<r && st.x>0.? smoothstep(w, 0., abs(st.y )):.0;
}
然后加上扇形,其实原图便是个扇形,和上面画的圆弧相对应,可谓是一个是填充,一个是描边。
制作扇形便是把之前的等于半径改成小于半径即可, 然后这个突变,是视点突变。
加了偏移之后,呈现了问题。我想调试一下,可是shader没法像js那样调试,那就用js调试,哈哈,我真是个机灵鬼。
好吧,然后发现bug是由于,我处理视点大于180的状况,这行代码,应该在偏移之前的, 细心点,嗯。最终扫描是要放在最上层的,这儿先做,
float sector2(vec2 st , float r ,float endAng, float offset ){
float d = length(st) ;
// 余弦值的值域比较好控制 就用它,仍是直接用反三角函数算了
float cosVal = st.y/ d ; // 把这个d变成r ,成果便是扇形减去三角形剩下圆弧
float ang = acos(cosVal) ;
if(st.x<0.) ang = 2.0 * pi - ang ;
// 相同 这个偏移要逆着用
ang-=offset ;
// 我逻辑是比较视点大小 所以小于零要处理 有while 可是我写了错了
ang = mod(ang,2.*pi);
if(ang <0.)ang+=2.*pi;
// 突变 便是百分比
float percent = ang / endAng ;
return d < r && ang < endAng ? percent:0. ;
}
color = mix(color, blue3, sector2(st,0.35, pi/2.5, t+pi/2. - pi/2.5 )) ;
方针物体及光圈分散动画
说起来,初学前端的时候,就学了一个这样css动画,地图热门符号。 画圆,加上半径周期变改变,这个作用啊,就差不多了。 咱们这个更简略,只要一个圆环的半径改变,内部那个圆点其实没变。
// 由于我默许便是圆点圆心,所以pos便是偏移。
float circleMove(vec2 st, vec2 pos, float radius, float w ){
// 简略一点,直接逆变换然后调用之前得函数
st-=pos ;
return circle(radius, st,w) ;
}
// 圆环加径向突变 加移动
float circleWave(vec2 st ,vec2 pos ,float radius, float width){
// 还需要判断是否超出了扫描规模,超出了不予烘托
if(length(st) >0.35)return 0.;
st -=pos ;
float d = length(st) ;
float percent = (radius - d) /width ;// 反了?的确反了 难怪作用有点奇怪,按我现在的写法,w实际上是双倍的
// float percent = 1.- (radius - d) /width/2.0 ;//上面得会有负数,所以才觉得怪怪得,扫描是加法,所以作用是那样得
// float percent = 1.-( 1.+ (radius - d) /width)/2. ;//先把值域搞到0-1 然后取反
return (SMOOTH(d-width,radius)-SMOOTH(d+width,radius))*percent;
// return circle2(vec2 uv, float radius, float width)
}
之前的逻辑是有些问题的,宽度实际上是参数的两倍,也因而我核算的百分比是有负值的
// 发现方针
vec2 pos = vec2(.15*(sin(t/30.)+cos(t/50.)), .18*cos(t/20.+1.)) ;
// 仍是 要一个小圆环 里面一个圆点 圆点还有用得 可是要能漂移,所以得重写一个
color = mix(color, red, circleMove(st,pos,.01,0.002)) ;
color = mix(color, red, circleWave(st,pos,r1, 0.02)) ;
到这儿基本上功德圆满。 再加上随机漂浮的小白点,和上面的逻辑能够说一毛相同了。
float circledot(vec2 st , vec2 pos , float r ){
st -=pos ;
float d= length(st) ;
return smoothstep(0.0, 0.01, r -d) ; // 要求是 d < r
}
.......
vec2 pos1 =vec2(.4*sin(-t/10.)+ .1* cos(-t/10.0+1.), .15*cos(-t/4.) + .17*sin(t/50.+1.));
vec2 pos2 =vec2(.2*sin(t/30.-1.)+ .1*cos(t/20.0)+.1*sin(t/5.) , .16*cos(-t/10.)- .09*cos(-t/40.+1.) - .18*sin(-t/50.));
color = mix(color, color1, circledot(st,pos1,.01)) ;
color = mix(color, color1, circledot(st,pos2,.01)) ;
我这儿的扫描仍是用了加法,和原作品比较,这样扇形扫到方针的时候会有些不同,当然了,雷达不是这么扫描的。还有,热门分散不能超过第四层圆弧。
最终三角形
三角形的确没啥难画,只不过我之前想着用间隔场怎么画,到现在还没搞明白。仍是用三条直线围成一个吧。
下面的函数,是有些奇妙的,是一条水平或者竖直的线和一组垂直交叉的线限制成的。两条垂直的线其斜率互为倒数取反。 而45角的斜率的肯定相同,所以这儿后边直接便是一个肯定值省事。 实际上便是三条线限制为一个三角。
// 看上去这个函数很麻烦,看不懂,先直接用 radius应该是三角形的位置
float triangles(vec2 uv, float radius)
{
vec2 d = uv;
//加法代表或,乘法代表且 。
// 剖析第一个三角 -0.009 < x- radius < 0 ===> x在【r-0.009, r】内, x - radius
return RS(-.009, 0.0, d.x-radius) * (1.0-smoothstep( 0.007+d.x-radius,0.009+d.x-radius, abs(d.y)))
+ RS( 0.0, 0.009, d.x+radius) * (1.0-smoothstep( 0.007-d.x-radius,0.009-d.x-radius, abs(d.y)))
+ RS(-.009, 0.0, d.y-radius) * (1.0-smoothstep( 0.007+d.y-radius,0.009+d.y-radius, abs(d.x)))
+ RS( 0.0, 0.009, d.y+radius) * (1.0-smoothstep( 0.007-d.y-radius,0.009-d.y-radius, abs(d.x)));
}
float triangle(vec2 st, float r ){
float x= st.x,y = st.y;// 0.7近似根号2的一半
return step(-r, y) *step( y - x, -r*0.7 ) *step( y + x, -r*0.7 )
+ step( y,r) *step( r*0.7,y - x ) *step( r*0.7, y + x )
+ step( x,r) *step( y-x, -r*0.7 ) *step( r*0.7, x+y )
+ step(-r, x) *step( r*.7 ,y - x) *step( y + x, -r*0.7 )
;
}
// 画三角形
color+=triangles(st,0.45 + 0.05*sin(t))* vec3(0.79, 0.19, 0.19)
这就完了,完好代码见码上。