现在的大部分游戏都有给角色身上的服饰染色的功能。有的是给出几种染色的配色方案供玩家选择,还有的是直接开发染色区域,在区域里可以自定义染色颜色。
这里用unity实现一下染色功能,主要思路就是用一张mask的四通道划分染色的区域。
这里没有做其他光照计算,仅做了染色的部分,材质面板:
使用代码把材质面板接入UI操控,就可以让玩家自定义染色的颜色了。
具体实现方案:
首先求初始颜色的luminance
inline half luminance(half3 c)
{
return dot(float3(0.2125, 0.7154, 0.0721), c);
}
然后采样mask,mask的rgb通道分别控制三块染色区域,a通道则负责识别染色与非染色区域:
half3 SamplerTex_Dye(sampler2D mainTex, sampler2D dyeTex, float2 uv, half4 dyeColor0, half4 dyeColor1, half4 dyeColor2)
{
half4 texColor = tex2D(mainTex, uv);
half4 dye = tex2D(dyeTex, uv);
half dyeFlag0 = dye.r * dye.a;
half dyeFlag1 = dye.g * dye.a;
half dyeFlag2 = dye.b * dye.a;
half noChangeFlag = 1 - dye.a;
half3 color0 = ComputeDyeColor(texColor.rgb, dyeColor0);
half3 color1 = ComputeDyeColor(texColor.rgb, dyeColor1);
half3 color2 = ComputeDyeColor(texColor.rgb, dyeColor2);
return saturate(dyeFlag0*color0 + dyeFlag1*color1 + dyeFlag2*color2 + noChangeFlag*texColor.rgb);
}
其中dyeColor为外部控制,rgb控制染色的颜色,a控制染色的强度。
计算染色颜色的函数ComputeDyeColor:
inline half3 ComputeDyeColor(half3 c, half4 dyeColor)
{
half brightness = lerp(1, luminance(c), dyeColor.a);
return brightness * lerp(c.rgb, dyeColor.rgb, dyeColor.a);
}
上图中Unitychan染色的mask:
可以看到R通道控制袖子,G通道控制马甲,B通道控制袜子。
注意mask图导入Unity的时候,压缩格式需要选择带A通道的,且勾上Alpha is Transparency。
Shader全部代码:
Shader "Custom/Actor/Tex-Dye"
{
Properties
{
_MainTex ("MainTex", 2D) = "white" {}
_Color ("Base Color", Color) = (1,1,1,1)
_DyeTex ("Dye Tex", 2D) = "black" {}
_DyeColor0 ("Dye Color 0", Color) = (1,1,1,1)
_DyeColor1 ("Dye Color 1", Color) = (1,1,1,1)
_DyeColor2 ("Dye Color 2", Color) = (1,1,1,1)
}
SubShader
{
Tags { "Queue"="Geometry" "RenderType"="Opaque" }
LOD 200
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
half4 _Color;
sampler2D _DyeTex;
half4 _DyeColor0;
half4 _DyeColor1;
half4 _DyeColor2;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
inline half luminance(half3 c)
{
return dot(float3(0.2125, 0.7154, 0.0721), c);
}
inline half3 ComputeDyeColor(half3 c, half4 dyeColor)
{
half brightness = lerp(1, luminance(c), dyeColor.a);
return brightness * lerp(c.rgb, dyeColor.rgb, dyeColor.a);
}
half3 SamplerTex_Dye(sampler2D mainTex, sampler2D dyeTex, float2 uv, half4 dyeColor0, half4 dyeColor1, half4 dyeColor2)
{
half4 texColor = tex2D(mainTex, uv);
half4 dye = tex2D(dyeTex, uv);
half dyeFlag0 = dye.r * dye.a;
half dyeFlag1 = dye.g * dye.a;
half dyeFlag2 = dye.b * dye.a;
half noChangeFlag = 1 - dye.a;
half3 color0 = ComputeDyeColor(texColor.rgb, dyeColor0);
half3 color1 = ComputeDyeColor(texColor.rgb, dyeColor1);
half3 color2 = ComputeDyeColor(texColor.rgb, dyeColor2);
return saturate(dyeFlag0*color0 + dyeFlag1*color1 + dyeFlag2*color2 + noChangeFlag*texColor.rgb);
}
fixed4 frag (v2f i) : SV_Target
{
return half4(SamplerTex_Dye(_MainTex, _DyeTex, i.uv, _DyeColor0, _DyeColor1, _DyeColor2), 1);
}
ENDCG
}
}
}