【Shader】后处理之屏幕映射

一、计算屏幕空间坐标

        一个模型的MVP变换,即从模型空间(M)转换到观察空间(V)再转换到裁剪空间(P),然后将投影完成的标准立方体通过屏幕映射(视口变换)转换成屏幕坐标进行显示。

        下图即标准立方体到屏幕空间的映射过程。

【Shader】后处理之屏幕映射_第1张图片

  • Clip Space → Screen Space的基本方法 
v2f vert (appdata v)
{
    o.screen_pos = o.pos;
    //_ProjectionParams.x用于校正不同平台下的uv坐标系
    o.screen_pos.y = o.screen_pos.y * _ProjectionParams.x;
}

half4 frag (v2f i) : SV_Target
{
    //透视除法,将裁剪空间坐标从[-w,w]变换到屏幕空间坐标[-1,1]
    half2 screen_uv = i.screen_pos.xy / (i.screen_pos.w + 0.000001);
    //重构屏幕uv坐标到[0,1]
    screen_uv = (screen_uv + 1.0) * 0.5;
    half4 col = tex2D(_MainTex, screen_uv);
    return col;
}

  • 通过Unity内置函数实现Clip Space → Screen Space
v2f vert (appdata v)
{
    //处理跨平台所引起的坐标差异问题(包括屏幕翻转与缩放)
    o.screen_pos = ComputeScreenPos(o.pos);
}

half4 frag (v2f i) : SV_Target
{
    //透视除法,将裁剪空间坐标从[-w,w]变换到屏幕空间坐标[0,1]
    half2 screen_uv = i.screen_pos.xy / (i.screen_pos.w + 0.000001);
    half4 col = tex2D(_MainTex, screen_uv);
    return col;
}

  •  当贴图uv(1:1)与屏幕uv(eg:4:3)不匹配时要如何纠正? → 利用屏幕空间uv对贴图uv进行缩放
//纠正贴图uv与屏幕uv的不匹配
//_ScreenParams.x = width, _ScreenParams.y = height, _ScreenParams.z = 1 + 1.0 / width, _ScreenParams.z = 1 + 1.0 / height
half aspect = _ScreenParams.x / _ScreenParams.y;
half2 mask_uv = i.uv * _Mask_ST.xy + _Mask_ST.zw;
mask_uv.x = mask_uv.x * aspect;
half mask = tex2D(_Mask, glass_uv).r;
final_RGB = lerp(final_RGB, _MaskIntensity, mask);

  •  如何解决增加法线贴图后边缘像素被拉伸的问题? → 做一个边缘mask来限制边缘像素的值
half3 normal = UnpackNormal(tex2D(_GlassNormal, glass_uv));
half2 distance = 1.0 - smoothstep(0.98, 1, abs(i.uv * 2.0 - 1.0));
half vfactor = distance.x * distance.y;
half2 uv_distort = i.uv + normal.xy * _Distort * vfactor;
half4 col = tex2D(_MainTex, uv_distort);

  • 如何解决图片随着偏移值一起滑动的问题? → 对法线区域做一个mask
//抠出法线mask
half2 distance_mask = step(0.005, abs(glass_normal.xy));
half mask_data = distance_mask.x * distance_mask.y;
half2 uv_distort = i.uv + normal.xy * _Distort * vfactor * mask_data;
half4 col = tex2D(_MainTex, uv_distort);

二、后处理Shader的构建

        首先需要给Camera挂上后处理的C#脚本:

public class EasyImageEffect : MonoBehaviour
{
    public Material mat;
    public float _Brightness = 1;
    public float _Saturation = 1;
    public float _Contrast = 1;
    [Range(0.05f,3.0f)]
    public float _VignetteIntensity = 1.5f;
    [Range(0.05f,5.0f)]
    public float _VignetteRoundness = 5.0f;
    [Range(0.05f,5.0f)]
    public float _VignetteSmoothness = 5.0f;

    void Start()
    {
        //初始化操作
        if (mat == null || mat.shader == false || mat.shader.isSupported == false)
        {
            enabled = false;
            return;
        }
    }

    private void OnRenderImage(RenderTexture src, RenderTexture dest) 
    {
        mat.SetFloat("_Brightness", _Brightness);
        mat.SetFloat("_Saturation", _Saturation);
        mat.SetFloat("_Contrast", _Contrast);
        mat.SetFloat("_VignetteIntensity", _VignetteIntensity);
        mat.SetFloat("_VignetteRoundness", _VignetteRoundness);
        mat.SetFloat("_VignetteSmoothness", _VignetteSmoothness);
        Graphics.Blit(src, dest, mat, 0);
    }
}

        然后创建后处理Shader:  

  • ASE实现后处理shader所具有的亮度、饱和度、对比度、色相效果

【Shader】后处理之屏幕映射_第2张图片

  •  代码实现后处理shader所具有的亮度、饱和度、对比度、暗角效果
SubShader
{
    // No culling or depth
    Cull Off ZWrite Off ZTest Always

    Pass
    {
        CGPROGRAM
        #pragma vertex vert_img
        #pragma fragment frag

        #include "UnityCG.cginc"


        sampler2D _MainTex;
        float _Brightness;
        float _Saturation;
        float _Contrast;
        float _VignetteIntensity;
        float _VignetteRoundness;
        float _VignetteSmoothness;

        half4 frag (v2f_img i) : SV_Target
        {
            half4 col = tex2D(_MainTex, i.uv);

            //亮度
            half3 final_RGB = col * _Brightness;

            //饱和度
            //gamma空间求明度
            half lumin = dot(final_RGB, half3(0.22, 0.707, 0.071));
            //linear空间求明度
            // half lumin = dot(final_RGB, half3(0.0396, 0.458, 0.0061));
            final_RGB = lerp(lumin, final_RGB, _Saturation);

            //对比度
            half3 mid_point = half3(0.5, 0.5, 0.5);
            final_RGB = lerp(mid_point, final_RGB, _Contrast);

            //暗角
            half2 distance = abs(i.uv - half2(0.5, 0.5)) * _VignetteIntensity;
            distance = pow(saturate(distance), _VignetteRoundness);
            half dis_length = length(distance);
            half vfactor = pow(saturate(1.0 - dis_length * dis_length), _VignetteSmoothness);
            final_RGB = lerp(0.02, final_RGB, vfactor);

            return half4(final_RGB, col.a);
         }
         ENDCG
    }
}

你可能感兴趣的:(UnityShader,unity,shader)