Moving tracer shader with line renderer

Hi, I’m trying to write a customizable tracer shader to use with my game. The gun raycasts outward to find a target, and passes in an origin and target to my tracer factory, which then creates a TracerPrefab consisting of an empty GameObject with a LineRenderer component and sets the line’s positions to the origin and target.

The line renderer is using a custom material with a shader I’m attempting to write. At the moment, I’m stuck trying to get the shaded portion of the LineRenderer to move at a consistent speed. When aiming into the distance, it moves very fast, but when aiming at something close it moves very slowly. This is because it doesn’t take into account how far along relative to the LineRenderer it is, which is my question.

Is it possible to figure out how far along the uv.x is relative to the LineRenderer?

I’ve attempted to use the vertex.z coordinate, but of course that deals with position from the camera, not on the line.

		CGPROGRAM
		#pragma vertex vert
		#pragma fragment frag
		
		#include "UnityCG.cginc"

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

		struct v2f
		{
			float2 uv : TEXCOORD0;
			float4 vertex : SV_POSITION;
		};

		sampler2D _MainTex;
		fixed4 _Color;
		float _Length;
		float _StartTime;
		float _Speed;
		float _Offset;
		
		v2f vert (appdata v)
		{
			v2f o;
			o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
			o.uv = v.uv;
			return o;
		}
		
		fixed4 frag (v2f i) : SV_Target
		{
			float timeDiff = _Time.y - _StartTime + _Offset;
			if ((i.uv.x + _Speed) > timeDiff * _Speed)
			{
				return fixed4(0, 0, 0, 1);
			}
			return fixed4(1, 1, 1, 0);
		}

The script that creates it passes in the current time with Time.timeSinceLevelLoad to _StartTime. This is used to move the drawn position based on how long it has existed. _Speed is a range from 0 to 2. _Offset is used to offset the spawning of the tracer from the spawning of the line.

Sorry if this is obvious, I’m slightly green with shaders in general. Thanks for any help.

I haven’t found a way to do it directly from the shader, so I’m simply extending the LineRenderer to a predetermined maximum, and scripting _MaxDistance and _CutoffDistance values to the shader, so when the tracer needs to stop because it hit a wall, the _CutoffDistance will stop it, as well as _MaxDistance lending itself pretty well towards fixed speed calculations. Here’s the shader if anyone wants it. It has controls to set base transparency and fade duration as well as things like length, color, speed, etc. Please note that the speed is partially tied to the length; if you make it longer, you should turn down the speed if you want it to move the same speed as a shorter tracer. And here’s a super-professional, Grade-A tracer texture to use.

Shader "Custom/TracerShader"
{
Properties
{
	_MainTex("Tracer Texture", 2D) = "white" {}
	_Color("Color", Color) = (1, 1, 1, 1)
	_Length("Length", Range(0.1, 1000)) = 1
	_Speed("Speed", Range(0, 15)) = 2.0
	_StartTime("Start Time", Float) = 0.0
	_Offset("Offset", Range(1, 5)) = 0
	_Fade("Fade time", Range(0, 5)) = 0
	_Transparency("Transparency", Range(0, 1)) = 0
	_TailBlendCutoff("Tail Blending Cutoff", Range(0, 1)) = 0.0
	_Width("Tracer Texture Width", Float) = 0.0
	_MaxDistance("Max Distance", Float) = 1000.0
	_CutoffDistance("Cutoff Distance", Float) = 0.0
	_AlphaClamp("Alpha Clamp", Range(0, 1)) = 0.0
	_Debug("Debug", int) = 1
	_DebugColorPre("DebugColorPre", Color) = (1, 1, 1, 1)
	_DebugColorPost("DebugColorPost", Color) = (1, 1, 1, 1)
}

SubShader
{
	Tags 
	{
		"RenderType" = "Transparent"
		"Queue" = "Transparent"
	}

	Pass
	{
		BlendOp Add
		Blend SrcAlpha SrcAlpha
		ZTest LEqual
		ZWrite Off

		CGPROGRAM
		#pragma vertex vert
		#pragma fragment frag
		
		#include "UnityCG.cginc"

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

		struct v2f
		{
			float2 uv : TEXCOORD0;
			float4 vertex : SV_POSITION;
		};

		sampler2D _MainTex;
		fixed4 _Color;
		float _Length;
		float _StartTime;
		float _Speed;
		float _Width;
		float _Offset;
		float _Fade;
		float _Transparency;
		float _TailBlendCutoff;
		float _MaxDistance;
		float _CutoffDistance;
		float _AlphaClamp;
		int _Debug;
		fixed4 _DebugColorPre;
		fixed4 _DebugColorPost;

		fixed alpha(fixed4 col)
		{
			fixed total = max(col.r, max(col.g, col.b));
			if (total < _AlphaClamp) //if the color is too dark, clamp to transparent
				return 1;
			return total;
		}
		
		v2f vert (appdata v)
		{
			v2f o;
			o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
			o.uv = v.uv;
			return o;
		}
		
		fixed4 frag (v2f i) : SV_Target
		{
			//if debugging is off, make the colors transparent
			if (_Debug == 0)
			{
				_DebugColorPre = fixed4(0, 0, 0, 1);
				_DebugColorPost = fixed4(0, 0, 0, 1);
			}
			
			//if we've passed the cutoff distance, don't sample the tracer
			float cutoff = _CutoffDistance / _MaxDistance;
			if (i.uv.x > cutoff || i.uv.x < (_Offset / _MaxDistance))
			{
				return _DebugColorPost;
			}

			//calculate the difference between the current time and the start time
			//if the uv.x coordinate is past it, don't sample the tracer
			float timeDiff = (_Time.y - _StartTime) * ((_Speed / 10) * _Length); //divided by 10 to give reasonable speed values
			if (i.uv.x > timeDiff)
			{
				return _DebugColorPost;
			}

			//calculate the length of the tracer by dividing the _Length parameter by the _MaxDistance parameter and multiplying by the texture width
			//NOTE: this does make tracer lengths dependent on both the _MaxDistance used and the width of the texture
			float tracerLength = (_Length / _MaxDistance) * _Width;

			//get the uv coordinate of the start of the tracer
			float tracerStart = timeDiff - tracerLength;

			if (i.uv.x > tracerStart) //if we're inside the tracer's area...
			{
				float tracerOffset = (i.uv.x - tracerStart) / tracerLength;

				float2 samp = i.uv;
				samp.x = tracerOffset; //calculate the uv coordinate relative to the tracer that we need to sample
				fixed4 col = tex2D(_MainTex, samp);

				col = (col * _Color * (1 - _Transparency)); //calculate _Color and _Transparency

				if (_Fade > 0)
					col = col * (1 - timeDiff / _Fade); //calulate _Fade

				float tailBlendUV = _TailBlendCutoff / (_MaxDistance / 10);

				if (i.uv.x < tailBlendUV)
				{
					float blendVal = (i.uv.x / tailBlendUV) - 0.3; //calculate tail blend
					col = col * min(1, blendVal);
				}

				col.a = alpha(col);
				return col;
			}
			else //don't sample the tracer
			{
				return _DebugColorPre;
			}

		}
		ENDCG
	}
}

}