先上一个效果图,如下:
大致的实现思路是:
1.根据当前模型坐标的x取值和图片的分割位置比较,x坐标值小于这分割位置时显示第一张图片,大于则显示另外一张;
2.然后在分割点的两边做一个图片渐变的融合即可。
先实现第一步:
1.需要的属性三个:两张贴图和一个分割点的值:
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_SecondTex ("SecondTex", 2D) = "white" {}
_TextureMix("TextureMix",range(0,1)) = 0.5
}
模型到顶点的结构体有两个,模型的顶点坐标和UV
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
顶点到片元函数的结构体需要得到模型空间下的坐标(只需要X,Y两个值)和两个需要采样的UV:
struct v2f
{
float2 uv1 : TEXCOORD0;
float2 uv2 : TEXCOORD1;
float4 vertex : SV_POSITION;
float2 localPos : TEXCOORD2;
};
在顶点函数中需要注意的是,需要将模型空间下的顶点坐标的范围做一下转化,如下面的代码注释:
v2f vert (appdata v)
{
v2f o;
o.localPos = v.vertex.xy + float2(0.5,0.5); //v.vertex 范围在(-0.5,0.5)之间,加上0.5取值到0-1
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv1 = TRANSFORM_TEX(v.uv, _MainTex);
o.uv2= TRANSFORM_TEX(v.uv, _SecondTex);
return o;
}
最后在片元函数中输出:
fixed4 frag (v2f i) : SV_Target
{
if(i.localPos.x < _TextureMix)
{
return tex2D(_MainTex, i.uv1);
}
else
{
return tex2D(_SecondTex,i.uv2);
}
完成后可以看到,拖动分割的位置,两张图片会根据这个位置分别显示在面片上:
2.继续第二步,需要在分割点上进行两张图片的融合,有一个渐变的过程看起来不那么僵硬:
在最后的片元函数中,先计算出需要 分割点 到 模型位置x 的距离:
fixed distanceFrontMixPoint = _TextureMix - i.localPos.x;
有了距离后,需要一个渐变的距离y,当距离这个分割点在y的范围内时,两张图片做一个融合显示,我用的是0.2,这个值可以根据自己的喜好调整:
if(abs(distanceFrontMixPoint) < 0.2) //取绝对值,因为左右两边都需要显示
{
fixed mixFactor = 1 - (distanceFrontMixPoint + 0.2)/0.4; //取得混合因子,这个记住就好了,如果修改了显示范围的话,分母需要修改长度的两倍
return lerp(tex2D(_MainTex, i.uv1),tex2D(_SecondTex,i.uv2),mixFactor);
}
完成后可以看到,在滑动分割点时,图片出现了融合,但是有一个问题是在最左边和最右边(也就是0和1的位置)时,显示也出现了融合:
什么原因呢,因为当在x在0的位置时,也满足进行混合的条件:
if(abs(distanceFrontMixPoint) < 0.2)
同样在1的位置也是一样,所以想要在0和1的位置显示完成的图片,修改一下分割点的范围即可(两边分别增加一个融合的范围,这里例子中是0.2):
_TextureMix("TextureMix",range(-0.2,1.2)) = 0.5
完成后就和开头实现的效果一样啦~~
git仓库:https://github.com/Looooooong/ShadersRoom
最后附上整体的源码:
Shader "ShadersRoom/MixTwoTextures"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_SecondTex ("SecondTex", 2D) = "white" {}
_TextureMix("TextureMix",range(-0.2,1.2)) = 0.5
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv1 : TEXCOORD0;
float2 uv2 : TEXCOORD1;
float4 vertex : SV_POSITION;
float2 localPos : TEXCOORD2;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _SecondTex;
float4 _SecondTex_ST;
half _TextureMix;
v2f vert (appdata v)
{
v2f o;
o.localPos = v.vertex.xy + float2(0.5,0.5); //v.vertex 范围在(-0.5,0.5)之间,加上0.5取值到0-1
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv1 = TRANSFORM_TEX(v.uv, _MainTex);
o.uv2= TRANSFORM_TEX(v.uv, _SecondTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed distanceFrontMixPoint = _TextureMix - i.localPos.x;
if(abs(distanceFrontMixPoint) < 0.2)
{
fixed mixFactor = 1 - (distanceFrontMixPoint + 0.2)/0.4;
return lerp(tex2D(_MainTex, i.uv1),tex2D(_SecondTex,i.uv2),mixFactor);
}
if(i.localPos.x < _TextureMix)
{
return tex2D(_MainTex, i.uv1);
}
else
{
return tex2D(_SecondTex,i.uv2);
}
}
ENDCG
}
}
}