"Flat" texture following the object.

I need to use a flat (flat on screen space) texture on the object and I want it to move, more or less, with the object. Seems simple:

v2f o;
  o.vertex = UnityObjectToClipPos(v.vertex);
  o.zero = UnityObjectToClipPos(float4(0, 0, 0, 1));
  o.zero = ComputeScreenPos(o.zero);
}
fixed4 frag (v2f i) : SV_Target
            {float4 oTex = min(1,tex2D(_MainTex, (i.vertex.xy  * _InstTexScale / 1000) - (i.zero.xy /= i.zero.w)));
}

But it acts weird. There’s a factor missing. I don’t know what it should be. On one object I needed to float4 oTex = min(1,tex2D(_MainTex, (i.vertex.xy * _InstTexScale / 1000) - float2(1,0.5)* (i.zero.xy /= i.zero.w))); on the same object with a different shader including the same piece of code the multiplication factors were different, like: float2(2.5, 1.5).
It doesn’t depend on texture size, it doesn’t depend on object… i don’t know what is it.

…or what is the simplest way to add “flat” texture to the object ?

You’re close, but the o.vertex value set in the vertex shader isn’t the same as the one the fragment shader get. In the vertex shader it’s the clip space position, and in the fragment shader it’s the pixel position since the vertex shader output is the value the GPU uses to calculate the pixel position. Neither of those match the value calculated by the ComputeScreenPos function from the clip space position as it’s a rescaling of the clip space position.

So basically none of the values quite match up. The easiest solution would be do something like this:

// vert
o.vertex = UnityObjectToClipPos(v.vertex);
o.screenPos = ComputeScreenPos(o.vertex);
o.screenPos.xy /= o.screenPos.w;

float4 center = ComputeScreenPos(UnityObjectToClipPos(float4(0, 0, 0, 1)));
center.xy /= center.w;

o.screenPos.xy -= center.xy;
o.screenPos.xy *= o.screenPos.w;

// frag
fixed4 tex = tex2D(_Tex, i.screenPos.xy / i.screenPos.w);
1 Like

oh thank you! it works.
i didn’t really know what does the SV_POSITION do. it does something more than just multiplying coordinate by the transformation matrix. but I, instead of calculating the screenPos of the vertex, was trying to do something what SV_POSITION does, with the origin.

Yeah, converting from clip space to pixel space in the fragment shader is possible. But it has to be exactly what the GPU does to work. This depends on the platform and what your render target settings are, so it can’t be difficult to impossible to do fully in a shader since not all of the data you need is necessarily available to the shader by default.

Hmmm… this seems to work fine, but… not always.
I have no idea why on some objects it works differently.
the gears and the tree share the same material. on the tree (and on the majority of objects) it works fine but on gears the texture moves fast in the game mode (in the scene window works fine). Every shader contaning the code you gave me behave the similar way on this object.
What’s the difference in calculating the screen result by a shader in scene and game mode? maybe I should start debugging there.
There must be something wrong with those objects. they were made in blender…

EDIT: it was something wrong with those object. they were prefabs made in previous versions of unity and something with update went wrong.

1 Like

My guess is you have the gears set to be static, which means they’re getting statically batched, which means it looses its pivot information since batched meshes are merged into one single mesh with the pivot at the world origin.