前言
今天闲来无事,实现了一个简单的放大镜特效。
效果图如下:
思路
思路其实很简单,大致分为两个步骤:
-
先实现整体放大效果;
-
最后在一定范围内放大(这里是圆)
既然是放大镜,那也就是对图像的处理, 这里我们就需要用到后期处理了, 在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。
7264

被折叠的 条评论
为什么被折叠?



