一、原理
参考博文
Unity Shader 实现磨皮效果
Bilateral Filters(双边滤波算法)原理及实现(二)
对皮肤美白算法的一些研究
美白公式
效果:图像两端(明-暗)变化弱,中间变化强。
w(x,y)为输入的像素颜色,v(x,y)为输出的像素颜色,beta为调节参数。
双边滤波公式
效果:保边去噪
(高斯滤波函数)
g(i,j)为输出的像素颜色,f(k,l)为当前输入的像素颜色,S(i,j)是指以(i,j)为中心的(2N+1)*(2N+1)的卷积核覆盖的像素区域。
二、Shader
//一个简单美颜Shader
//功能:磨皮、美白
Shader "Custom/BeautyFaceShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
//模糊大小 (值越大越模糊)
_Radius ("Radius", Range(0, 10)) = 5
//调节距离空间权重,值越大权重越大
_SigmaS("SigmaS", float) = 3
//调节颜色空间权重,值越大权重越大
_SigmaR("SigmaR", float) = 1
//亮度值
_Brightness("Brightness", float) = 20
//对比度
_Contrast("Contrast", float) = 1.18
//饱和度
_Saturation("Saturation", float) = 1
}
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 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
half4 _MainTex_TexelSize;
float _Radius;
float _SigmaS;
float _SigmaR;
float _Brightness;
float _Contrast;
float _Saturation;
//计算像素亮度值
float Luminance(float3 color)
{
return dot(color, float3(0.2125, 0.7154, 0.0721));
}
//提亮暗部区域
float3 Brightness(float3 col)
{
//此公式: 使图像暗部变化强,亮部变化弱
//col.r = log(col.r * _Brightness);
//col.g = log(col.g * _Brightness);
//col.b = log(col.b * _Brightness);
//原理: https://blog.csdn.net/weixin_33716557/article/details/86197248?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-7.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-7.control
//此公式: 使图像两端变化弱,中间变化强
col.r = log(col.r * (_Brightness - 1) + 1) / log(_Brightness);
col.g = log(col.g * (_Brightness - 1) + 1) / log(_Brightness);
col.b = log(col.b * (_Brightness - 1) + 1) / log(_Brightness);
return col;
}
//原理: https://blog.csdn.net/u013066730/article/details/87919412
//转载: https://www.jianshu.com/p/90feece27a04?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation
//双边滤波算法-保边去噪
float3 BilateralFilter(float2 uv)
{
float i = uv.x;
float j = uv.y;
float sigmaSSquareMult2 = (2 * _SigmaS * _SigmaS);
float sigmaRSquareMult2 = (2 * _SigmaR * _SigmaR);
float3 centerCol = tex2D(_MainTex, uv).rgb; // 中心点像素的颜色 //
float centerLum = Luminance(centerCol); // 中心点像素的亮度 //
float3 sum_up; // 分子 //
float3 sum_down; // 分母 //
for (int k = -_Radius; k <= _Radius; k++)
{
for (int l = -_Radius; l <= _Radius; l++)
{
float2 uv_new = uv + _MainTex_TexelSize.xy * float2(k, l);
float3 curCol = tex2D(_MainTex, uv_new).rgb; // 当前像素的颜色 //
float curLum = Luminance(curCol); // 当前像素的亮度 //
float3 deltaColor = curCol - centerCol;
float len = dot(deltaColor, deltaColor);
// float exponent = -((i-k)*(i-k)+(j-l)*(j-l))/sigmaSSquareMult2 - (curLum-centerLum)*(curLum-centerLum)/sigmaRSquareMult2;
float exponent = -((i - k) * (i - k) + (j - l) * (j - l)) / sigmaSSquareMult2 - len / sigmaRSquareMult2;
float weight = exp(exponent);
sum_up += curCol * weight;
sum_down += weight;
}
}
float3 rgb = sum_up / sum_down;
return rgb;
}
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
float4 frag(v2f i) : SV_Target
{
//保边去噪
float3 rgb = BilateralFilter(i.uv);
//该像素对应的亮度值
fixed luminance = Luminance(rgb);
//使用该亮度值创建一个饱和度为0的颜色
fixed3 luminanceColor = fixed3(luminance, luminance, luminance);
//创建一个对比度度为0的颜色
fixed3 avgColor = fixed3(0.5, 0.5, 0.5);
//调整饱和度
rgb = lerp(luminanceColor, rgb, _Saturation);
//调整对比度
rgb = lerp(avgColor, rgb, _Contrast);
//调整亮度
rgb = Brightness(rgb);
return fixed4(rgb, 1);
}
ENDCG
}
}
}
效果