In my Shader I’m trying to compute the distance between the fragment’s position.xy, and a point’s XY (or optionally, a line segment made of two points). The fragment position is provided by the SV_POSITION parameter of the fragment shader, and the other points are provided via the TEX_COORD1,2 or 3 parameters.
I use a geometry shader to scale all input triangles by a factor of 2 in model space. More importantly the geometry shader passes these points’ original, NOTscaled values, for use in the fragment shader. (It is the storage of the original triangle vertices, that requires a goemetry shader rather than just a vertex shader.)
I’ll get to making better shaped triangles in the future (perpendicular to the camera), but wanted to keep this simple for now.
It works pretty well, but has two major problems:
-
While it looks fine in the scene view, when in game view, or camera preview, the distance computations seems to get different results; it looks like there is some offset I’m not accounting for.
-
When in scene view, and otherwise working ok: should one of the points I’m computing the distance to, fall behind the camera: it makes the distance computations incorrect. I suspect I need to do some additional transformation to the point, if it’s w is negative, but can’t figure out what that transform should be.
Here is the shader code : note that the distance calculations consider only the .xy coordinates of the points it references.
Shader "Unlit/TriTestShader"
{
Properties
{
_GlowRadius("GlowRadius", Float) = 0.05
[Toggle]_useDistanceToEdge ("DistanceToEdge/Corner",int) = 1
}
SubShader
{
Tags { "Queue"="Transparent" "RenderType"="Transparent" "IgnoreProjector"="True" }
LOD 100
Pass
{
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
ZTest LEqual
Lighting Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma geometry GS_Main
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float4 color : COLOR;
};
struct g2f
{
UNITY_VPOS_TYPE vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float4 color : COLOR;
float4 P0 : TEXCOORD1;
float4 P1 : TEXCOORD2;
float4 P2 : TEXCOORD3;
};
float _GlowRadius;
appdata vert (appdata v)
{
return v;
}
[maxvertexcount(3)] //1 tri
void GS_Main(triangle appdata p[3], inout TriangleStream<g2f> pStream)
{
float4 p0 = p[0].vertex;
float4 p1 = p[1].vertex;
float4 p2 = p[2].vertex;
g2f newpoint;
newpoint.P0 = (UnityObjectToClipPos(p0.xyz));
newpoint.P1= (UnityObjectToClipPos(p1.xyz));
newpoint.P2= (UnityObjectToClipPos(p2.xyz));
newpoint.P0 = ComputeScreenPos(newpoint.P0);
newpoint.P1 = ComputeScreenPos(newpoint.P1);
newpoint.P2 = ComputeScreenPos(newpoint.P2);
newpoint.P0.xy = newpoint.P0.xy / newpoint.P0.w;
newpoint.P1.xy = newpoint.P1.xy / newpoint.P1.w;
newpoint.P2.xy = newpoint.P2.xy / newpoint.P2.w;
newpoint.vertex = UnityObjectToClipPos(p0.xyz *2.0f);
newpoint.color=p[0].color;
newpoint.uv=p[0].uv;
pStream.Append(newpoint);
newpoint.vertex = UnityObjectToClipPos(p1.xyz*2.0f);
newpoint.color=p[1].color;
newpoint.uv=p[1].uv;
pStream.Append(newpoint);
newpoint.vertex = UnityObjectToClipPos(p2.xyz*2.0f);
newpoint.color=p[2].color;
newpoint.uv=p[2].uv;
pStream.Append(newpoint);
}
float PointToSegementDist(float2 P, float2 P0, float2 P1)
{
float2 v = P1 - P0;
float2 w = P - P0;
float c1 = dot(w, v);
float c2 = dot(v, v);
if ((c1) <= 0) // before P0
return length(P - P0);
if ((c2) <= c1) // after P1
return length(P - P1);
float b = c1 / c2;
float2 Pb = P0 + b*v;
return length(P - Pb);
}
bool _useDistanceToEdge;
fixed4 frag (g2f i) : SV_Target //the parameter's SV_POSITON value(i.vertex) contains the interpolated pixel position being drawn
{
float4 vert = (i.vertex);
vert.x/=(_ScreenParams.x);
vert.y /= (_ScreenParams.y);
//vert ?= ?
float2 clipP0 = i.P0.xy;
float2 clipP1 = i.P1.xy;
float2 clipP2 = i.P2.xy;
float alpha;
if (_useDistanceToEdge==1)
{
alpha = min(PointToSegementDist(vert.xy, clipP0, clipP1),
PointToSegementDist(vert.xy,clipP1,clipP2));
alpha = min(alpha,
PointToSegementDist(vert.xy,clipP2,clipP0));
}
else
{
alpha = min(length(vert.xy - clipP0),
length(vert.xy - clipP1));
alpha = min(alpha,
length(vert.xy - clipP2));
}
alpha /= _GlowRadius;
fixed4 col = float4(1,0,0,1);
col.a=(1-sqrt(alpha));
return col;
}
ENDCG
}//endpass
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
ZWrite On
Cull Off
ZTest LEqual
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float4 color : COLOR;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float4 color : COLOR;
};
float _GlowRadius;
v2f vert (appdata v)
{
v2f o;
float4 vert=v.vertex;
o.vertex = mul(UNITY_MATRIX_MVP, vert);
o.color=v.color;
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = i.color;
col.a=1.0f;
return col;
}
ENDCG
}
}//end subshader
}
attatched images:
ShaderOK.png - show the shader operating as expected, in scene view
ShaderGameView shows the shader in game view- the offset and missing lines can be seen.
shaderNegW - shows whats happening if one of the points passed via TEX_COORDx is behind the camera.