Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Need Help with Scaling Vertex Positions in Shader

Discussion in 'Shaders' started by RedVonix, Dec 4, 2018.

  1. RedVonix

    RedVonix

    Joined:
    Dec 13, 2011
    Posts:
    421
    Hey all!

    I'm working on an effect shader for my game, and as part of it I need to be able to scale up the vertexes outwards of a sprite. The scaling in my shader works perfect, however it is offsetting the position of the sprite itself, and the further the sprite is from the position of 0,0 in the world, the worse the offsetting gets. So I'm presuming I'm doing something silly in my object space.

    Here's what it currently looks like... obviously, that tomater should be inside that black box, where the handle is.


    And this is the specific code that does the actual scaling:
    Code (CSharp):
    1.         float4 wpos = mul(unity_ObjectToWorld, v.position);
    2.         wpos.x *= 2;
    3.         wpos.y *= 2;
    4.         v.position = mul(unity_WorldToObject, wpos);
    What do you think? What am I doing wrong, and what do I need to change to make it work right?

    Thanks in advance!!
     
    JayGarxP likes this.
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Lets think through this code for the case of a quad.

    The default quad's mesh has 4 vertices with local positions like this:
    (-0.5, 0.5, 0.0)
    ( 0.5, 0.5, 0.0)
    ( 0.5,-0.5, 0.0)
    (-0.5,-0.5, 0.0)


    This also means the position of (0.0, 0.0, 0.0) is conveniently the center of the quad (the average of all 4 vertex positions).

    If you scale those by 2.0, then the resulting positions will be:
    (-1.0, 1.0, 0.0)
    ( 1.0, 1.0, 0.0)
    ( 1.0,-1.0, 0.0)
    (-1.0,-1.0, 0.0)


    That's straightforward enough. The quad is now twice as large and scaled up, and the center point hasn't changed, i.e. it hasn't shifted.

    Now you're applying the object to world transform matrix. If the quad's game object transform is positioned at (0.0, 0.0, 0.0) in world space (as it is when shown in the inspector preview window), then nothing changes. However lets say you've moved the quad 1 unit along the x axis in world space. Those world space vertex positions are now:
    (0.5, 0.5, 0.0)
    (1.5, 0.5, 0.0)
    (1.5,-0.5, 0.0)
    (0.5,-0.5, 0.0)


    And if you scale that by 2.0 then:
    (1.0, 1.0, 0.0)
    (3.0, 1.0, 0.0)
    (3.0,-1.0, 0.0)
    (1.0,-1.0, 0.0)


    The quad's center is now at (2.0, 0.0, 0.0)! Scaling the positions when they're not centered also effectively moves it, and this is ignoring any rotation or scaling you might have. For the simple case of the code above, the solution is just don't use the matrix, just scale the vertices in their original space:

    float4 wpos = mul(unity_ObjectToWorld, v.position);
    wpos.x *= 2;
    wpos.y *= 2;
    v.position = mul(unity_WorldToObject, wpos);

    v.position.xy *= 2;

    But perhaps you want to scale the xy in world space and you're rotating the game object? Well then you need to recenter the vertex positions before you scale, or you could apply only the rotation and scale and not the translation.

    float3 wpos = mul((float3x3)unity_ObjectToWorld, v.position.xyz);
    wpos.xy *= 2;
    v.position.xyz = mul((float3x3)unity_WorldToObject, wpos);


    This works because the original vertex positions are already centered around zero. However, all of this is moot when using sprites, or objects that get batched as the mesh Unity uses is pre-transformed into world space. There's no way to get the original pivot as that information has been lost. You can disable batching in the shader by adding a tag to the shader:

    Tags { "Queue"="Whaterver" "RenderType"="Something" "DisableBatching"="True" }
     
    Last edited: Dec 4, 2018
  3. RedVonix

    RedVonix

    Joined:
    Dec 13, 2011
    Posts:
    421
    This is unquestionably the single most informative response I have ever seen on a post. Thank you so much for taking the time to write this up! You've given me an extremely clear understanding of the issue, and the solution. The community needs more people like you! Track me down at GDC or TGS or something and I'll buy you lunch - not joking, either. (I'm really easy to find - usually handing out badge ribbons and being super social.)
     
    Alic, trenthm and bgolus like this.
  4. trenthm

    trenthm

    Joined:
    Nov 18, 2017
    Posts:
    19
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    It's the same problem. The difference is I think it's impossible to fix with UI elements since I think they're always batched no matter what.
     
    trenthm likes this.
  6. trenthm

    trenthm

    Joined:
    Nov 18, 2017
    Posts:
    19
    Oh man. What a bummer.

    Big thanks for the response, @bgolus

    (Nearly) all of my graphics have a transparent border around them to prevent mipmap artifacts at the edges. In my case, a 512x512 texture only has 500x500 used (6px transparent border). So, to make it look right in the UI I have to scale it up by 512/500 = 1.024.

    This has gotten really messy, scaling up the transforms (or manually setting the width & height) on all the GameObjects with images.

    I thought a shader would be an elegant way to scale up images without affecting layout, etc.

    Can you think of any way to get around this?

    (Using the sprites in the game is easy. I just set Pixels Per Unit to 500 on the texture and I'm all good)
     
  7. brownboot67

    brownboot67

    Joined:
    Jan 5, 2013
    Posts:
    375
    trenthm likes this.
  8. trenthm

    trenthm

    Joined:
    Nov 18, 2017
    Posts:
    19
    Modifying the quad does seem like an elegant solution.

    I couldn't figure out how to scale the UV in the shader. (I'm very new to shaders, so not a big surprise)

    Any chance you could give UV scaling a try in my sample project? (https://github.com/trenthm/Image-Scale-Shader)
     
    Korplex likes this.