These past few days I’ve been tearing my hair out, trying to find out a way to modify vertices in screen space within a surface shader, like what can be achieved with an unlit shader. All examples, documentation, blogs, and tutorials cite do something like this, which can only transform vertices in model space, before they have been multiplied by the transformation matrix.

Here’s an example of what I currently have, as an unlit shader:

``````Shader "Unlit/JitterWarp"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Jitter ("Jitter", Range(1, 64)) = 1
[MaterialToggle] _Warp ("Warp", Float) = 0
}
{
Tags { "RenderType"="Opaque" }
LOD 100

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog

#include "UnityCG.cginc"

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

struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};

sampler2D _MainTex;
float4 _MainTex_ST;
float _Jitter;
float _Warp;

v2f vert (appdata v)
{
v2f o;
// Note that all calculations take place *after* this code
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);

o.vertex.xyz /= abs(o.vertex.w);
o.vertex.w = sign(o.vertex.w);

float2 j = _ScreenParams.xy / _Jitter;
o.vertex.xy = round(o.vertex.xy * j) / j;

return o;
}

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

The reason why I need it as a surface shader, is to apply this effect to terrain, which only seems to support surface shaders. I have looked online for hours to no avail, to find a shader or example which does not use surface shaders, but I have come up empty.

If it turns out to be impossible/ridiculously complex to have screen space vertex shaders in surface shaders, I would also accept a way to have unlit shaders on terrain. I assume the latter might be easier but I don’t know.

I would greatly appreciate the help, I only started Unity a week ago and am trying to bend it to my will

Surface Shaders are vertex fragment shader generators. You can take a Surface Shader written for terrain, and modify the generated shader code all you want.

You can also do some wacky stuff like transform the vertex position into clip space, modify it, and transform it back to local space. The problem is Unity doesn’t actually provide the inverse transform matrix for the `UNITY_MATRIX_VP` matrix that `UnityObjectToClipPos()` uses. They do provide some other matrices that can get you the close equivalent with `unity_CameraProjection` and `unity_CameraInvProjection`. These don’t exactly match the `UNITY_MATRIX_P`, but they might get you what you need.

So you’d need to do:

``````float4 worldPos = mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1.0));
float4 viewPos = mul(UNITY_MATRIX_V, worldPos);
float4 clipPos = mul(unity_CameraProjection, viewPos);
v.vertex = mul(unity_WorldToObject, mul(UNITY_MATRIX_I_V, mul(unity_CameraInvProjection, clipPos)));
v.vertex /= v.vertex.w;
``````

It should be noted that this isn’t exactly the same as keeping the position in clip space, but it’s close, and the artifacts from doing this should only be visible if you’re really, really messing with the screen space positions, or close to the camera’s near plane.

There’s also an even more crazy solution, and that is to transform the `appdata` values into the final forms the shader is going to output them in, and then set all of the transform matrices to identity matrices so the later code won’t modify them.

``````void vertex(inout appdata v)
{
v.vertex = UnityObjectToClipPos(v.vertex);
unity_ObjectToWorld = UNITY_MATRIX_VP = float4x4(
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1
);
When the generated shader code later calls `UnityObjectToClipPos()`, which uses those two matrices, the value in `v.vertex` won’t be modified at all and you’ll be good to go.