shader_实现放大镜功能
需求
放大镜镜头内的区域需要有放大的效果;成品Gif如下:
分析
shader编写分析:
- 先实现整体放大效果
- 最后在一定范围内放大(这里是圆)
- 需要实时获得放大镜的中心点
- 需要知道放大的强度,和放大镜的大小,边缘需要有一个边缘强化
因为要实时从获取屏幕中获取放大镜的中心点,所以这一块需要用到屏幕后处理。
(屏幕后处理,顾名思义,通常指的是在渲染完整个场景得到屏幕图像后,在对这个图像进行一系列的操作,实现各种屏幕特效;Unity也提供了一个接口——OnRenderImage)
C#脚本的编写
首先是重写Unity提供的接口:
// 放大强度
[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 = Vector2.zero;
// 渲染屏幕
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函数使用特定的UnityShader来对当前图像进行处理,再把返回的渲染纹理显示到屏幕上
Graphics.Blit (source, destination, material);
} else {
Graphics.Blit (source, destination);
}
}
因为放大镜在屏幕中的位置在不断的变换,所以需要在Update函数中不断的获取放大镜的位置:
public void Update ()
{
if (!isStartEnlarge) return;
if (tool == null) return;
Vector2 mousePos = GameCamera.Ins.gameCamera.WorldToScreenPoint(tool.position);
//将mousePos转化为(0,1)区间
pos = new Vector2 (mousePos.x / Screen.width, mousePos.y / Screen.height);
}
C#全部代码如下:
MagnifyingGlass类:
using Logic.Global;
using UnityEngine;
public class MagnifyingGlass : PostEffectsBase
{
//放大的工具
public Transform tool;
//是否开始放大
public bool isStartEnlarge = false;
// shader
private Shader myShader;
//材质
private Material mat = null;
public Material material {
get {
// 检查着色器并创建材质
mat = CheckShaderAndCreateMaterial(myShader, ref 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 = Vector2.zero;
void Start () {
//找到对应的Shader文件
myShader = shader;
if(myShader == null)
myShader = Shader.Find ("Custom/MagnifyingGlass");
}
// 渲染屏幕
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函数使用特定的UnityShader来对当前图像进行处理,再把返回的渲染纹理显示到屏幕上
Graphics.Blit (source, destination, material);
} else {
Graphics.Blit (source, destination);
}
}
public void Update ()
{
if (!isStartEnlarge) return;
if (tool == null) return;
Vector2 mousePos = GameCamera.Ins.gameCamera.WorldToScreenPoint(tool.position);
//将mousePos转化为(0,1)区间
pos = new Vector2 (mousePos.x / Screen.width, mousePos.y / Screen.height);
}
}
PostEffectsBase类:
using UnityEngine;
//提供一个后处理的基类,主要功能在于直接通过Inspector面板拖入shader,生成shader对应的材质
public class PostEffectsBase : MonoBehaviour
{
//Inspector面板上直接拖入
public Shader shader = null;
private Material _material = null;
/// <summary>
/// 检测需要渲染的Shader可用性,然后返回使用了该shader的material
/// </summary>
/// <param name="shader">指定shader</param>
/// <param name="material">创建的材质</param>
/// <returns>得到指定shader的材质</returns>
protected Material CheckShaderAndCreateMaterial(Shader shader, ref Material material)
{
if (shader == null || !shader.isSupported)
return null;
if (material && material.shader == shader)
return material;
material = new Material(shader);
material.hideFlags = HideFlags.DontSave;
return material;
}
}
放大镜的shader
// ---------------------------【放大镜特效】---------------------------
Shader "Custom/MagnifyingGlass"
{
// ---------------------------【属性】---------------------------
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) // 接受类型为VertexInput的参数,返回类型为VertexOutput
{
VertexOutput o; // 定义一个VertexOutput类型的变量o
o.vertex = UnityObjectToClipPos(v.vertex); // 将顶点坐标从对象空间转换到剪辑空间
o.uv = v.uv; // 将纹理坐标复制到输出结构
return o; // 返回VertexOutput类型的结构体
}
// ---------------------------【片元着色器】---------------------------
fixed4 frag(VertexOutput i) : SV_Target // 接受VertexOutput类型的参数,指定输出颜色类型为fixed4
{
float2 scale = float2(_ScreenParams.x / _ScreenParams.y, 1); // 计算屏幕长宽比的缩放因子
float2 center = _Pos; // 放大区域的中心点
float2 dir = center - i.uv; // 当前像素到中心点的距离向量
float dis = length(dir * scale); // 当前像素到中心点的距离
float atZoomArea = smoothstep(_Size + _EdgeFactor, _Size, dis); // 根据距离判断是否在放大区域内
fixed4 col = tex2D(_MainTex, i.uv + dir * _ZoomFactor * atZoomArea); // 根据放大程度计算新的坐标位置,并获取对应的颜色值
return col; // 返回颜色值
}
ENDCG
}
}
}