最近有一個需求是換背景拍照,用到了kinect,但是卻發現利用kinect摳人物圖像出來時頭髮不見了,可能是因爲頭髮黑色容易吸收紅外線吧,下面這張是kinect原始摳圖效果:
可以看到,頭髮部分丟失了。
其實我的解決方案就是找到頭髮所在的紋理區域,然後將黑色的頭髮顯示出來,以下慢慢道來。
1,先利用kinect把頭部和脖子定位出來,先創建2個小球,用來定位頭和脖子:
於是,運行程序就是如下圖所示:
2,將頭部和脖子的空間座標轉爲屏幕顯示的紋理座標,將頭部距離脖子的距離大小作爲整個頭的大小,以此容納頭髮區域,然後傳入最後需要的shader中,shader放在後面講:
Vector3 HeadInPlanePos = m_Plane.worldToLocalMatrix.MultiplyPoint(m_HeadNode.position);
Vector3 NeckInPlanePos = m_Plane.worldToLocalMatrix.MultiplyPoint(m_NeckNode.position);
Debug.Log(HeadInPlanePos + " " + NeckInPlanePos);
//座標從-5 到 5;
//頭部對應的紋理座標
Vector2 HeadInTexPos = new Vector2((HeadInPlanePos.x + 5.0f) * 0.1f, (HeadInPlanePos.z + 5.0f) * 0.1f);
m_Mat.SetFloat("_HeadX", 1.0f - HeadInTexPos.x);
m_Mat.SetFloat("_HeadY", 1.0f - HeadInTexPos.y);
//脖子對應的紋理座標
Vector2 NeckInTexPos = new Vector2((NeckInPlanePos.x + 5.0f) * 0.1f, (NeckInPlanePos.z + 5.0f) * 0.1f);
m_CurRadius = Mathf.Lerp(m_CurRadius, Vector2.Distance(NeckInTexPos, HeadInTexPos), 0.5f);
if (m_CurRadius > 0.05f)
{
m_CurRadius = 0.05f;
}
m_Mat.SetFloat("_Radius", m_CurRadius);
3,以上步驟完成,來看看我的shader是如何實現的,爲了不影響效果,我這裏只是修改了kinect原本使用的shader,在此基礎上做了細微的修改,也就是顯示丟失的頭髮:
上圖就是在頭部以頭部到脖子距離的兩倍爲半徑擴展出來的摳圖部分,完整shader如下:
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
// Unlit alpha-blended shader.
// - no lighting
// - no lightmap support
// - no per-material color
Shader "Jx/Bg" {
Properties {
_MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
}
SubShader {
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
LOD 100
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f {
float4 vertex : SV_POSITION;
float2 texcoord : TEXCOORD0;
UNITY_FOG_COORDS(1)
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
float4 _MainTex_ST;
uniform half _HeadX;
uniform half _HeadY;
uniform half _Radius;
uniform half _Head2X;
uniform half _Head2Y;
uniform half _Radius2;
v2f vert (appdata_t v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.vertex = UnityObjectToClipPos(v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.texcoord);
UNITY_APPLY_FOG(i.fogCoord, col);
if(distance(i.texcoord.xy, half2(_HeadX, _HeadY)) < _Radius*2.0 /*&& (col.r + col.g + col.b) < 0.8 && abs(col.r - col.g) < 0.6 && abs(col.r - col.b) < 0.6 && abs(col.b - col.g) < 0.6*/)
{
col.a = 1;
}
if(distance(i.texcoord.xy, half2(_Head2X, _Head2Y)) < _Radius2*2.0 /*&& (col.r + col.g + col.b) < 0.8 && abs(col.r - col.g) < 0.6 && abs(col.r - col.b) < 0.6 && abs(col.b - col.g) < 0.6*/)
{
col.a = 1;
}
return col;
}
ENDCG
}
}
}
因爲我這裏是想最多支持兩個玩家的摳圖,所以可以看到
uniform half _Head2X;
uniform half _Head2Y;
uniform half _Radius2;
這些就是爲第二個玩家的頭髮確實做修改的。
4,來看看最終效果和最終shader代碼:
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
// Unlit alpha-blended shader.
// - no lighting
// - no lightmap support
// - no per-material color
Shader "Jx/Bg" {
Properties {
_MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
}
SubShader {
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
LOD 100
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f {
float4 vertex : SV_POSITION;
float2 texcoord : TEXCOORD0;
UNITY_FOG_COORDS(1)
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
float4 _MainTex_ST;
uniform half _HeadX;
uniform half _HeadY;
uniform half _Radius;
uniform half _Head2X;
uniform half _Head2Y;
uniform half _Radius2;
v2f vert (appdata_t v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.vertex = UnityObjectToClipPos(v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.texcoord);
UNITY_APPLY_FOG(i.fogCoord, col);
if(distance(i.texcoord.xy, half2(_HeadX, _HeadY)) < _Radius*2.0 && (col.r + col.g + col.b) < 0.8 && abs(col.r - col.g) < 0.6 && abs(col.r - col.b) < 0.6 && abs(col.b - col.g) < 0.6)
{
col.a = 1;
}
if(distance(i.texcoord.xy, half2(_Head2X, _Head2Y)) < _Radius2*2.0 && (col.r + col.g + col.b) < 0.8 && abs(col.r - col.g) < 0.6 && abs(col.r - col.b) < 0.6 && abs(col.b - col.g) < 0.6)
{
col.a = 1;
}
return col;
}
ENDCG
}
}
}
(col.r + col.g + col.b) < 0.8 && abs(col.r - col.g) < 0.6 && abs(col.r - col.b) < 0.6 && abs(col.b - col.g) < 0.6這部分代碼片段就是判定黑色部分爲頭髮,然後將圖中透明度爲0的對應該處的像素透明設置爲1,就把頭髮顯示出來了。