赞
踩
游戏中NPC脚下的地面上通常会显示其攻击范围。如果NPC具有视野属性,那么这个攻击范围就可能是任意角度。就需要Sprite如同Image组件一样可以360度填充。
不过Image的角度填充是在C#层实现的,无法套用在Sprite上。
参照Unity内置shader,Sprites-Default.shader :
- Pass
- {
- CGPROGRAM
- #pragma vertex SpriteVert
- #pragma fragment SpriteFrag
- #pragma target 2.0
- #pragma multi_compile_instancing
- #pragma multi_compile_local _ PIXELSNAP_ON
- #pragma multi_compile _ ETC1_EXTERNAL_ALPHA
- #include "UnitySprites.cginc"
- ENDCG
- }
Sprite的shader相当简短,因为它的顶点/片元着色器全都是在UnitySprites.cginc里实现。参考默认片元着色器函数SpriteFrag的实现,自定义片元着色器:
- Pass
- {
- CGPROGRAM
- #pragma vertex SpriteVert
- #pragma fragment MySpriteFrag
- #pragma target 2.0
- #pragma multi_compile_instancing
- #pragma multi_compile_local _ PIXELSNAP_ON
- #pragma multi_compile _ ETC1_EXTERNAL_ALPHA
- #include "UnitySprites.cginc"
-
- fixed4 MySpriteFrag(v2f IN) : SV_Target
- {
- fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
- c.rgb *= c.a;
- return c;
- }
- ENDCG
- }
根据角度填充圆形Sprite,首先根据UV坐标判断角度,角度大于限定值的区域不显示。夹角的计算很简单,还有一个更简单的方法就是Unity有现成的函数Vector3.Angle(Vector3 dirA, Vector3 dirB),把函数实现移植到shader中:
- fixed GetAngle(fixed2 from, fixed2 to){
- float denominator = sqrt((from.x*from.x + from.y*from.y) * (to.x*to.x + to.y * to.y));
- if (denominator < 0.000001)
- return 0;
-
- float dotNum = clamp(dot(from, to) / denominator, -1.0, 1.0);
- return degrees(acos(dotNum));
- }
判断当前UV坐标方向相对坐标系Y轴正方向的夹角,并把限定角度外的像素设置为透明:
- fixed4 MySpriteFrag(v2f IN) : SV_Target
- {
- fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
- fixed2 uvCenter = fixed2(0.5, 0.5);
- fixed absAngle = GetAngle(fixed2(0,1), IN.texcoord - uvCenter);
- c.a *= absAngle <= _Angle;
- c.rgb *= c.a;
- return c;
- }
中心的角度比较尖锐,为了美观通常还会对近点进行裁剪,将圆心掏空。实现原理就是把距离圆心一定距离内的像素改为全透明:
- fixed4 MySpriteFrag(v2f IN) : SV_Target
- {
- fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
- fixed2 uvCenter = fixed2(0.5, 0.5);
- fixed absAngle = GetAngle(fixed2(0,1), IN.texcoord - uvCenter);
- c.a *= (absAngle <= _Angle) * distance(IN.texcoord, uvCenter)*2 > _NearClip;
- c.rgb *= c.a;
- return c;
- }
完整shader:
- Shader "Custom/SpriteFillCircle"
- {
- Properties
- {
- [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
- _Color ("Tint", Color) = (1,1,1,1)
- _Angle ("Angle", Range(0,180)) = 30
- _NearClip ("Near Clip", Range(0,1)) = 0.1
- [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
- [HideInInspector] _RendererColor ("RendererColor", Color) = (1,1,1,1)
- [HideInInspector] _Flip ("Flip", Vector) = (1,1,1,1)
- [PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {}
- [PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0
- }
-
- SubShader
- {
- Tags
- {
- "Queue"="Transparent"
- "IgnoreProjector"="True"
- "RenderType"="Transparent"
- "PreviewType"="Plane"
- "CanUseSpriteAtlas"="True"
- }
-
- Cull Off
- Lighting Off
- ZWrite Off
- Blend One OneMinusSrcAlpha
-
- Pass
- {
- CGPROGRAM
- #pragma vertex SpriteVert
- #pragma fragment MySpriteFrag
- #pragma target 2.0
- #pragma multi_compile_instancing
- #pragma multi_compile_local _ PIXELSNAP_ON
- #pragma multi_compile _ ETC1_EXTERNAL_ALPHA
- #include "UnitySprites.cginc"
- float _Angle;
- float _NearClip;
- fixed GetAngle(fixed2 from, fixed2 to){
- float denominator = sqrt((from.x*from.x + from.y*from.y) * (to.x*to.x + to.y * to.y));
- if (denominator < 0.000001)
- return 0;
-
- float dotNum = clamp(dot(from, to) / denominator, -1.0, 1.0);
- return degrees(acos(dotNum));
- }
- fixed4 MySpriteFrag(v2f IN) : SV_Target
- {
- fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
- fixed2 uvCenter = fixed2(0.5, 0.5);
- fixed absAngle = GetAngle(fixed2(0,1), IN.texcoord - uvCenter);
- c.a *= (absAngle <= _Angle) * distance(IN.texcoord, uvCenter)*2 > _NearClip;
- c.rgb *= c.a;
- return c;
- }
- ENDCG
- }
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。