Screen UV Perturbation Refraction Effect

屏幕UV扰动折射效果

Posted by Yoko on June 12, 2020

实现原理:在渲染前,先把需要扰动的物体的背景存成一张图,再把屏幕坐标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
        }
    }
}