[Unity Shader 技术整理]边缘检测的使用(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_26722425/article/details/77341284

  先上一张Gif效果图,屋里的衣柜来一张~

用到的 : 边缘检测Shader + UV移动 + 渐变图遮罩 + 双Pass + EasyAR

完整代码 :

 C# Code 

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151

 

Shader "Unlit/ScannerShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _MaskTex("MaskTex", 2D) = "white" {}
        _EdgeOnly ("Edge Only", Float) = 1.0
        _EdgeColor ("Edgge Color", Color) = (0,0,0,1)
        _BackgroundColor ("Background Color",Color) = (1,1,1,0)
        _Thickness ("Thickness",Float) = 1.0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        cull off
        LOD 100
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0; 
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                    
                float2 maskuv : TEXCOORD0;
                float2 uv[9] : TEXCOORD1;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            sampler2D _MaskTex;
            float4 _MainTex_TexelSize;
            fixed _EdgeOnly;
            fixed4 _EdgeColor;
            fixed4 _BackgroundColor;
            fixed _Thickness;
            
            fixed luminance(fixed4 color)
            {
                return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
            }

            half Sobel(v2f i)
            {
                const half Gx[9] = {-1,-2,-1,0,0,0,1,2,1};
                const half Gy[9] = { -1,0,1,-2,0,2,-1,0,1 };

                half texColor;
                half edgeX = 0;
                half edgeY = 0;
                for (int it = 0; it < 9; it++)
                {
                    texColor = luminance(tex2D(_MainTex, i.uv[it]));
                    edgeX += texColor * Gx[it];
                    edgeY += texColor * Gy[it];
                }

                half edge = 1 - abs(edgeX) - abs(edgeY);
                return edge;
            }

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);

                half2 uv = v.uv;
                
                o.maskuv = v.uv + frac(fixed2(0,-0.7 * _Time.y));

                o.uv[0] = uv + _MainTex_TexelSize.xy * half2(-1, -1) * _Thickness;
                o.uv[1] = uv + _MainTex_TexelSize.xy * half2(0, -1)* _Thickness;
                o.uv[2] = uv + _MainTex_TexelSize.xy * half2(1, -1)* _Thickness;
                o.uv[3] = uv + _MainTex_TexelSize.xy * half2(-10)* _Thickness;
                o.uv[4] = uv + _MainTex_TexelSize.xy * half2(00)* _Thickness;
                o.uv[5] = uv + _MainTex_TexelSize.xy * half2(10)* _Thickness;
                o.uv[6] = uv + _MainTex_TexelSize.xy * half2(-11)* _Thickness;
                o.uv[7] = uv + _MainTex_TexelSize.xy * half2(01)* _Thickness;
                o.uv[8] = uv + _MainTex_TexelSize.xy * half2(11)* _Thickness;

                //o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                half edge = Sobel(i);
                
                fixed4 maskColor = tex2D(_MaskTex, i.maskuv);

                fixed4 withEdgeColor = lerp(_EdgeColor,tex2D(_MainTex, i.uv[4]),edge);
                fixed4 onlyEdgeColor = lerp(_EdgeColor, _BackgroundColor, edge);

                fixed4 col = lerp(withEdgeColor, onlyEdgeColor, _EdgeOnly);
                return fixed4(col.rgb,col.a * (1.0 - maskColor.r));
            }
            ENDCG
        }
    }
}

首先,边缘检测的相关代码,完全抄袭自《Unity Shader入门精要》这本书,没有一点保留完全照搬照抄。没看过的请去买本书看,原理大概就是算卷积,反正我也不会去记这些乱七八糟的东西。

然后开始对边缘检测源码加工...

第一步 :Blend SrcAlpha OneMinusSrcAlpha 开启混合,目的只是为了让背景色可以透明,只保留边缘线的颜色

第二步 :用绘图软件搞一个扫描线遮罩图,用作和边缘线遮罩,让边缘线只在黑色区域可见,图如下

第三步 :让UV动起来,也就是代码中的 maskuv,代码如下

o.maskuv = v.uv + frac(fixed2(0,-0.7 * _Time.y));

将maskuv.y的值沿着y轴向下移,_Time是内置变量,有四个分量,xyzw,他们之间存在一个倍率关系,这里用的是y。

maskuv是作为扫描线颜色的依据,代码如下

fixed4 maskColor = tex2D(_MaskTex, i.maskuv);

至此,就会得到一个黑色扫描线不断向下移动的扫描线遮罩图

第五步 :扫描线遮罩图的灰度和边缘线本身的透明度相乘,因为黑色是0 0 0,白色是1 1 1,而我需要的是黑色地方扫描线显示

假设边缘线的透明度为a,扫描线遮罩图输出的颜色为c,则最终的透明度应该是a(1-c.r),这里用的是r分量,其实gb分量也无所谓,反正是黑白的,三个值都是一样的,代码如下

fixed4(col.rgb,col.a * (1.0 - maskColor.r)); 

col是冯乐乐同学输出的结果,透明度由输出的结果和遮罩共同控制。

第六步 :添加一个_Thinkness参数,作为扫描线的宽度,就是为了美观考虑吧,没啥太大用处,觉得太细可以把值调大点,另外可以随着uv偏移修改颜色,输出一条五彩缤纷的扫描线,反正我懒得做....

第七步 : 去EasyAR官网弄个包,弄个Key,把prefab拖进来,Key粘进去,删除原来的摄像机,blabla...把带有上面Shader的Material拖入到RealityPlane里面,完工

猜你喜欢

转载自blog.csdn.net/qq_26722425/article/details/77341284