前言
今天閒來無事,實現了一個簡單的放大鏡特效。
效果圖如下:
思路
思路其實很簡單,大致分爲兩個步驟:
- 先實現整體放大效果;
- 最後在一定範圍內放大(這裏是圓)
既然是放大鏡,那也就是對圖像的處理, 這裏我們就需要用到後期處理了,
在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。