Unity Shader - 放大鏡

前言

今天閒來無事,實現了一個簡單的放大鏡特效。

效果圖如下:

在這裏插入圖片描述

思路

思路其實很簡單,大致分爲兩個步驟:

  1. 先實現整體放大效果;
  2. 最後在一定範圍內放大(這裏是圓)

既然是放大鏡,那也就是對圖像的處理, 這裏我們就需要用到後期處理了,
在Camera上面掛一個c#腳本來捕獲需要渲染的圖像,然後通過shader處理後渲染。

實現

1.首先我們來實現一下整體放大的效果:

思路非常簡單:沿着 中心點到當前像素點的方向 採樣像素點即可, 採樣的距離越大, 縮放率就越大。

這裏就貼一下關鍵代碼,完整代碼在文章最後會貼出來。

關鍵代碼如下:

// ---------------------------【片元着色器】---------------------------
fixed4 frag (VertexOutput i) : SV_Target
{
    // 中心點(鼠標點擊位置,由c#傳過來)
    float2 center = _Pos;
    // 方向
    float2 dir = center-i.uv;
    // 沿着縮放方向 縮放
    fixed4 col = tex2D(_MainTex, i.uv + dir * _ZoomFactor);
    return col;
}

效果如下:
在這裏插入圖片描述

2.然後限制它在一定範圍內縮放:

我們這裏就實現最簡單的,讓它在一個圓的範圍內縮放。

其實我們仔細思考一下,會發現這個也比較簡單,

決定縮放的關鍵代碼無非就是這行:

    fixed4 col = tex2D(_MainTex, i.uv + dir * _ZoomFactor);

只要我們給縮放因子乘以一個控制變量(atZoomArea), 當 atZoomArea = 0 時, 不縮放; 當 atZoomArea = 1 時 縮放。

代碼修改如下:

fixed4 col = tex2D(_MainTex, i.uv + dir * _ZoomFactor * atZoomArea );

現在最關鍵的點來了, 我們怎麼計算出 控制變量(atZoomArea)呢?

這裏就和之前的文章 Unity Shader - 遮罩效果 類似了。

可以通過判斷當前像素點到中心點的距離是否小於等於 圓的半徑來決定是否在圓內。 小於說明在圓內,反之則不在,代碼如下:

// ---------------------------【片元着色器】---------------------------
fixed4 frag (VertexOutput i) : SV_Target
{
    // 中心點(鼠標點擊位置,由c#傳過來)
    float2 center = _Pos;
    // 方向
    float2 dir = center-i.uv;
    //當前像素到中心點的距離
    float dis = length(dir);
    // 是否在放大鏡區域
    fixed atZoomArea = 1-step(_Size,dis);
    // 沿着縮放方向 縮放
    fixed4 col = tex2D(_MainTex, i.uv + dir * _ZoomFactor * atZoomArea);
    return col;
}               

效果如下:

在這裏插入圖片描述

我們發現這個圓不太一樣,是一個橢圓, 這是由於 屏幕寬高不一樣, 然而在uv中, x軸和y軸始終是在[0,1]區間內, 所以造成圓被拉伸成了橢圓。

解決方案如下:

fixed4 frag (VertexOutput i) : SV_Target
{
    //屏幕長寬比 縮放因子
    float2 scale = float2(_ScreenParams.x / _ScreenParams.y, 1);
    // 中心點(鼠標點擊位置,由c#傳過來)
    float2 center = _Pos;
    // 方向
    float2 dir = center-i.uv;
    //當前像素到中心點的距離
    float dis = length(dir*scale);
    // 是否在放大鏡區域
    fixed atZoomArea = 1-step(_Size,dis);
    // 沿着縮放方向 縮放
    fixed4 col = tex2D(_MainTex, i.uv + dir * _ZoomFactor * atZoomArea);
    return col;
}   

如圖:

在這裏插入圖片描述

以上,基本的功能已經實現了, 其實我們還可以優化一下, 我們仔細看這個圓的邊緣鋸齒感比較嚴重, 我們可以使之更加平滑!

可以用 smoothstep 函數代替 step 達到平滑的效果。

// fixed atZoomArea = 1-step(_Size,dis);
float atZoomArea = smoothstep(_Size + _EdgeFactor,_Size,dis );

最終效果如下:

在這裏插入圖片描述

最終完整代碼如下:

C#:

PostEffectsBase 基類可以在這裏獲取(來自《Unity Shader入門精要》)

// create by 長生但酒狂
// create time 2020.4.8
// ---------------------------【放大鏡特效】---------------------------

using UnityEngine;
public class Zoom : PostEffectsBase {
    // shader
    public Shader myShader;
    //材質 
    private Material mat = null;
    public Material material {
        get {
            // 檢查着色器並創建材質
            mat = CheckShaderAndCreateMaterial (myShader, mat);
            return mat;
        }
    }

    // 放大強度
    [Range (-2.0f, 2.0f), Tooltip ("放大強度")]
    public float zoomFactor = 0.4f;

     // 放大鏡大小
    [Range (0.0f, 0.2f), Tooltip ("放大鏡大小")]
    public float size = 0.15f;

    // 凸鏡邊緣強度
    [Range (0.0001f, 0.1f), Tooltip ("凸鏡邊緣強度")]
    public float edgeFactor = 0.05f;

    // 遮罩中心位置
    private Vector2 pos = new Vector4 (0.5f, 0.5f);

    void Start () {
        //找到對應的Shader文件  
        myShader = Shader.Find ("lcl/screenEffect/Zoom");
    }

    // 渲染屏幕
    void OnRenderImage (RenderTexture source, RenderTexture destination) {
        if (material) {
            // 把鼠標座標傳遞給Shader
            material.SetVector ("_Pos", pos);
            material.SetFloat ("_ZoomFactor", zoomFactor);
            material.SetFloat ("_EdgeFactor", edgeFactor);
            material.SetFloat ("_Size", size);
            // 渲染
            Graphics.Blit (source, destination, material);
        } else {
            Graphics.Blit (source, destination);
        }
    }

    void Update () {
        if (Input.GetMouseButton (0)) {
            Vector2 mousePos = Input.mousePosition;
            //將mousePos轉化爲(0,1)區間
            pos = new Vector2 (mousePos.x / Screen.width, mousePos.y / Screen.height);
        }
    }
}

Shader:

// create by 長生但酒狂
// create time 2020.4.8
// ---------------------------【放大鏡特效】---------------------------

Shader "lcl/screenEffect/Zoom"
{
    // ---------------------------【屬性】---------------------------
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    // ---------------------------【子着色器】---------------------------
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always
        // ---------------------------【渲染通道】---------------------------
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            //頂點輸入結構體
            struct VertexInput
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            // 頂點輸出結構體
            struct VertexOutput
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };
            
            // 變量申明
            sampler2D _MainTex;
            float2 _Pos;
            float _ZoomFactor;
            float _EdgeFactor;
            float _Size;
            // ---------------------------【頂點着色器】---------------------------
            VertexOutput vert (VertexInput v)
            {
                VertexOutput o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }
            // ---------------------------【片元着色器】---------------------------
            fixed4 frag (VertexOutput i) : SV_Target
            {

                //屏幕長寬比 縮放因子
                float2 scale = float2(_ScreenParams.x / _ScreenParams.y, 1);
                // 放大區域中心
                float2 center = _Pos;
                float2 dir = center-i.uv;
                
                //當前像素到中心點的距離
                float dis = length(dir * scale);
                // 是否在放大鏡區域
                // fixed atZoomArea = 1-step(_Size,dis);
                float atZoomArea = smoothstep(_Size + _EdgeFactor,_Size,dis );

                fixed4 col = tex2D(_MainTex, i.uv + dir * _ZoomFactor * atZoomArea );
                return col;
            }
            ENDCG
        }
    }
}

最後

歡迎來我GitHub點個Star,謝謝!
裏面有我平時學習unity shader過程中實現的一些特效demo。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章