实现原理:在渲染前,先把需要扰动的物体的背景存成一张图,再把屏幕坐标UV把这张图贴回去
获取背景纹理的方法:
- GrabPass:方便,但贵
- CommandBuffer:前Srp时代管线自定义方法;
- Lwrp/Urp:后Srp时代管线自定义方法(推荐);
输入结构,不用改;
输出结构,追加grabPos用于背景纹理采样;
顶点Shader,对应追加grabPos的计算方法:
像素Shader:
- 对MainTex采样;
- 将MainTex某通道或者灰度作为扰动背景纹理UV的源,
- 扰动背景纹理UV;
- 用扰动后的背景纹理UV对背景采样;
- 计算finalRGB,opacity;
- 返回值;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Shader "Custom/ScreenWarp" {
Properties {
_MainTex ("RGB:颜色 A:透贴", 2d) = "gray"{}
_Opacity ("不透明度", range(0, 1)) = 0.5
_WarpMidVal ("扰动中间值", range(0, 1)) = 0.5
_WarpInt ("扰动强度", range(0, 5)) = 1
}
SubShader {
Tags {
"Queue"="Transparent" // 调整渲染顺序
"RenderType"="Transparent" // 对应改为Cutout
"ForceNoShadowCasting"="True" // 关闭阴影投射
"IgnoreProjector"="True" // 不响应投射器
}
获取背景纹理
1
2
3
GrabPass {
"_BGTex"
}
Forward Pass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
Blend One OneMinusSrcAlpha // 混合方式
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
// 输入参数
uniform sampler2D _MainTex;
uniform half _Opacity;
uniform half _WarpMidVal;
uniform half _WarpInt;
uniform sampler2D _BGTex; // 拿到背景纹理
// 输入结构
struct VertexInput {
float4 vertex : POSITION; // 顶点位置 总是必要
float2 uv : TEXCOORD0; // UV信息 采样贴图用
};
// 输出结构
struct VertexOutput {
float4 pos : SV_POSITION; // 顶点位置 总是必要
float2 uv : TEXCOORD0; // UV信息 采样贴图用
float4 grabPos : TEXCOORD1; // 背景纹理采样坐标
};
// 输入结构>>>顶点Shader>>>输出结构
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
o.pos = UnityObjectToClipPos( v.vertex); // 顶点位置 OS>CS
o.uv = v.uv; // UV信息
o.grabPos = ComputeGrabScreenPos(o.pos); // 背景纹理采样坐标
return o;
}
// 输出结构>>>像素
half4 frag(VertexOutput i) : COLOR {
// 采样 基本纹理 RGB颜色 A透贴
half4 var_MainTex = tex2D(_MainTex, i.uv);
// 扰动背景纹理采样UV
i.grabPos.xy += (var_MainTex.r - _WarpMidVal) * _WarpInt;
// 采样背景
half3 var_BGTex = tex2Dproj(_BGTex, i.grabPos).rgb;
// FinalRGB 不透明度
half3 finalRGB = lerp(1.0, var_MainTex.rgb, _Opacity) * var_BGTex;
half opacity = var_MainTex.a;
// 返回值
return half4(finalRGB * opacity, opacity);
}
ENDCG
}
}
}