I’m in a situation where I need to know the “relative” position inside the sprite. Evertything works fine and easy when using single sprites, since I can simply refer to UV coordinates, but when I start to use sprite atlasing everything breaks up, because the texcoords contains the coordinates to the whole spritesheet.
Another thing that is giving me the headache is this: I was able to resolve the problem above by passing the size of the sprite to the shader like this
because it assumes that the geometry of the sprite is centered at the origin, but it works as long as I create new material instances for each object. If I draw evertything in a single draw call the vertices seems to be already offseted in some way, and the calculation doesn’t work anymore. (In this scenario, the calculations are correct only if I leave the object at 0,0,0 position)
Is there anyway to calculate the “relative” UV position? (providing the size of the sprite to the shader, or any other parameter needed)
I’m actually looking into this one as well, hoping someone will come along and help us out.
From what I’ve looked up, there’s no way the shader directly knows about the sprite size. According to this post that I found: unity game engine - Unity3D - Shader for sprite clipping - Stack Overflow
He/She basically explains that they way they solved it was by providing parameters to the shader to allow him to pass in the information about the current sprite being used.
I’m not sure which code he’s using to grab the sprite’s relative UV position, but at the moment in an Update method I’m trying:
So far can’t really tell if it’s working, that’s mostly because I’m not quite sure what value I’m meant to be passing into it, or even how the shader in the provided link is even checking whether it’s in the sprites bounds.
Thanks Aedous! You brought some good information. I looked at the Sprite documentation and bounds do no good in this case. It refers to the the bounds of the sprite in the world space. But…
This property refers to the texture rect in the atlas. If we pass this rect information to the shader we can get the UV bounds using _MainTex_TexelSize, which contains the size of the Atlas.
I will try it tomorrow and post the results.
Thanks for the heads up, I actually managed to get something actually looking good and working!
Current sprite UV in the shader ( which I then pass to tex2D(_MainTex2, relativePos) when drawing the second texture on top of the sprite).
By using that I passing in the Sprite.rect Width and Height, I was able to fit a texture into the shape of the current sprite I’m looking at, even with animations playing! YAY!
I noticed that if I only pass in sprite Width and Height at the start and not in the update, it actually stops the shader texture from jumping about, therefore making it more smooth :).
I think your _MainTex_TexelSize might help with the other problem I have now, which I think is similar to @gigapunk issue, which is not being able to get the relative position, therefore making the secondary texture not follow the sprite properly. How are you using this _MainTex_TexelSize?
When the sprite is in the Sprite Packer, it refers to the atlas of the image, same as the texture field in the Sprite object.
I was using as a provisory solution for my shader. Since I now the size of the atlas I can calculate safe offsets and pixel based offsets in the image, but now that I know the bounds, I can acheive better results.
Nice! thanks for the info, does that mean we don’t have to try and get the reference from the Sprite through script and can simply just get it through _MainTex_TexelSize ?
I’ve actually got mine working quite well, I’m quite new to even shader code so there might be some things that are off, but I figured out how to get the relative position using this in my shader:
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
half2 texcoord : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float3 localPos : TEXCOORD2;
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
};
v2f vert(appdata_t IN)
{
v2f OUT;
OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
float4 scaleVertex = float4(IN.vertex.xyz, 0); //By setting the last value to 0 it ignores the flipping ( loses relative position if sprite is flipped :( )
float4 wP = mul(_Object2World, scaleVertex); //Get the object to world vertex and store it
OUT.worldPos = wP.xyz; //For use in fragment shader
float4 lP = mul(_World2Object, scaleVertex); //Get the world to object vertex and store it
OUT.localPos = lP.xyz; //For use in fragment shader
OUT.texcoord = TRANSFORM_TEX(IN.texcoord, _MainTex);
OUT.color = IN.color;
return OUT;
}
I make sure that the vertex that I’m grabbing for the local and world position are not flipped, this is because the second texture then loses it’s relative position to the sprite when it’s flipped.
Finally in the frag function I calculate the relative position for the sprite with this:
fixed4 frag(v2f IN) : COLOR
{
fixed4 t = tex2D(_MainTex, IN.texcoord)*IN.color;
//Calculate relative position
fixed2 relativeWorld = fixed2(IN.worldPos.x + IN.localPos.x, IN.worldPos.y + IN.localPos.y);
//This becomes the UV for the texture I want to apply to the sprite ( using the sprites width and height )
fixed2 relativePos = fixed2((relativeWorld.x + _Width), (relativeWorld.y + _Height));
return tex2D(_MainTex_2, relativePos);
//Other shader nonsense
}
Seems to work quite well for me, but I’m sure there most be some noob things I’m doing. Will post back with any updates I find, and will try out your method with “Texel Size”.
I cannot seem to use _MainTex_TexelSize. When I use it, it just says that it doesn’t exist. How do I use it? In for example Unity’s default Sprites/Default shader?
It’s been more than one year that I solved this problem… but I think you have to declare it. You don’t have to set the value, Unity will set for you.
float4 _MainTex_TexelSize;
_MainTex = the name of your Sprite texture/sampler2D.