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#:
    
// 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。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值