unity shader 实现九宫格拉伸效果
【代码】unity shader 实现九宫格拉伸效果。
最初本来觉得这应该是一个比较简单的问题(哪怕我这种刚学shader的都感觉好像能解决),但是网上好像没有找到这方面的文章,问GPT也全给我乱答写了一堆错误的代码,因此决定自己实现以下并留下此篇笔记供人参考。
整体思路
首先我们的目标很明确,我们能够拿到当前变换后的纹理uv坐标,
需要找到在原图中他应该对应的uv坐标)。
→
这里我们先将问题给分解,从单独研究x(也就是u)方向的计算
我们分解为三个区域13区域就是我们九宫格保持不变的区域,1、3在原图是多少宽度那在变换后宽度也还应该是多少。
那么我们开始分区域讨论
当在区域1中
在区域1中的条件
:
当前与原点的距离=当前图片的宽度Wide×u 如果这个值小于 原图宽度BaseWide×九宫格左边的宽度,Left(自己设的值)那么我们就可以判定当前我们的x方向落在了区域1。
在区域1中映射的计算
:
已知区域1两边宽度一致,都是BaseWide×Left,
当前与原点的距离Wide×u那么映射到原图上,也应该是原图第Wide×u像素的位置,在除以原图的总宽度,BaseWide,就能得到他在原图中的u坐标。
因此得出结论:
u'=u*Wide/BaseWide
当在区域3中
在区域3中的条件:(1-u)Wide<九宫格右边界的距离Right*BaseWide
区域3的映射计算和区域1基本一致,这里直接得出结论:
u'=1-(1-u)*Wide/BaseWide)
当在区域2中
在区域2中的条件:不在区域1,3中,
这个区域的计算相对比较麻烦。
我们的计算思路:
破题的关键
我们破题的关键就在于AB的长度在区域2的占比,和A’B’的长度在区域2的占比应该是一致的。
A距离原点的距离u×Wide,AB的长度=A距离原点的距离-区域1的长度。
因此
AB=u×Wide-BaseWide*Left
BC=Wide-BaseWide*(Left+Right)
因此得出AB在BC中的占比=A’B’在B’C’中的占比=AB/BC
B'C'=BaseWide*(1-Left-Right)
A'B'=BC×占比(也就是AB/BC)
算到这里就已经很明朗了:
A'距离原点的坐标=A'B'+BaseWide*Left
那么新的u'=(A'B'+BaseWide*Left)/BaseWide
最后整理公式:
u'=((u*Wide-Left*BaseWide)/(Wide-(Left+Right)
*BaseWide)*(BaseWide*(1-Left-Right))+Left*BaseWide)/BaseWide
y(v)方向的计算和x(u)方向的计算是完全一致的,这里就不再赘述了,
值得注意的是y方向的坐标原点是在下方。
Shader "Unlit/Tshader" {
Properties {
_MainTex ("Texture", 2D) = "white" { }// 贴图
_Wide ("Wide", float) = 1000//当前的宽度(在改变图片大小后需要传入的参数)
_Hight ("Hight", float) = 259//当前高度(在改变图片大小后需要传入的参数)
_BaseWide ("BaseWide", float) = 525//贴图的基础宽度
_BaseHight ("BaseHight", float) = 259//贴图的基础高度
_Left ("left", Range(0, 1)) = 0.3//九宫格左边界距离
_Right ("right", Range(0, 1)) = 0.3//九宫格右边界距离
_Up ("up", Range(0, 1)) = 0.3//九宫格上边界距离
_Down ("down", Range(0, 1)) = 0.3//九宫格下边界距离
}
SubShader {
LOD 100
Tags { "QUEUE" = "Transparent" "IGNOREPROJECTOR" = "true" "RenderType" = "Transparent" }
Pass {
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
sampler2D _MainTex;
float4 _MainTex_ST;
float _Wide;
float _Hight;
float _BaseWide;
float _BaseHight;
float _Left;
float _Right;
float _Up;
float _Down;
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert(appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
return o;
}
fixed4 frag(v2f i) : COLOR {
float x = i.uv.x;
float y = i.uv.y;
//UV映射
if (x * _Wide < _Left * _BaseWide) {
x = i.uv.x * _Wide / _BaseWide;
} else if ((1 - i.uv.x) * _Wide < _Right * _BaseWide) {
x = 1 - (1 - i.uv.x) * _Wide / _BaseWide;
} else {
x = ((i.uv.x * _Wide - _Left * _BaseWide) / (_Wide - (_Left + _Right) * _BaseWide) * (_BaseWide * (1 - _Left - _Right)) + _Left * _BaseWide) / _BaseWide;
}
if (y * _Hight < _Down * _BaseHight) {
y = i.uv.y * _Hight / _BaseHight;
} else if ((1 - i.uv.y) * _Hight < _Up * _BaseHight) {
y = 1 - (1 - i.uv.y) * _Hight / _BaseHight;
} else {
y = ((i.uv.y * _Hight - _Down * _BaseHight) / (_Hight - (_Down + _Up) * _BaseHight) * (_BaseHight * (1 - _Up - _Down)) + _Down * _BaseHight) / _BaseHight;
}
return tex2D(_MainTex, float2(x, y)); // 返回对应UV坐标处的像素值作为最终输出
}
ENDCG
}
}
}
更多推荐
所有评论(0)