Fix Shader Flipbook and Orientation

I am trying to make a grass shader that’s using a flipbook sprite sheet however I am having issues with the math to get it to show at the right orientation with a proper flipbook look.

Shader "Custom/ProceduralGrass"
{
	Properties
	{
		_BaseColor("Base Color", Color) = (0, 0, 0, 0)
		_MainTex("Base Texture", 2D) = "white" {}
		_Cutoff("Alpha Cutoff", Range(0,1)) = 0.15

		[Header(Flipbook Settings)]
		_ColumnsX("Columns (X)", int) = 1
		_RowsY("Rows (Y)", int) = 1
		_AnimationSpeed("Frames Per Seconds", float) = 10
	}

		SubShader
	{
		Tags
		{
			"RenderType" = "TransparentCutout"
			"Queue" = "Transparent"
			"RenderPipeline" = "UniversalPipeline"
			"PreviewType" = "Plane"

		}

		HLSLINCLUDE
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

			#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
			#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
			#pragma multi_compile _ _SHADOWS_SOFT

			struct appdata
			{
				uint vertexID : SV_VertexID;
				uint instanceID : SV_InstanceID;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float4 positionCS : SV_Position;
				float4 positionWS : TEXCOORD1;
				float2 uv : TEXCOORD0;
			};

			StructuredBuffer<float3> _Positions;
			StructuredBuffer<float3> _Normals;
			StructuredBuffer<float2> _UVs;
			StructuredBuffer<float4x4> _TransformMatrices;

			CBUFFER_START(UnityPerMaterial)
				float4 _BaseColor;
				sampler2D _MainTex;
				float4 _MainTex_ST;
				uint _ColumnsX;
				uint _RowsY;
				float _AnimationSpeed;
				float _Cutoff;
			CBUFFER_END
		ENDHLSL

		Pass
		{
			Name "GrassPass"
			Tags { "LightMode" = "UniversalForward" }

			HLSLPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			v2f vert(appdata v)
			{
				v2f o;

				float2 size = float2(1.0f / _ColumnsX, 1.0f / _RowsY);
				uint totalFrames = _ColumnsX * _RowsY;
				uint index = _Time.y * _AnimationSpeed;
				uint indexX = index % _ColumnsX;
				uint indexY = floor((index % totalFrames) / _ColumnsX);
				float2 offset = float2(size.x * indexX, -size.y * indexY);
				float2 newUV = v.uv * size;
				newUV.y = newUV.y + size.y * (_RowsY - 1);
				o.uv = newUV + offset;


				float4 positionOS = float4(_Positions[v.vertexID], 1.0f);
				float4x4 objectToWorld = _TransformMatrices[v.instanceID];

				o.positionWS = mul(objectToWorld, positionOS);
				o.positionCS = mul(UNITY_MATRIX_VP, o.positionWS);
				o.uv += _UVs[v.vertexID];

				return o;
			}

			float4 frag(v2f i) : SV_Target
			{
				float4 color = tex2D(_MainTex, i.uv);

				VertexPositionInputs vertexInput = (VertexPositionInputs)0;
				vertexInput.positionWS = i.positionWS;

				float4 shadowCoord = GetShadowCoord(vertexInput);
				float shadowAttenuation = saturate(MainLightRealtimeShadow(shadowCoord) + 0.25f);
				float4 shadowColor = lerp(0.0f, 1.0f, shadowAttenuation);
				color *= shadowColor;

				clip(color.a - _Cutoff);
				
				return color;
			}

			ENDHLSL
		}

		Pass
		{
			Name "ShadowCaster"
			Tags { "LightMode" = "ShadowCaster" }

			ZWrite On
			ZTest LEqual

			HLSLPROGRAM
			#pragma vertex shadowVert
			#pragma fragment shadowFrag

			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SurfaceInput.hlsl"

			float3 _LightDirection;
			float3 _LightPosition;

			v2f shadowVert(uint vertexID : SV_VertexID, uint instanceID : SV_InstanceID)
			{
				v2f o;

				float4 positionOS = float4(_Positions[vertexID], 1.0f);
				float3 normalOS = _Normals[vertexID];
				float4x4 objectToWorld = _TransformMatrices[instanceID];

				float4 positionWS = mul(objectToWorld, positionOS);
				o.positionCS = mul(UNITY_MATRIX_VP, positionWS);
				o.uv = _UVs[vertexID];

				float3 normalWS = TransformObjectToWorldNormal(normalOS);

		#if _CASTING_PUNCTUAL_LIGHT_SHADOW
						float3 lightDirectionWS = normalize(_LightPosition - positionWS);
		#else
						float3 lightDirectionWS = _LightDirection;
		#endif
						o.positionWS = float4(ApplyShadowBias(positionWS, normalWS, lightDirectionWS), 1.0f);

						return o;
			}

			float4 shadowFrag(v2f i) : SV_Target
			{
				//Alpha(SampleAlbedoAlpha(i.uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap)).a, _BaseColor, _Cutoff);
				return 0;
			}

			ENDHLSL
		}
	}
		Fallback Off
}

I managed to fix my issue. I realized I was trying to set the position and flipbook both in the vert function. Splitting the flipbook function into the frag function works properly. One other issue I have now is the sprite sheet doesn’t work unless it is rotated clockwise 90 degrees. Here’s the code for reference.

Shader "Custom/ProceduralGrass"
{
	Properties
	{
		_BaseColor("Base Color", Color) = (0, 0, 0, 0)
		_MainTex("Base Texture", 2D) = "white" {}
		_Cutoff("Alpha Cutoff", Range(0,1)) = 0.15

		[Header(Flipbook Settings)]
		_ColumnsX("Columns (X)", int) = 1
		_RowsY("Rows (Y)", int) = 1
		_AnimationSpeed("Frames Per Seconds", float) = 10
	}

		SubShader
	{
		Tags
		{
			"RenderType" = "TransparentCutout"
			"Queue" = "Transparent"
			"RenderPipeline" = "UniversalPipeline"
			"PreviewType" = "Plane"

		}

		HLSLINCLUDE
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

			#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
			#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
			#pragma multi_compile _ _SHADOWS_SOFT

			struct appdata
			{
				uint vertexID : SV_VertexID;
				uint instanceID : SV_InstanceID;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float4 positionCS : SV_Position;
				float4 positionWS : TEXCOORD1;
				float2 uv : TEXCOORD0;
			};

			StructuredBuffer<float3> _Positions;
			StructuredBuffer<float3> _Normals;
			StructuredBuffer<float2> _UVs;
			StructuredBuffer<float4x4> _TransformMatrices;

			CBUFFER_START(UnityPerMaterial)
				float4 _BaseColor;
				sampler2D _MainTex;
				float4 _MainTex_ST;
				uint _ColumnsX;
				uint _RowsY;
				float _AnimationSpeed;
				float _Cutoff;
			CBUFFER_END
		ENDHLSL

		Pass
		{
			Name "GrassPass"
			Tags { "LightMode" = "UniversalForward" }

			HLSLPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			v2f vert(appdata v)
			{
				v2f o;
				

				float4 positionOS = float4(_Positions[v.vertexID], 1.0f);
				float4x4 objectToWorld = _TransformMatrices[v.instanceID];

				o.positionWS = mul(objectToWorld, positionOS);
				o.positionCS = mul(UNITY_MATRIX_VP, o.positionWS);
				o.uv = _UVs[v.vertexID];


				

				return o;
			}

			float4 frag(v2f i) : SV_Target
			{
				float2 size = float2(1.0f / _ColumnsX, 1.0f / _RowsY);
				uint totalFrames = _ColumnsX * _RowsY;
				uint index = _Time.y * _AnimationSpeed;
				uint indexX = index % _ColumnsX;
				uint indexY = floor((index % totalFrames) / _ColumnsX);
				float2 offset = float2(size.x * indexX, -size.y * indexY);
				float2 newUV = i.uv * size;
				newUV.y = newUV.y + size.y * (_RowsY - 1);

				i.uv = newUV + offset;			

				float4 color = tex2D(_MainTex, i.uv);

				VertexPositionInputs vertexInput = (VertexPositionInputs)0;
				vertexInput.positionWS = i.positionWS;

				float4 shadowCoord = GetShadowCoord(vertexInput);
				float shadowAttenuation = saturate(MainLightRealtimeShadow(shadowCoord) + 0.25f);
				float4 shadowColor = lerp(0.0f, 1.0f, shadowAttenuation);
				color *= shadowColor;

				clip(color.a - _Cutoff);
				
				return color;
			}

			ENDHLSL
		}

		Pass
		{
			Name "ShadowCaster"
			Tags { "LightMode" = "ShadowCaster" }

			ZWrite On
			ZTest LEqual

			HLSLPROGRAM
			#pragma vertex shadowVert
			#pragma fragment shadowFrag

			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
			#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SurfaceInput.hlsl"

			float3 _LightDirection;
			float3 _LightPosition;

			v2f shadowVert(uint vertexID : SV_VertexID, uint instanceID : SV_InstanceID)
			{
				v2f o;

				float4 positionOS = float4(_Positions[vertexID], 1.0f);
				float3 normalOS = _Normals[vertexID];
				float4x4 objectToWorld = _TransformMatrices[instanceID];

				float4 positionWS = mul(objectToWorld, positionOS);
				o.positionCS = mul(UNITY_MATRIX_VP, positionWS);
				o.uv = _UVs[vertexID];

				float3 normalWS = TransformObjectToWorldNormal(normalOS);

		#if _CASTING_PUNCTUAL_LIGHT_SHADOW
						float3 lightDirectionWS = normalize(_LightPosition - positionWS);
		#else
						float3 lightDirectionWS = _LightDirection;
		#endif
						o.positionWS = float4(ApplyShadowBias(positionWS, normalWS, lightDirectionWS), 1.0f);

						return o;
			}

			float4 shadowFrag(v2f i) : SV_Target
			{
				//Alpha(SampleAlbedoAlpha(i.uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap)).a, _BaseColor, _Cutoff);
				return 0;
			}

			ENDHLSL
		}
	}
		Fallback Off
}