On second thought, I’m guessing you have some other issues in your shader causing the flickering. You also don’t have a custom shadow caster pass which is another issue. The flickering might be because you don’t have a light mode tag on your base pass. My original suggestion may not really be fully relevant, but I honestly am not really sure what you’re trying to go for with your look.
Can you post your entire shader rather than just a snippet?
To get from a mesh asset to being drawn on screen, each individual vertex needs to be transformed in a number of ways, through several different spaces.
A single vertex position is initially stored in object space, aka local space. This is a 3D position relative to the mesh’s pivot position and orientation. This is the position that is stored in the v.vertex value that comes into the vertex shader.
This is then transformed into world space using an object to world matrix, sometimes called the “model” matrix. In Unity shaders this is the unity_ObjectToWorld matrix, or UNITY_MATRIX_M matrix. M for Model.
The world space position is then transformed into view space, also called eye or camera space. This is the UNITY_MATRIX_V matrix. V for View. Technically it’s only “camera relative” when rendering from the point of view of a camera, like what you see in the scene or game views. Things like shadow maps are rendered from the view of the light rather than from a “camera”, so in those cases the UNITY_MATRIX_V is that light’s position and orientation.
From view space the position is transformed into clip space, also called projection space and more accurately called homogeneous clip space. This is what the UNITY_MATRIX_P is for, and what a vertex shader should be outputting. This one is the hardest to understand as it’s no longer a “regular” 3D space position where only the first three components matter. It’s now a 4D position of sorts. Really it’s a 3D position specially encoded for interpolation in screen space so the original positions can be reconstructed. I’ll leave that to you to research more if you want, but the easiest way to think about it is, for a perspective camera, the range of x and y are from -w to +w for stuff that’s within view, where w is the view space depth. I don’t mention z because the range of z depends on the graphics API, with most being from w to 0.0, but OpenGL is -w to +w like x and y. I won’t go into why now, but know OpenGL was first, and everyone else realized this was a mistake to do.
After that things get transformed by the GPU into normalized device space coordinates, or “NDC”, which is basiscally just the xyz / w, and from there into viewport space, aka window, or screen space, which is the screen pixel positions and a 0.0 to 1.0 depth range. But, again, that’s all done by the GPU on the value output by the vertex shader before it runs the fragment shader and you can ignore.
Here’s a graphic for which I don’t know the original author of to give proper attribution to.

Unity also has a couple of extra macros for matrices like UNITY_MATRIX_MV, UNITY_MATRIX_VP, and UNITY_MATRIX_MVP, which are the model and view matrix, view and projection matrix, and model, view, and projection matrix combined into a single matrix.
So what Keijiro’s shader is doing is converting the position into view space, snapping those positions to a view space grid of sorts, then multiplying by the projection matrix to put it into the coordinate system the vertex shader should be outputting.
Back to my original suggestion, I’m guessing at some point you’re going to need a custom shadow caster pass so the camera depth texture, screen space shadows from the main directional light, and all other shadows, actually line up with the geometry rather than shadowing as if they’re normal geometry.
UNITY_MATRIX_V, and the c# camera.worldToCameraMatrix are the same matrix when rendering to the camera view. Both convert from a world space position to the same camera relative view space position. However when rendering the shadow maps, they’re not the same since the UNITY_MATRIX_V is the view from the light’s orientation rather than the camera. Hence why I recommended passing that matrix as a shader global, though I forgot to link to the appropriate script function to do so:
https://docs.unity3d.com/ScriptReference/Shader.SetGlobalMatrix.html
However, I think Unity may already have access to those matrices in the shadow maps in the form of the unity_WorldToCamera and unity_CameraToWorld matrices. So try this:
float4 worldPos = mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1));
float4 cameraPos = mul(unity_WorldToCamera, worldPos);
cameraPos.xyz = floor(cameraPos.xyz * _Distortion) / _Distortion;
worldPos = mul(unity_CameraToWorld, cameraPos);
v.vertex = mul(unity_WorldToObject, worldPos);
All together it would look something like this:
Shader "Custom/Keijiro Style Retro With Shadows"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Distortion ("Distortion", Float) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#pragma multi_compile_fog
#include "UnityCG.cginc"
#include "AutoLight.cginc"
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 diff : TEXCOORD1;
float3 ambient : TEXCOORD2;
SHADOW_COORDS(3)
UNITY_FOG_COORDS(4)
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _Distortion;
half3 _LightColor0;
v2f vert (appdata_full v)
{
v2f o;
float4 worldPos = mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1));
float4 cameraPos = mul(unity_WorldToCamera, worldPos);
cameraPos.xyz = round(cameraPos.xyz * _Distortion) / _Distortion;
worldPos = mul(unity_CameraToWorld, cameraPos);
v.vertex = mul(unity_WorldToObject, worldPos);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
half diff = saturate(dot(worldNormal, _WorldSpaceLightPos0.xyz));
o.diff = diff * _LightColor0.rgb;
o.ambient = ShadeSH9(float4(worldNormal, 1));
UNITY_TRANSFER_FOG(o,o.vertex);
TRANSFER_SHADOW(o)
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
half shadow = SHADOW_ATTENUATION(i);
col.rgb *= i.diff * shadow + i.ambient;
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
// Pass to render object as a shadow caster
Pass {
Name "ShadowCaster"
Tags { "LightMode" = "ShadowCaster" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile_shadowcaster
#pragma multi_compile_instancing // allow instanced shadow pass for most of the shaders
#include "UnityCG.cginc"
struct v2f {
V2F_SHADOW_CASTER;
UNITY_VERTEX_OUTPUT_STEREO
};
float _Distortion;
v2f vert( appdata_base v )
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
float4 worldPos = mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1));
float4 cameraPos = mul(unity_WorldToCamera, worldPos);
cameraPos.xyz = round(cameraPos.xyz * _Distortion) / _Distortion;
worldPos = mul(unity_CameraToWorld, cameraPos);
v.vertex = mul(unity_WorldToObject, worldPos);
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
return o;
}
float4 frag( v2f i ) : SV_Target
{
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
}