0
点赞
收藏
分享

微信扫一扫

Unity学习shader笔记[五十六] Unity3D油画滤镜shader的实现

吃面多放酱 2022-05-02 阅读 73

转载自 Unity3D油画滤镜shader的实现

原图
在这里插入图片描述

添加后处理效果后

半径为1
在这里插入图片描述

半径为2
在这里插入图片描述
半径为3
在这里插入图片描述
半径为5
在这里插入图片描述

思路

简单普遍的油画笔刷比较大,一般每一笔之间都有覆盖到其他的之前下的笔的范围,所以覆盖范围能实现两个笔划之间柔和的过度。

做法简单说是

对相机成像图的每个像素
像素往左上,左下,右上,右下角四个方向一定范围内分若干步进,每次步进进行一次采样,每个方向所有采样到的颜色都各自进行以下处理

  1. 每次采样的颜色叠加,
  2. 每次采样的颜色平方后叠加,

在一个方向都采样完之后,将2的结果除以采样次数然后减去1的结果的除以采样次数后的平方,这样做的处理就是方差了。

在普遍的数学原理上来说,方差值越大,形成方差的这些数值之间的差值是越大的,所以方差越大,在某个方向上这些采样的颜色本身之间差值比较大,例如一次采样是白色,一次采样是黑色。

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;
            }
        }

求到方差之后,再用这个颜色的方差求亮度值,这个含义不是很懂。
最后一个像素的四个方向的这四个值进行比较,求最低值。

Luminance亮度公式:
luminance = 0.2125 * Red + 0.7154 * Green + 0.0721 * Blue
这是一个固定的公式,之前自己用rgb相加然后除以3的这个做法是求的灰度值,和亮度值的概念要区分开来。

模糊的主要思想是像素附近的像素的颜色求平均值,只是不同的模糊,求平均值的方式不同,例如高斯模糊和平均模糊。

方差选择差值最低的方向的颜色亮度作为自身颜色,也类似一种模糊,当所有的像素都用了这个做法之后,一个像素周围的像素 ,比较可能他们四周一定范围内最后选择的的是同个方向, 那么这两个像素的颜色最后就类似, 这样的情况多了之后,就实现了柔和的过渡。

对原因的一点点理解,可能表达有问题,目前理解一下油画的做法就好,做法的原因是更深层次的理解了,目前没必要考虑那么深。

代码

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
}

举报

相关推荐

0 条评论