当前位置:   article > 正文

Unity3D---通过Shader实现美颜_unity 美颜

unity 美颜

 

视频类APP没有美颜功能大概没人会用吧?

鉴于此,有点心血来潮,打算用Unity实现简单的美颜。

真正的商业级美颜算法是很复杂的,经过无数次打磨才成型,包括磨皮、美白、瘦脸、大眼等等细节,我这里只是用Unity3D的后处理技术做个简单的磨皮、美白。

首先,美颜一般针对的是脸部区域,我们得先识别出脸部区域。

完整的人脸识别算法,这在Unity3D中实现起来有点困难(不借助SDK),因此我们需要换个思路,仔细想想,我们也没必要进行人脸识别,我们大可识别出肤色区域,然后在肤色区域进行美颜。

  1. Shader "Extand/Face/SkinCheck"
  2. {
  3. Properties
  4. {
  5. _MainTex ("Texture", 2D) = "white" {}
  6. }
  7. SubShader
  8. {
  9. Tags { "RenderType"="Opaque" }
  10. LOD 100
  11. Pass
  12. {
  13. CGPROGRAM
  14. #pragma vertex vert
  15. #pragma fragment frag
  16. #include "UnityCG.cginc"
  17. struct appdata
  18. {
  19. float4 vertex : POSITION;
  20. float2 uv : TEXCOORD0;
  21. };
  22. struct v2f
  23. {
  24. float2 uv : TEXCOORD0;
  25. float4 vertex : SV_POSITION;
  26. };
  27. sampler2D _MainTex;
  28. v2f vert (appdata v)
  29. {
  30. v2f o;
  31. o.vertex = UnityObjectToClipPos(v.vertex);
  32. o.uv = v.uv;
  33. return o;
  34. }
  35. fixed4 check(fixed4 col)
  36. {
  37. //使用的是ycbcr颜色模型,一般肤色会在这个区间内
  38. //也可以使用RGB颜色模型,我试了下,感觉上面更准确
  39. half u = (-0.169 * col.r - 0.331 * col.g + 0.5 * col.b + 0.5) * 255;
  40. half v = (0.5 * col.r - 0.419 * col.g - 0.081 * col.b + 0.5) * 255;
  41. fixed t1 = saturate(sign(u - 80));
  42. fixed t2 = saturate(sign(121 - u));
  43. fixed t3 = saturate(sign(v - 124));
  44. fixed t4 = saturate(sign(175 - v));
  45. //肤色区域 t=1
  46. fixed t = sign(t1 * t2 * t3 * t4);
  47. //只显示肤色区域
  48. //return col * t;
  49. //记录下肤色区域 t = 1
  50. return fixed4(col.rgb, t);
  51. }
  52. fixed4 frag (v2f i) : SV_Target
  53. {
  54. fixed4 col = tex2D(_MainTex, i.uv);
  55. return check(col);
  56. }
  57. ENDCG
  58. }
  59. Pass
  60. {
  61. //降噪;
  62. CGPROGRAM
  63. #pragma vertex vert
  64. #pragma fragment frag
  65. #include "UnityCG.cginc"
  66. struct appdata
  67. {
  68. float4 vertex : POSITION;
  69. float2 uv : TEXCOORD0;
  70. };
  71. struct v2f
  72. {
  73. float2 uv[9] : TEXCOORD0;
  74. float4 vertex : SV_POSITION;
  75. };
  76. sampler2D _MainTex;
  77. fixed4 _MainTex_TexelSize;
  78. v2f vert (appdata v)
  79. {
  80. v2f o;
  81. o.vertex = UnityObjectToClipPos(v.vertex);
  82. half size = 1;
  83. for(int m = 0; m < 2; m++)
  84. {
  85. for(int n = 0; n < 2; n++)
  86. {
  87. float x = _MainTex_TexelSize.x * (n - 1);
  88. float y = _MainTex_TexelSize.y * (1 - m);
  89. o.uv[m*3+n] = v.uv + float2(x, y) * size;
  90. }
  91. }
  92. return o;
  93. }
  94. fixed4 frag (v2f i) : SV_Target
  95. {
  96. fixed4 color = tex2D(_MainTex, i.uv[4]);
  97. half alpha = 0;
  98. for(int m = 0; m < 2; m++)
  99. {
  100. for(int n = 0; n < 2; n++)
  101. {
  102. fixed4 col = tex2D(_MainTex, i.uv[m*3+n]);
  103. alpha += col.a;
  104. }
  105. }
  106. half a0 = saturate((alpha - color.a - 0.5) * 10);//周围全黑;
  107. half a1 = 1 - saturate((alpha - color.a - 7.5) * 10);//周围全白;
  108. return color * a0 * a1;
  109. //return fixed4(color.rgb, color.a * a0 * a1);
  110. }
  111. ENDCG
  112. }
  113. Pass
  114. {
  115. //降噪---除去肤色小块;
  116. CGPROGRAM
  117. #pragma vertex vert
  118. #pragma fragment frag
  119. #include "UnityCG.cginc"
  120. struct appdata
  121. {
  122. float4 vertex : POSITION;
  123. float2 uv : TEXCOORD0;
  124. };
  125. struct v2f
  126. {
  127. float2 uv : TEXCOORD0;
  128. float4 vertex : SV_POSITION;
  129. };
  130. sampler2D _MainTex;
  131. fixed4 _MainTex_TexelSize;
  132. v2f vert (appdata v)
  133. {
  134. v2f o;
  135. o.vertex = UnityObjectToClipPos(v.vertex);
  136. o.uv = v.uv;
  137. return o;
  138. }
  139. fixed isskin(v2f i)
  140. {
  141. float r = min(_ScreenParams.x, _ScreenParams.y);
  142. r = round(r * 0.2);
  143. int step = max(1, round(r * 0.1));
  144. half rate = 1;
  145. //向四个方向射出射线;
  146. for(int m = 0; m < 5; m++)
  147. {
  148. half alpha = 0;
  149. half count = 0.01;
  150. for(int n = 0; n < r; n += step)
  151. {
  152. float x = n * ((m + 1) % 2) * sign(1 - m);
  153. float y = n * (m % 2) * sign(2 - m);
  154. count += 1;
  155. alpha += tex2D(_MainTex, i.uv + float2(x * _MainTex_TexelSize.x, y * _MainTex_TexelSize.y)).a;
  156. }
  157. //采样75%都是肤色,说明这个区域是脸部;
  158. rate = rate * saturate((0.9 - alpha / count) * 1000);
  159. }
  160. return 1 - rate;
  161. }
  162. fixed4 frag (v2f i) : SV_Target
  163. {
  164. fixed4 color = tex2D(_MainTex, i.uv);
  165. return color * color.a * isskin(i);
  166. //return fixed4(color.rgb, color.a * rate);
  167. }
  168. ENDCG
  169. }
  170. }
  171. }

最终我们发现,肤色识别算法可以大体识别出肤色区域,但是会有噪点(环境中类似肤色的点被误识别),我们可以继续优化,去除部分噪点,也可以放任不管,毕竟这只是一个Mask, 环境中有少量的点被美颜了也没事。

接下来是磨皮,磨皮的本质是模糊算法,以祛痘祛斑。

模糊算法有很多,我这里主要选择的是双边滤波算法(一种保边算法),高斯模糊作为辅助,相较于一般模糊算法,双边滤波可以在降噪平滑的同时保留边缘的一些细节,至于具体的原理,请同学们自行搜索(懒Orz)

我们直接上核心源码,是不是简单粗暴?

  1. Shader "Extand/Effect/BilateralFilters"
  2. {
  3. Properties
  4. {
  5. _MainTex ("Texture", 2D) = "white" {}
  6. _BlurSize("BlurSize", Range(1,12)) = 1
  7. _SigmaS("_SigmaS", Range(1,10)) = 5
  8. _SigmaR("_SigmaR", Range(0.01,1)) = 0.09
  9. }
  10. SubShader
  11. {
  12. Tags { "RenderType"="Opaque" }
  13. LOD 100
  14. Pass
  15. {
  16. CGPROGRAM
  17. #pragma vertex vert
  18. #pragma fragment frag
  19. #include "UnityCG.cginc"
  20. struct appdata
  21. {
  22. float4 vertex : POSITION;
  23. float2 uv : TEXCOORD0;
  24. };
  25. struct v2f
  26. {
  27. float2 uv : TEXCOORD0;
  28. float4 vertex : SV_POSITION;
  29. };
  30. sampler2D _MainTex;
  31. float4 _MainTex_TexelSize;
  32. float _BlurSize;
  33. float _SigmaS;
  34. float _SigmaR;
  35. v2f vert (appdata v)
  36. {
  37. v2f o;
  38. o.vertex = UnityObjectToClipPos(v.vertex);
  39. o.uv = v.uv;
  40. return o;
  41. }
  42. fixed4 bilater(v2f i)
  43. {
  44. half sigmas2 = 2 * _SigmaS * _SigmaS;//函数中常量系数,取5
  45. half sigmar2 = 2 * _SigmaR * _SigmaR;//函数中常量系数,取0.09
  46. half fenzi_r = 0,fenzi_g = 0,fenzi_b = 0;
  47. half fenmu_r = 0, fenmu_g = 0, fenmu_b = 0;
  48. fixed4 col = tex2D(_MainTex,i.uv);
  49. for(int m = 0; m < 5; m++)
  50. {
  51. half mpingfang = pow(m - 2, 2);
  52. for(int n = 0; n < 5; n++)
  53. {
  54. //_BlurSize为模糊级别,数值越大,模糊程度越高,图像失真也越大
  55. fixed4 tcol = tex2D(_MainTex,i.uv + float2(_MainTex_TexelSize.x * (m-2), _MainTex_TexelSize.y * (n-2)) * _BlurSize);
  56. fixed4 ncol = col - tcol;
  57. half npingfang = pow((n-2),2);
  58. half w_s = (mpingfang + npingfang) / sigmas2;
  59. half wr = pow(2.718, -(w_s + ncol.r * ncol.r / sigmar2));//e常量=2.718...
  60. half wg = pow(2.718, -(w_s + ncol.g * ncol.g / sigmar2));
  61. half wb = pow(2.718, -(w_s + ncol.b * ncol.b / sigmar2));
  62. fenmu_r += wr;
  63. fenmu_g += wg;
  64. fenmu_b += wb;
  65. fenzi_r += wr * tcol.r;
  66. fenzi_g += wg * tcol.g;
  67. fenzi_b += wb * tcol.b;
  68. }
  69. }
  70. return fixed4(fenzi_r/fenmu_r, fenzi_g/fenmu_g, fenzi_b/fenmu_b, col.a);
  71. }
  72. fixed4 frag (v2f i) : SV_Target
  73. {
  74. return bilater(i);
  75. }
  76. ENDCG
  77. }
  78. }
  79. FallBack Off
  80. }
  1. Shader "Extand/Effect/GaussBlur"
  2. {
  3. Properties
  4. {
  5. _MainTex ("Texture", 2D) = "white" {}
  6. _BlurSize("BlurSize", Range(1,20)) = 5
  7. }
  8. SubShader
  9. {
  10. Tags { "RenderType"="Opaque" }
  11. LOD 100
  12. Pass
  13. {
  14. CGPROGRAM
  15. #pragma vertex vert
  16. #pragma fragment frag
  17. #include "UnityCG.cginc"
  18. struct appdata
  19. {
  20. float4 vertex : POSITION;
  21. float2 uv : TEXCOORD0;
  22. };
  23. struct v2f
  24. {
  25. float2 uv[5] : TEXCOORD0;
  26. float4 vertex : SV_POSITION;
  27. };
  28. sampler2D _MainTex;
  29. float4 _MainTex_TexelSize;
  30. float _BlurSize;
  31. v2f vert (appdata v)
  32. {
  33. v2f o;
  34. o.vertex = UnityObjectToClipPos(v.vertex);
  35. o.uv[0] = v.uv;
  36. //高斯-x方向的模糊(y方向同理)
  37. o.uv[1] = v.uv + float2(_MainTex_TexelSize.x * 1, 0) * _BlurSize;//_BlurSize模糊级别
  38. o.uv[2] = v.uv - float2(_MainTex_TexelSize.x * 1, 0) * _BlurSize;
  39. o.uv[3] = v.uv + float2(_MainTex_TexelSize.x * 2, 0) * _BlurSize;
  40. o.uv[4] = v.uv - float2(_MainTex_TexelSize.x * 2, 0) * _BlurSize;
  41. return o;
  42. }
  43. fixed4 frag (v2f i) : SV_Target
  44. {
  45. float weight[3] = {0.4026, 0.2442, 0.0545};
  46. fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
  47. for(int m = 1; m < 3; m++)
  48. {
  49. sum += tex2D(_MainTex, i.uv[m * 2 - 1]).rgb * weight[m];
  50. sum += tex2D(_MainTex, i.uv[m * 2]).rgb * weight[m];
  51. }
  52. return fixed4(sum, 1.0);
  53. }
  54. ENDCG
  55. }
  56. Pass
  57. {
  58. CGPROGRAM
  59. #pragma vertex vert
  60. #pragma fragment frag
  61. #include "UnityCG.cginc"
  62. struct appdata
  63. {
  64. float4 vertex : POSITION;
  65. float2 uv : TEXCOORD0;
  66. };
  67. struct v2f
  68. {
  69. float2 uv[5] : TEXCOORD0;
  70. float4 vertex : SV_POSITION;
  71. };
  72. sampler2D _MainTex;
  73. float4 _MainTex_TexelSize;
  74. float _BlurSize;
  75. v2f vert (appdata v)
  76. {
  77. v2f o;
  78. o.vertex = UnityObjectToClipPos(v.vertex);
  79. o.uv[0] = v.uv;
  80. o.uv[1] = v.uv + float2(0, _MainTex_TexelSize.y * 1) * _BlurSize;
  81. o.uv[2] = v.uv - float2(0, _MainTex_TexelSize.y * 1) * _BlurSize;
  82. o.uv[3] = v.uv + float2(0, _MainTex_TexelSize.y * 2) * _BlurSize;
  83. o.uv[4] = v.uv - float2(0, _MainTex_TexelSize.y * 2) * _BlurSize;
  84. return o;
  85. }
  86. fixed4 frag (v2f i) : SV_Target
  87. {
  88. float weight[3] = {0.4026, 0.2442, 0.0545};
  89. fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
  90. for(int m = 1; m < 3; m++)
  91. {
  92. sum += tex2D(_MainTex, i.uv[m * 2 - 1]).rgb * weight[m];
  93. sum += tex2D(_MainTex, i.uv[m * 2]).rgb * weight[m];
  94. }
  95. return fixed4(sum, 1.0);
  96. }
  97. ENDCG
  98. }
  99. }
  100. FallBack Off
  101. }

再来,美白提亮算法

我也已经很贴心地帮你们把原理说明找到了

核心源码技术点就是这些,我们只需要将这些整合一下,即可形成一个完整的美颜功能。

  1. Shader "Extand/Face/Beauty"
  2. {
  3. Properties
  4. {
  5. _MainTex ("Texture", 2D) = "white" {}
  6. _BilateralTex ("_BlurTex", 2D) = "white" {}
  7. _GaussTex ("GaussTex", 2D) = "white" {}
  8. _SkinTex ("SkinTex", 2D) = "white" {}
  9. _SkinWhite("SkinWhite", Range(0,1)) = 0
  10. }
  11. SubShader
  12. {
  13. Tags { "RenderType"="Opaque" }
  14. LOD 100
  15. Pass
  16. {
  17. CGPROGRAM
  18. #pragma vertex vert
  19. #pragma fragment frag
  20. #include "UnityCG.cginc"
  21. struct appdata
  22. {
  23. float4 vertex : POSITION;
  24. float2 uv : TEXCOORD0;
  25. };
  26. struct v2f
  27. {
  28. float2 uv : TEXCOORD0;
  29. float4 vertex : SV_POSITION;
  30. };
  31. sampler2D _MainTex;
  32. float4 _BlurTex_TexelSize;
  33. sampler2D _BlurTex;
  34. sampler2D _GaussTex;
  35. sampler2D _SkinTex;
  36. float _SkinWhite;
  37. v2f vert (appdata v)
  38. {
  39. v2f o;
  40. o.vertex = UnityObjectToClipPos(v.vertex);
  41. o.uv = v.uv;
  42. return o;
  43. }
  44. fixed4 skin(fixed4 col)
  45. {
  46. half u = (-0.169 * col.r - 0.331 * col.g + 0.5 * col.b + 0.5) * 255;
  47. half v = (0.5 * col.r - 0.419 * col.g - 0.081 * col.b + 0.5) * 255;
  48. fixed t1 = saturate(sign(u - 80));
  49. fixed t2 = saturate(sign(121 - u));
  50. fixed t3 = saturate(sign(v - 124));
  51. fixed t4 = saturate(sign(175 - v));
  52. fixed t = sign(t1 * t2 * t3 * t4);
  53. return fixed4(col.r, col.g, col.b, t);
  54. }
  55. half luminance(fixed4 color){
  56. return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
  57. }
  58. fixed4 bright(fixed4 col)
  59. {
  60. //美颜提亮算法
  61. half BrightLevel = 5;
  62. half3 temp = (0,0,0);
  63. temp.x = log(col.r * (BrightLevel - 1) + 1) / log(BrightLevel);
  64. temp.y = log(col.g * (BrightLevel - 1) + 1) / log(BrightLevel);
  65. temp.z = log(col.b * (BrightLevel - 1) + 1) / log(BrightLevel);
  66. return fixed4(temp, col.a);
  67. }
  68. fixed4 frag (v2f i) : SV_Target
  69. {
  70. fixed4 col = tex2D(_MainTex, i.uv); //原图
  71. fixed4 cskin = tex2D(_SkinTex, i.uv); //肤色Mask
  72. fixed4 bilater = tex2D(_BlurTex, i.uv); //双边过滤
  73. fixed4 gauss = tex2D(_GaussTex, i.uv); //高斯模糊
  74. //按照我们的设想,只需要对肤色区域进行双边过滤,再提亮即可完成美颜
  75. //而实际上,这样做的效果不算理想,因为双边过滤算法虽然是保边算法,但它不可能做到绝对保边
  76. //因此,我们需要再给模糊后的纹理,增加脸部细节
  77. //主要算法原理:
  78. //1.原图 = 模糊 + 细节 ---> 细节 = 原图 - 模糊
  79. //2.增强 = 模糊 + 细节 * k
  80. //这一步具有很强的主观性,是试出来的
  81. //0.2 * (col - bilater) 是取原图双边过滤剩下的细节
  82. //0.8 * (bilater - gauss) 是取原图双边过滤再高斯模糊剩下的细节
  83. half4 nblur = bilater + 0.2 * (col - bilater) + 0.8 * (bilater - gauss);
  84. nblur.r = saturate(nblur.r);//防止颜色值溢出
  85. nblur.g = saturate(nblur.g);
  86. nblur.b = saturate(nblur.b);
  87. //使用肤色Mask,如果是肤色区域,即取模糊值,否则取原图
  88. fixed4 final = lerp(col, fixed4(nblur.rgb,1) , cskin.a);
  89. //提亮
  90. fixed4 cbright = bright(final);
  91. //根据提亮级别插值
  92. final = lerp(final, cbright , _SkinWhite);
  93. final.a = 1;
  94. return final;
  95. }
  96. ENDCG
  97. }
  98. }
  99. FallBack Off
  100. }

搞定收工!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/123503?site
推荐阅读
相关标签
  

闽ICP备14008679号