So you want these to be a constant size on screen regardless of resolution, distance, fov, etc., correct? The easiest solution is … do everything in screen space!
//vertex shader
// get clip space position of the mesh's pivot
float4 clipPivot = UnityObjectToClipPos(float3(0,0,0));
// get the normalized device coordinate space position of that pivot
float3 ndcPivot = clipPivot.xyz / clipPivot.w;
// do stuff you're already doing to get the object space vertex position
float3 vertexPos = // stuff;
// the screen's aspect ratio
float aspect = _ScreenParams.x / _ScreenParams.y;
// scale to the size you want it to be on screen
vertexPos.xy *= _ArcScreenSize * 2.0;
// correct for the aspect ratio
vertexPos.x *= aspect;
// flip upside down, for reasons
vertexPos.y *= -1;
// add ndc pivot and vertex position together
float3 outPos = vertexPos + ndcPivot;
// SV_Position output
o.pos = float4(outPos, 1.0);
Homogeneous Clip Space is a 4 component projective coordinate system that can be thought of as “screen space”, but where the x and y values are in a -w to +w range for what will appear on screen, where w is literally the w value of that coordinate. It exists in part as a byproduct of linear projection matrices (how real time rendering calculates results of the camera’s FOV), and has the advantageous property of being a value that can be interpolated linearly in screen space while remaining perspective correct. Basically it helps prevent the weird PS1 style texture warping.
The Normalized Device Coordinates are the clip space after the “perspective divide”, which a fancy term for dividing the clip space by the w component. The x and y are now a value that is a -1 to +1 range for what appears on screen.
Assuming your arc mesh has a width of 1 unit on the x and y, you’ll want to scale it up or down to the size you want it to be on screen. Because we’re working in NDC space which has a -1 to +1 range, I multiply that by 2.0. Otherwise you’re setting the half size, which you can do too. The x axis is divided by the aspect ratio.
Shader "Screen Space Mesh"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Size ("Screen Space Size", Range(0,1)) = 0.25
}
SubShader
{
Tags { "RenderType"="Opaque" "DisableBatching"="True" }
LOD 100
Pass
{
Cull Off
ZTest Always
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _Size;
v2f vert (appdata v)
{
v2f o;
// get clip space position of the mesh's pivot
float4 clipPivot = UnityObjectToClipPos(float3(0,0,0));
// get the normalized device coordinate space position of that pivot
float3 ndcPivot = clipPivot.xyz / clipPivot.w;
// do stuff you're already doing to get the object space vertex position
float3 vertexPos = v.vertex.xyz;
// just something to make it rock back and forth
float angle = sin(_Time.y);
float c = cos(angle);
float s = sin(angle);
vertexPos.xy = mul(float2x2(c,-s,s,c), vertexPos.xy);
// scale
vertexPos.xy *= _Size * 2.0;
// correct for aspect ratio and screen space y flip
vertexPos.x /= _ScreenParams.x / _ScreenParams.y;
vertexPos.y *= -1;
// add ndc pivot and vertex position together and output as SV_Position
o.pos = float4(vertexPos + ndcPivot, 1.0);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
return col;
}
ENDCG
}
}
}