Modifying vertex position in a surface shader

Hi! So I have this vertex shader in my surface shader:

		void myvert( inout appdata_full v) 
		{
			float4 vv = mul( _Object2World, v.vertex );
			vv.xyz -=  _WorldSpaceCameraPos.xyz;
		 	v.vertex.y -= vv.z * vv.z * 0.005f;
		}

Which is a fairly standard “cylindrical roll off” type vertex modification (a la animal crossing) where the camera is pointing along the z access

  • Get world vertex
  • Get the distance along the z access from the camera
  • do a quadratic roll off according to distance

This works fine, generally. Problem is, there’s some obvious “sliding” when the z- rotation in the object’s world matrix is non-zero.

Anyone got any ideas how to solve this (doing the same thing in object space, perhaps? Unsure how to do that)

Many thanks,
Jonny.

EDIT: I figured it out! It’s simply transforming the offset back into the object’s local space, like so:

v.vertex += mul( _World2Object, float4( 0.0f, vv.z * vv.z * -0.005f, 0.0f, 0.0f ) );

Thought it might be worth writing this up properly as it may be useful to others. The problem is not necessarily obvious, even though the solution is.

Problem: Writing a custom vertex shader for a surface shader does not allow you to output a world space vertex. This is presumably because Unity is going to do a bunch of other local space processing before transforming it into world space afterwards.

Problems then arise when you have scales or rotations on your local vertex, which is pretty much always.

Solution: Simply transform the vertex into world space, do your world space processing and then transform back into local space.

void SurfaceShaderVertexTransform( inout appdata_full v) 
{
     // transform into worlspace
     float4 world_space_vertex = mul( _Object2World, v.vertex );

     /* Do some cool things here */

     // transform back into local space
     v.vertex = mul( _World2Object, world_space_vertex )
}