原图
添加后处理效果后
实现思路是对每一个像素点,采样它周围的四块矩形区域的均值,并选取各像素颜色分量色差最小的均值作为该像素点的颜色。从而既可以达到一种模糊效果,又可以对画面进行柔化。
(中间的是像素着色器当前要执行的像素,每个像素需要采样1234四个区域)
具体的计算过程是先计算该像素点周围四块矩形每个矩形区域内的所有像素颜色的均值的平方,接着计算所有像素颜色的平方的均值。然后求差值,那么什么时候这两个值差距最小呢?大家可以列式计算一下,推导也比较复杂,结果就是这一块区域内每个像素的颜色互相更接近的时候。因此比较四块区域的这个差值,选取差值最小的一块作为最后的像素颜色,这样就同时完成了模糊和柔化的效果,也就模拟了油画的效果。
如果用数学语言说的话,那么就是输出多个子区域中颜色方差最小的子区域的颜色均值
完整代码
c#代码
namespace Colorful
{
using UnityEngine;
[ExecuteInEditMode]
public class OilPaint : MonoBehaviour
{
public int Radius = 3;
public Shader shader;
protected void OnRenderImage(RenderTexture source, RenderTexture destination)
{
Material material = new Material(shader);
material.SetInt("_Radius", Radius);
material.SetVector("_PSize", new Vector2(1f / (float) source.width, 1f / (float) source.height));
Graphics.Blit(source, destination, material);
}
}
}
shader代码
Shader "LX/OilPaint"
{
Properties
{
_MainTex ("Base (RGB)", 2D) = "white" {}
_PSize ("Pixel Size (XY)", Vector) = (0,0,0,0)
}
CGINCLUDE
#include "UnityCG.cginc"
#define H3Z half3(0.0, 0.0, 0.0)
sampler2D _MainTex;
half2 _PSize;
int _Radius;
half4 frag(v2f_img i) : SV_Target
{
half3 m0 = H3Z;
half3 m1 = H3Z;
half3 m2 = H3Z;
half3 m3 = H3Z;
half3 s0 = H3Z;
half3 s1 = H3Z;
half3 s2 = H3Z;
half3 s3 = H3Z;
int k, j;
for (j = -_Radius; j <= 0; j++)
{
for (k = -_Radius; k <= 0; k++)
{
half3 c = tex2D(_MainTex, i.uv + half2(k, j) * _PSize).rgb;
m0 += c;
s0 += c * c;
}
}
for (j = -_Radius; j <= 0; j++)
{
for (k = 0; k <= _Radius; k++)
{
half3 c = tex2D(_MainTex, i.uv + half2(k, j) * _PSize).rgb;
m1 += c;
s1 += c * c;
}
}
for (j = 0; j <= _Radius; j++)
{
for (k = 0; k <= _Radius; k++)
{
half3 c = tex2D(_MainTex, i.uv + half2(k, j) * _PSize).rgb;
m2 += c;
s2 += c * c;
}
}
for (j = 0; j <= _Radius; j++)
{
for (k = -_Radius; k <= 0; k++)
{
half3 c = tex2D(_MainTex, i.uv + half2(k, j) * _PSize).rgb;
m3 += c;
s3 += c * c;
}
}
const half n = half((_Radius + 1) * (_Radius + 1));
half minSigma2 = 1;
half3 color = H3Z;
m0 /= n;
s0 = abs(s0 / n - m0 * m0);
half sigma2 = Luminance(s0);
if (sigma2 < minSigma2)
{
minSigma2 = sigma2;
color = m0;
}
m1 /= n;
s1 = abs(s1 / n - m1 * m1);
sigma2 = Luminance(s1);
if (sigma2 < minSigma2)
{
minSigma2 = sigma2;
color = m1;
}
m2 /= n;
s2 = abs(s2 / n - m2 * m2);
sigma2 = Luminance(s2);
if (sigma2 < minSigma2)
{
minSigma2 = sigma2;
color = m2;
}
m3 /= n;
s3 = abs(s3 / n - m3 * m3);
sigma2 = Luminance(s3);
if (sigma2 < minSigma2)
{
minSigma2 = sigma2;
color = m3;
}
return half4(color, 1.0);
}
ENDCG
SubShader
{
ZTest Always Cull Off ZWrite Off
Fog
{
Mode off
}
Pass
{
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
ENDCG
}
}
FallBack off
}
另外代码也传到github仓库里了,大家也可以关注一下哦~
我的github