Stretching when using world coordinates

Hi, I’ve just started to work with shaders and I am completely lost.

I’ve now created a vertex shader (?) that uses world coordinates, but they only work in one direction and appear streched when viewed from the side (1)

I’ve done some research already and this code should fix the issue

			float3 Pos = IN.worldPos / (-1.0 * abs(_UVs));

			float3 c1 = tex2D(_MainTex, Pos.yz).rgb;
			float3 c2 = tex2D(_MainTex, Pos.xz).rgb;
			float3 c3 = tex2D(_MainTex, Pos.xy).rgb;

		    float alpha21 = abs(IN.worldNormal.x);
			float alpha23 = abs(IN.worldNormal.z);

			float3 c21 = lerp(c2, c1, alpha21).rgb;
			float3 c23 = lerp(c21, c3, alpha23).rgb;

But I can’t find a way to implement this in my code.

This is what my code looks this far:

Shader "Custom/TextureMask"
{
	Properties
	{
		_MainTex("Texture", 2D) = "white" {}
		_UVs("UV Scale", float) = 1.0
		_CutOff("CutOff", Range(0,1)) = 0
	}
		SubShader
		{
			LOD 100
			Blend One OneMinusSrcAlpha
			Tags { "Queue" = "Geometry-1" }  // Write to the stencil buffer before drawing any geometry to the screen
			ColorMask 0 // Don't write to any colour channels
			ZWrite Off // Don't write to the Depth buffer
			// Write the value 1 to the stencil buffer
			Stencil
			{
				Ref 1
				Comp Always
				Pass Replace
			}

			Pass
			{
				CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag

				#include "UnityCG.cginc"

				struct appdata
				{
					float2 uv : TEXCOORD0;
					float4 position : POSITION;
					float3 normal : NORMAL;
				};

				struct v2f
				{
					float2 uv : TEXCOORD0;
					float4 position : SV_POSITION;
					float3 worldSpacePos : TEXCOORD1;
					float3 normal : NORMAL;
				};

				sampler2D _MainTex;
				float4 _MainTex_ST;
				float _CutOff;
				float _UVs;

				v2f vert(appdata v)
				{
					v2f o;
					o.position = UnityObjectToClipPos(v.position);
					o.worldSpacePos = mul(unity_ObjectToWorld, v.position);
					o.uv = TRANSFORM_TEX(v.uv, _MainTex);

					return o;
				}

				fixed4 frag(v2f i) : SV_Target
				{
					fixed4 col = tex2D(_MainTex, i.worldSpacePos);
					float dissolve = step(col, _CutOff);
					clip(_CutOff - dissolve);
					return float4(1, 1, 1, 1) * dissolve;
				}
				ENDCG
			}
		}

Also I’ve just realized that I get a warning “Output value ‘vert’ is not completely initialized at line 82 (on d3d11)” that I don’t know how to fix.

Any help would be greatly appreciated!

First of all the warning you get comes from the fact that you did not assign a value to all the fields of your output structure inside your “vert” function. Specifically you did not assign the “normal”.

About your primary question: First of all I would not recommend using such UV coordinates. They would only work if the texture wrap mode is set to repeat. Though you most likely will get interpolation issues if the object / mesh is far from the world origin. You should manually grab the fractional part of your worldspace coordinate and turn that into your 3d uv coordinate. Since the uv coordinate and all other vertex attributes are interpolated linearly across a triangle, it’s best to calculate the new UV in the vertex shader and pass it along to the fragment shader.

struct v2f
{
    float3 uv : TEXCOORD0;
    float4 position : SV_POSITION;
    float3 normal : NORMAL;
};

v2f vert(appdata v)
{
    v2f o;
    o.position = UnityObjectToClipPos(v.position);
    o.normal = UnityObjectToWorldNormal(v.normal);
    float3 worldSpacePos = mul(unity_ObjectToWorld, v.position).xyz;
    o.uv = frac(worldSpacePos / (-1.0 * abs(_UVs)));
    return o;
}

fixed4 frag(v2f i) : SV_Target
{
    float3 c1 = tex2D(_MainTex, i.uv.yz).rgb;
    float3 c2 = tex2D(_MainTex, i.uv.xz).rgb;
    float3 c3 = tex2D(_MainTex, i.uv.xy).rgb;

    float alpha21 = abs(i.normal.x);
    float alpha23 = abs(i.normal.z);

    float3 c21 = lerp(c2, c1, alpha21).rgb;
    float3 col = lerp(c21, c3, alpha23).rgb;

    float dissolve = step(col, _CutOff);
    clip(_CutOff - dissolve);
    return float4(1, 1, 1, 1) * dissolve;
}

Note I haven’t tested any of this as I don’t have Unity at hand ^^. Note that instead of worldspace position bound UV coordinates you may want to use the objectspace position instead unless you really want to base it on the world position. Keep in mind that when you rotate or move the object the UV mapping would change. Using object coordinates the object would keep it’s UV mapping, even when you move or rotate the object. If you want to do that, you can simply replace this line:

    o.uv = frac(worldSpacePos / (-1.0 * abs(_UVs)));

by this one

    o.uv = frac(v.position.xyz / (-1.0 * abs(_UVs)));

Though when doing that you also want to use the object space normal instead of the worldspace normal. Otherwise the texture mapping would also change when the object rotates.

ps: The “frac” function simply returns the fractional part and throws away the integer part. So if you have a position of 205.45 you get back 0.45. Note that according to the documentation frac always returns the positive fractional part. So a value of -54.12 would yield a value of 0.88. This ensures linearity around the origin.

pps: Unity has a similar tri planar shader example in the documentation.

Massive thanks now everything is working exactly as I wanted it to!

1

This is the code if anyone wants to copy that:

Shader "Custom/StencilMask"
{
	Properties
	{
		_MainTex("Tiling Texture", 2D) = "white" {}	//Tiled texture, preferably noise
		_MainTex2("Mask", 2D) = "white" {}			//Mask, center to border is white to black
		_UVs("UV Scale", float) = 1.0
		_CutOff("CutOff", Range(0,2)) = 1
	}
	SubShader
	{
		LOD 100
		Blend One OneMinusSrcAlpha
		Tags { "Queue" = "Geometry-1" }  // Write to the stencil buffer before drawing any geometry to the screen
		ColorMask 0 // Don't write to any colour channels
		ZWrite Off // Don't write to the Depth buffer

		// Write the value 1 to the stencil buffer
		Stencil
		{
			Ref 1
			Comp Always
			Pass Replace
		}

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			struct appdata
			{
				float3 uv : TEXCOORD0;
				float3 uv2 : TEXCOORD1;
				float4 position : POSITION;
				float3 normal : NORMAL;
			};

			sampler2D _MainTex;
			sampler2D _MainTex2;
			float4 _MainTex_ST;
			float4 _MainTex2_ST;
			float _CutOff;
			float _UVs;

			struct v2f
			{
				float3 uv : TEXCOORD0;
				float3 uv2 : TEXCOORD1;
				float4 position : SV_POSITION;
				float3 normal : NORMAL;
			};

			v2f vert(appdata v)
			{
				v2f o;
				o.position = UnityObjectToClipPos(v.position);
				o.normal = UnityObjectToWorldNormal(v.normal);
				float3 worldSpacePos = mul(unity_ObjectToWorld, v.position);
				o.uv = worldSpacePos / (-1.0 * abs(_UVs));
				o.uv2 = v.uv2;
				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				float3 c1 = tex2D(_MainTex, i.uv.yz).rgb;
				float3 c2 = tex2D(_MainTex, i.uv.xz).rgb;
				float3 c3 = tex2D(_MainTex, i.uv.xy).rgb;
				float alpha21 = abs(i.normal.x);
				float alpha23 = abs(i.normal.z);

				float3 c21 = lerp(c2, c1, alpha21).rgb;
				float3 c22 = lerp(c21, c3, alpha23).rgb;

				float3 col = c22 + tex2D(_MainTex2, i.uv2); //Adds the mask
				float dissolve = step(col, _CutOff);
				clip(col - _CutOff);

				return float4(1, 1, 1, 1) * dissolve;
			}
			ENDCG
		}
	}
}

I’ve ended up removing the frac function because this caused a weird oscillating effect.