Unity shader confusion

Here’s a shader I have, and it works fine. but somehow I’m getting a different result when
mask2 = 1-mask1;
vs
mask2 = (i.uv1.y > _DissolveGradientSize) ? 1 : 0;

when _DissolveAmt is at 0?

Shader "SelfMade/Unlit/Line"
{
	Properties
	{
		_MainTex ("Mask", 2D) = "white" {}  // use as over all edge mask
		_DissolveGradientSize  ("Start Gradient Size", Float) = .05
		
		//https://docs.unity3d.com/2023.2/Documentation/ScriptReference/MaterialPropertyDrawer.html
		_DissolveAmt  ("Reveal Amount", Range(0, 1)) = 0
		
		_Texture ("Texture", 2D) = "white" {} // use as tiled texture mask

	}
	SubShader
	{
		Tags {"Queue"="Transparent" "RenderType"="Transparent" }
		LOD 100
		ZWrite Off 
		Blend SrcAlpha OneMinusSrcAlpha
		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			float remapper(float i, float nMin, float nMax, float oMin, float oMax) 
			{
				return nMin + ( (i-oMin) * (nMax-nMin) / (oMax-oMin) );
			}

			struct appdata
			{
				float4 vertex : POSITION;
				float4 uv : TEXCOORD0;
				float2 uv1 : TEXCOORD1;
				float4 lColor : COLOR;
			};

			struct v2f
			{
				float4 uv : TEXCOORD0;
				float2 uv1 : TEXCOORD1;
				float4 vertex : SV_POSITION;
				float4 lColor : COLOR;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _Texture;
			float4 _Texture_ST;

			float _DissolveGradientSize; 
			float _DissolveAmt; 
						
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv.xy = TRANSFORM_TEX(v.uv, _MainTex);
				o.uv.zw = TRANSFORM_TEX(v.uv, _Texture);

				//float sinT = ((_SinTime.w + 1) / 2);

				o.uv1.x = remapper(v.uv1.x, 0, 1, 0, _DissolveAmt ); //remap the uv to scale it
				o.uv1.y = v.uv.x; // a staic uv gradient

				o.lColor = v.lColor; 
				return o;
			}

			float4 frag (v2f i) : SV_Target
			{
				//float sinT = ((_SinTime.w + 1) / 2);
				
				float mask1 = step(i.uv1.y, _DissolveGradientSize); 
				float mask2 = 1-mask1; //(i.uv1.y > _DissolveGradientSize) ? 1 : 0; // single line if statement (condition) ? true returns this : false returns this;
				/*
				for some unknown reason 
				1-mask1 
				vs (i.uv1.y > _DissolveGradientSize) ? 1 : 0 OR step(_DissolveGradientSize, i.uv1.y);  <- these produce same results
				produce different final image even tho the masks looks
				*/
				i.uv.x = (i.uv1.y * mask1) + (i.uv1.x * mask2); //overiding i.uv.x, making it so that the start doesn't stretch, but shows up immediately from 0 up to _DissolveGradientSize, and the stretches from that point onwards towards 1

				float a = (tex2D(_MainTex, i.uv.xy)).g;
				float col_a = (tex2D(_Texture, i.uv.zw)).g;

				return float4 (i.lColor.rgb, a*col_a);

				//return float4 (i.uv.z  , i.uv.z , i.uv.z  , 1);
				//return float4 (mask1, mask2, 0, 1);

			}
			ENDCG
		}
	}
}

like the masks looks the same when I output it from the frag shader, so why is the result different?
I’m pretty new to make shader with just code (it’s a lotta fun) but I have no idea what’s happening here and I’d like to know lol

so the issue seems to come from dividing by 0, causing NaN to leak thru to the end result, but I have no idea why the different masking calculation causes the result to be different?

I’m seeing the same results no matter if I do > or >= in the ternary or not.

The result from 1-Mask1 is what I expected, but I have no idea why it’s the only one that seems to not let NaN breaks it?

NaN leaks when I use step() or ternary for both masks.

if I do mask1 using ternary and mask2 w/ 1-mask1, NaN also leaks

and if I do mask1 using step() and mask2 w/ 1-mask1, there’s no leak?
and under mask1 using step() and mask2 w/ 1-mask1, if I calculate i.uv.x with lerp, NaN leaks, but if I use the *m1+*m2 it doesn’t leak???
:dizzy_face::dizzy_face::dizzy_face:

1 Like

Hi @Flying_Book,

for me both math functions look the same. Maybe you doing something between the result and your math?

Shader "SelfMade/Unlit/Line"
{
	Properties
	{
		_MainTex ("Mask", 2D) = "white" {}  // use as over all edge mask
		_DissolveGradientSize  ("Start Gradient Size", Float) = .05
		
		//https://docs.unity3d.com/2023.2/Documentation/ScriptReference/MaterialPropertyDrawer.html
		_DissolveAmt  ("Reveal Amount", Range(0, 1)) = 0
		
		_Texture ("Texture", 2D) = "white" {} // use as tiled texture mask

		[Header(Switch between To Math)]_Checker("Checker", vector) = (0.0, 0.0, 0.0, 1.0)

	}
	SubShader
	{
		Tags {"Queue"="Transparent" "RenderType"="Transparent" }
		LOD 100
		ZWrite Off 
		Blend SrcAlpha OneMinusSrcAlpha
		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			float remapper(float i, float nMin, float nMax, float oMin, float oMax) 
			{
				return nMin + ( (i-oMin) * (nMax-nMin) / (oMax-oMin) );
			}

			struct appdata
			{
				float4 vertex : POSITION;
				float4 uv : TEXCOORD0;
				float2 uv1 : TEXCOORD1;
				float4 lColor : COLOR;
			};

			struct v2f
			{
				float4 uv : TEXCOORD0;
				float2 uv1 : TEXCOORD1;
				float4 vertex : SV_POSITION;
				float4 lColor : COLOR;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _Texture;
			float4 _Texture_ST;

			float _DissolveGradientSize; 
			float _DissolveAmt;
			float4 _Checker; 
						
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv.xy = TRANSFORM_TEX(v.uv, _MainTex);
				o.uv.zw = TRANSFORM_TEX(v.uv, _Texture);

				//float sinT = ((_SinTime.w + 1) / 2);

				o.uv1.x = remapper(v.uv1.x, 0, 1, 0, _DissolveAmt ); //remap the uv to scale it
				o.uv1.y = v.uv.x; // a staic uv gradient

				o.lColor = v.lColor; 
				return o;
			}

			float4 frag (v2f i) : SV_Target
			{
				
				float mask = step(i.uv1.y, _DissolveGradientSize); 
				float math1 = 1-mask; 
				float math2 = (i.uv1.y > _DissolveGradientSize) ? 1 : 0; //(i.uv1.y > _DissolveGradientSize) ? 1 : 0; // single line if statement (condition) ? true returns this : false returns this;
		
				//Packed both math in channel to compare
				float4 combinedMath = float4(math1, math2, 0.0, 1.0);
				combinedMath.rgb *= _Checker.rgb;
				
				return combinedMath;
			}
			ENDCG
		}
	}
}


I mean I posted the entirety of my code, there shouldn’t be anything hidden away :confused:
If I just return the mask they are the same for me too, but somehow

i.uv.x = (i.uv1.y * mask1) + (i.uv1.x * mask2);

makes them gives different results.

It can be that your calculations giving the same visual result, but the values can be different. You can try to use a clamp() or saturation(), directly after your calculation.

I don’t see how that can be the case tho? Both step() and the ternary can only return 1 or 0, right? and 1- 0 or 1 is 1 or 0.

Hi @Flying_Book,

I checked this here…
(i.uv1.y * mask1)

there is something happing on the ride side…and later you are adding this maybe this cause the issue.
i.uv.x = (i.uv1.y * mask1) + (i.uv1.x * mask2);

return float4 (i.lColor.rgb, (i.uv1.y * mask1));

Sorry I haven’t have time to look at this again until now.
Isn’t that just showing what it’s suppose to be, the UV * the mask?

np, sry too…I was quiet busy the last months.

I can’t tell you what the issue is. The only thing what I figure out is:

This line i.uv.x = (i.uv1.y * mask1) + (i.uv1.x * mask2); gives 2 different results,
when you are using the 2 different calculation in variable mask2.

Here the results, what I am getting.

float mask2 = (i.uv1.y > _DissolveGradientSize) ? 1 : 0;
i.uv.x = (i.uv1.y * mask1) + (i.uv1.x * mask2);
return i.uv.x;
Unity_H8zmXQM5tX

float mask2 = 1-mask1;
i.uv.x = (i.uv1.y * mask1) + (i.uv1.x * mask2);
return i.uv.x;
Unity_QJrRVu0lcm

1 Like

No worries lol, everybody’s so busy these days :sob:
I appreciate any replies~
I notice that too, I don’t understand why that is happening.

1 Like

i think you want to prevent the div 0 issue first and then check the .05 seeing as how that is applying a lower boundary for the primary mask?

return (nMin + ((i - oMin) * (nMax - nMin) / (.00001 + oMax - oMin)));
rtvfxmask

afaik these are not the same with precision in the mix & the fact you set _DissolveGradientSize to 0.05

float mask2 = 1-mask1; // <— is the inverse to use

i.uv1.y > _DissolveGradientSize) ? 1 : 0 OR step(_DissolveGradientSize, i.uv1.y) //i do not think you want that

1 Like

When you say these are not the same precision, are you referring to? 1-mask1 and i.uv1.y > _DissolveGradientSize) ? 1 : 0 OR step(_DissolveGradientSize, i.uv1.y)?

I’m just confused as to why the way mask2 are calculated effects the outcome if the problem is with i.uv1.x dividing by zero? the masks’ calculation only deals with i.uv.y. Like in either case of mask2, would mask2 * i.uv1.x not return the same +/-INF or NaN? Or is it returning differently (0 or 1 here I’m guessing) because of the different in precision?

btw, how can you tell when things have different precision? aren’t I using floats for both of 'em?