Wait, for the people for whom the shader doesn’t work, are you all using OpenGL (ie: Linux / Mac)? I’m running Windows 10 and it works for me.
* switches editor to OpenGL
Yep, it’s broken in OpenGL. Likely a bug somewhere in the HLSL to GLSL conversion. It should probably be reported like @DominoM suggested.
However, for this particular case you’re likely better off calculating the UV offsets in the fragment shader like this:
Shader "GW/EdgeDetectionShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_EdgeOnly ("Edge Only",Float) = 1.0
_EdgeColor("Edge Color",Color) = (0,0,0,1)
_BackgroundColor("Background Color",Color) = (1,1,1,1)
}
SubShader
{
Pass
{
ZTest Always Cull Off ZWrite Off
CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
sampler2D _MainTex;
half4 _MainTex_TexelSize;
fixed _EdgeOnly;
fixed4 _EdgeColor;
fixed4 _BackgroundColor;
struct v2f
{
half2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
fixed luminance(fixed4 color){
return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
}
half Sobel(half2 uvs[9]){
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,uvs[it]));
edgeX += texColor * Gx[it];
edgeY += texColor * Gy[it];
}
half edge = 1 - abs(edgeX) - abs(edgeY);
return edge;
}
half Prewitt(half2 uvs[9]){
const half Gx[9] = {-1,-1,-1,
0,0,0,
1,1,1};
const half Gy[9] = {-1,0,1,
-1,0,1,
-1,0,1};
half texColor;
half edgeX = 0;
half edgeY = 0;
for(int it = 0;it<9;it ++){
texColor = luminance(tex2D(_MainTex,uvs[it]));
edgeX += texColor * Gx[it];
edgeY += texColor * Gy[it];
}
half edge = 1 - (abs(edgeX) + abs(edgeY));
return edge;
}
v2f vert (appdata_img v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
half2 uvs[9];
uvs[0] = i.uv + _MainTex_TexelSize.xy * half2(-1,-1);
uvs[1] = i.uv + _MainTex_TexelSize.xy * half2(0,-1);
uvs[2] = i.uv + _MainTex_TexelSize.xy * half2(1,-1);
uvs[3] = i.uv + _MainTex_TexelSize.xy * half2(-1,0);
uvs[4] = i.uv + _MainTex_TexelSize.xy * half2(0,0);
uvs[5] = i.uv + _MainTex_TexelSize.xy * half2(1,0);
uvs[6] = i.uv + _MainTex_TexelSize.xy * half2(-1,1);
uvs[7] = i.uv + _MainTex_TexelSize.xy * half2(0,1);
uvs[8] = i.uv + _MainTex_TexelSize.xy * half2(1,1);
// half edge = Sobel(uvs);
half edge = Prewitt(uvs);
fixed4 withEdgeColor = lerp(_EdgeColor,tex2D(_MainTex,uvs[4]),edge);
fixed4 onlyEdgeColor = lerp(_EdgeColor,_BackgroundColor,edge);
return lerp(withEdgeColor,onlyEdgeColor,_EdgeOnly);
}
ENDCG
}
}
FallBack Off
}
It might seem weird to do all of that extra work in the fragment shader, but the reality is it only trivially increases the cost of the fragment shader, but saves a ton on interpolation. Passing data from the vertex shader to the fragment shader is not free, and you’re passing a ton of data that is trivial to calculate.
The estimates I’ve seen from ~2012 was the cost of transferring a single float4 from vertex to fragment was about the same cost as ~2 instructions in the shader. This is trading 4~8 float4s (because DirectX 11 treats a half2 as a float4 and throws away the last two components in the fragment) for ~6 additional instructions in the fragment. On modern GCN based AMD GPUs it might even be a better ratio since the interpolation is done in the fragment shader. On mobile GPUs have really low memory bandwidth which is where most of the cost of the vertex to fragment shader is, so it should be even better there too.