千家信息网

Unity+shader如何绘制halftone动画实现星之卡比新星同盟切屏效果

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,这篇文章将为大家详细讲解有关Unity+shader如何绘制halftone动画实现星之卡比新星同盟切屏效果,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了
千家信息网最后更新 2026年11月01日Unity+shader如何绘制halftone动画实现星之卡比新星同盟切屏效果

这篇文章将为大家详细讲解有关Unity+shader如何绘制halftone动画实现星之卡比新星同盟切屏效果,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

卡比是个好游戏,这次的切屏和loading画面也是可爱得不行。先放出这篇文章想要重现的效果:


卡比的进门切屏效果

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

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

分析一番后,我们可以将卡比的切屏特效拆分成几个部分:

1.沿一个方向移动的halftone图形
2.两张贴图/颜色的切换
3.任意形状的mask贴图以指定轴旋转缩放

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

  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
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划分为若干网格。最后以每个格子的中心点为圆心画出圆点。

    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即可。

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

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

画圆点的时候就可以发现出现了一些明显的直线(上上图的第一列圆点处),画方块的时候就满看不出来了

这时就需要在计算时将后方法格子一起考虑进去了

       float _rdNext=_rd+0.5*_Diameter/_Num;         fixed2 posCenterNext= posCenter-fixed2(diameterW,0); //center of up-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)+step(distance(i.uvORI,posCenterNext),_rdNext); //Dot

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

是的,格子之间没有偏移,完全是横平竖直的分布,显得一点也不好看,这就需要加上一个参数来让它在y方向有一个偏移,之后为了让图形更加自然,在y方向偏移时,x方向也进行一定压缩。于是我们对frag进行以下修改
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;}

因为在uv方向上都有了偏移,所以绘制一个格子里的内容时需要左上左下、右上右下四个格子。

在properties中,添加

_rotOffset("Offset Between Points",  Range(0,0.5)) = 0.0

由于圆点是对称图形,偏移量的取值范围只需要是格子尺寸的一半。
至此,一个可以移动位置的halftone切图效果就完成了。说到这里,大家可能会想,这个跟直接使用一张贴图作为遮罩有什么不同呢?答案是,确实没什么不同,目前为止的效果是完全可以使用一张遮罩实现的。使用遮罩甚至还能达成更多花哨的效果,但遮罩也有它所不能完成的事情。接下来修改格子划分的方法,让格子的位置不再跟随position变化,再改变一下计算圆点半径的方法,作出圆点一个个弹出的效果。

修改一下格子划分的方法,让格子的位置不再跟随position变化:

fixed indexOfGrid=floor((i.uvORI.x)/diameterW);//num of grids between uv and PosW posCenter.x=(indexOfGrid+0.5)*diameterW;     fixed modOffset=frac(indexOfGrid*_rotOffset)*_Diameter;posCenter.y=(floor((i.uvORI.y-modOffset)/diameterH)+ 0.5)*diameterH+modOffset;

然后再改变一下计算圆点半径的方法,让其根据uv和position 的关系变化:

 _rd=0.5*(_Position-posCenter.x)/_Num;//radius of the current grid  float _rdNext=_rd+0.5*diameterW/_Num;  float _rdPrev=_rd-0.5*diameterW/_Num;

这里计算前后格子中的半径时,需要改用压缩过的diameter,不然就会出现不太对劲的效果。由于划分格子的方法已经发生了变化,原本的if(indexOfGrid>=1)的时候,直接输出textureB已经无效了,实际上,判断的条件应该是圆点半径小于0。

接下来,需要一个表明角度的property,来让图形按照一个方向移动,而不是只能从左到右。

   _Rotation("Rotation", Range(0,360)) = 0.0

旋转角度的算法,在很多地方都可以找到。这里直接转换计算halftone时使用的uv坐标,而textureA和B的uv不受影响。由于各个顶点之间的uv变化是线性的,这一转化可以放在vert函数中完成。

o.uvORI.xy=v.texcoord; fixed2 offsetXY=fixed2(0.5,0.5);  float Rot = _Rotation * (3.1415926f/180.0f);  float s = sin(Rot);  float c = cos(Rot);  float2x2 rMatrix = float2x2(c, -s, s, c);  rMatrix *= 0.5;  rMatrix += 0.5;  rMatrix = rMatrix * 2 - 1;  o.uvORI.xy = mul(o.uvORI.xy-offsetXY, rMatrix)+offsetXY;

这个_Rotation换成弧度角也是完全可以的。 _Rotation("Rotation", Range(0,6.283)) = 0.0 ,到时候就不需要进行乘PI再除180的计算。

现在看一下 示例场景

SlideshowEffect: Halftone and Popup

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

里面的halftone切图的效果基本完成了。

关于Unity+shader如何绘制halftone动画实现星之卡比新星同盟切屏效果就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

格子 效果 圆点 图形 半径 方向 方法 卡比 偏移 变化 移动 位置 问题 不同 接下来 内容 时候 更多 篇文章 动画 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 喔噻互联网科技有限公司 广州大一互联网络科技有限公司 互联网科技工作委员会副主任毛军 没上市的科技互联网公司 北京互联网科技 猪兼强互联网科技 华中科技大学互联网 狮子座互联网科技有限公司 奇酷互联网络科技公司 互联网 新科技 互联网科技公司招聘 银川新华互联网科技学校地址 成都玖崟互联网科技有限公司乐商圈怎么样 互联网之后的科技是什么 承德科技产业互联网发展 互联网大会黑科技排名 速维有品互联网信息科技(苏州)有限公司 银川新华互联网科技学校原画 杭州融税互联网科技有限公司 互联网科技自媒体灵感 天津市摩尔互联网科技 互联网金融监管科技数据 互联网科技保险 互联网最新的科技 平安科技 互联网 互联网科技公司官网 中国科技互联网公司收入 互联网金融科技公司的优势 上海同邻互联网科技有限公司规模 长沙渺峰互联网科技有限公司
0