游戏蛮牛学习群(纯技术交流,不闲聊):159852603
游戏蛮牛 手机端
开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

首页   >   博客   >   sakuraplus

Unity+shader实现星之卡比新星同盟切屏效果(1)

2018-10-18 18:49
标签:shader halftone

先放出想要重现的效果

卡比是个好游戏,这次的切屏和loading画面也是可爱得不行。

接下来放出我们最终实现的效果

使用起来也是十分简单的,以上效果连动画系统都可以完成。

分析一番后,我们可以将卡比的切屏特效拆分成几个部分:
1.沿一个方向移动的halftone图形
2.两张贴图/颜色的切换
3.任意形状的mask贴图以指定轴旋转缩放

那么我们从第一步开始,绘制一个halftone图形。
要绘制halftone dots 并让它们在画面中移动,首先需要一个property来确定图形的位置。
[code]csharpcode:

Properties {
 _MainTex ("Texture A", 2D) = "black" {} 
 _MainTexB ("Texture B", 2D) = "black" {} 
 [Space(10)]
 _Position("Halftone Position", Float)=1
 _Diameter("Diameter", Range(0,1) )=0.25
 _Num("Length", Range(1,16)) = 3.0
 }

这里面还定义了dots的直径 _Diameter ,和halftone过渡区域的长度 _Num。
[code]csharpcode:
struct v2f {
float4 pos : SV_POSITION;
half2 uvTA: TEXCOORD0;
half2 uvTB: TEXCOORD1;
half2 uvORI: TEXCOORD2;//original
};
 
v2f vert(appdata_img v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uvTA=(v.texcoord-_MainTex_ST.zw)*_MainTex_ST.xy ;
o.uvTB=(v.texcoord-_MainTexB_ST.zw)*_MainTexB_ST.xy ;
o.uvORI.xy=v.texcoord;

return o;
}
这里两张贴图可以使用tillng和offset,而绘制halftone的uv则使用原始的值。

之后以position为基础,将uv划分为若干网格。最后以每个格子的中心点为圆心画出圆点。
[code]csharpcode: 
fixed4 frag(v2f i) : SV_Target {
float _rd;
fixed2 posCenter;
fixed indexOfGrid=floor((i.uvORI.x-_Position)/_Diameter);//num of grids between uv and PosW
posCenter.x=_Position+(indexOfGrid + 0.5)*_Diameter;
posCenter.y=(floor(i.uvORI.y/_Diameter) + 0.5)*_Diameter;
_rd=0.5*_Diameter* abs(indexOfGrid)/_Num;//radius of the current grid 
fixed inCircle=step(distance(i.uvORI,posCenter),_rd);
inCircle=clamp(inCircle,0,1);

fixed4 texA=tex2D(_MainTex, i.uvTA).rgba;
fixed4 texB=tex2D(_MainTexB, i.uvTB).rgba;
fixed4 sum= lerp(texB,texA,inCircle);
return sum;
}

看一下效果,似乎出现了一些问题

可以发现,position的左右两侧同时出现了对称的圆点,解决这个问题,只需要让uv大于position的部分直接显示textureB即可。

[code]csharpcode: 
if(indexOfGrid>=1){
return tex2D(_MainTexB, i.uvTB).rgba ;//return texture A when uv is in front (larger) of PosW
}

另一个问题是,圆点的半径根据和position的距离是一个递增的关系,在后方的格子里,圆点的半径超过了格子的尺寸时,并不会在相邻的格子里显示出来。

这个问题在格子里画方块时不会看出异常,而画圆点的时候就可以发现出现了一些明显的直线,这时就需要在计算时将后方法格子一起考虑进去了。

[code]csharpcode:
float _rdNext=_rd+0.5*_Diameter/_Num; fixed2 posCenterNext=fixed2(0,0);//center of up-next grid posCenterNext=posCenter-fixed2(diameterW,0); //fixed inCircle=step(abs(i.uvORI.x-posCenter.x),_rd)*step(abs(i.uvORI.y-posCenter.y),_rd); //Square fixed inCircle=step(distance(i.uvORI,posCenter),_rd)+step(distance(i.uvORI,posCenterNext),_rdNext); //Dot

现在,一个可以移动的halftone图形就画出来了,但是这和我们常见的半调图形还有一些不同……

是的,格子之间没有偏移,完全是横平竖直的分布,显得一点也不好看,这就需要加上一个参数来让它在y方向有一个偏移,之后为了让图形更加自然,在y方向偏移时,x方向也进行一定压缩。

于是我们对frag进行以下修改

[code]csharpcode: 
fixed4 frag(v2f i) : SV_Target {
float _rd;
fixed2 posCenter;
fixed diameterW,diameterH;
diameterW=_Diameter*(1-_rotOffset/2);//width of grid , reduce when _rotOffset is larger than zero
diameterH=_Diameter;
fixed indexOfGrid=floor((i.uvORI.x-_Position)/diameterW);//num of grids between uv and PosW
if(indexOfGrid>=1){
return tex2D(_MainTexB, i.uvTB).rgba ;//return texture A when uv is in front (larger) of PosW
}

posCenter.x=_Position+(indexOfGrid+0.5)*diameterW;
fixed modOffset=frac(indexOfGrid*_rotOffset)*_Diameter;
posCenter.y=(floor((i.uvORI.y-modOffset)/diameterH)+ 0.5)*diameterH+modOffset;

_rd=0.5*diameterH* abs(indexOfGrid)/_Num;//radius of the current grid 
float _rdNext=_rd+0.5*diameterH/_Num;
fixed2 posCenterNextUp=posCenter-fixed2(diameterW,_Diameter*(_rotOffset-1));
fixed2 posCenterNextDown=posCenter-fixed2(diameterW,_Diameter*_rotOffset); //center of down-next grid
float _rdPrev=_rd-0.5*diameterH/_Num;
fixed2 posCenterPrevUp=posCenter+fixed2(diameterW,_Diameter*(_rotOffset-1));
fixed2 posCenterPrevDown=posCenter+fixed2(diameterW,_Diameter*_rotOffset); //center of down-next grid
//fixed inCircle=step(abs(i.uvORI.x-posCenter.x),_rd)*step(abs(i.uvORI.y-posCenter.y),_rd);//Square
fixed inCircle=step(distance(i.uvORI,posCenter),_rd);
inCircle+=step(distance(i.uvORI,posCenterNextUp),_rdNext)+step(distance(i.uvORI,posCenterNextDown),_rdNext);
inCircle+=step(distance(i.uvORI,posCenterPrevUp),_rdPrev)+step(distance(i.uvORI,posCenterPrevDown),_rdPrev);
inCircle=clamp(inCircle,0,1);

fixed4 texA=tex2D(_MainTex, i.uvTA).rgba;
fixed4 texB=tex2D(_MainTexB, i.uvTB).rgba;
fixed4 sum= lerp(texB,texA,inCircle);
return sum;
}


由于圆点是对称图形,偏移量的取值范围只需要是格子尺寸的一半。

至此,一个可以移动位置的halftone切图效果就完成了。

不过现在的效果还是跟预期的不太一样,图案只是在平移,而并没有圆点一个个弹出的感觉,下一章我们会修改格子划分的方法,让格子的位置不再跟随position变化。

然后再改变一下计算圆点半径的方法,让其根据uvposition 的关系变化,制作一个popup效果的halftone切图。

最终效果可以在这里观看:


https://sakuraplus.github.io/make-terrain-with-google-elevation/webplayershow/webplayershow.html


Buy me a coffee http://u3d.as/1jVq

0 0

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册帐号

标签

阅读排行

评论排行

推荐博客

最新博客

关闭

站长推荐 上一条 /1 下一条

返回顶部