Search Unity

Possible to keep parts of mesh constant while scaling it?

Discussion in 'General Graphics' started by dgoyette, Oct 18, 2019.

  1. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,196
    For convenience, some of my objects have a dynamic tiling shader on them, so that I can scale the object and have it tile, instead of appearing to stretch. For example:

    Barrier.gif

    This does stretch the mesh, but the material's shader dynamically adjusts the tiling to account for the new scale. This works well for very regular objects, but I'd like to go one step further.

    Let's say on the object I have shown above, I want to bevel the four corners instead of have right angles. Right now, if I do that, the angle of the bevel will change as I adjust the scale of the object. I was wondering if there's effective some way to do a "9 slice sprite" approach with the mesh itself. I don't know if that even really makes sense, but I picture some verts a mesh as maintaining a constant distance from each other even as other verts in the mesh get further apart or closer together when the object is scaled. Or maybe there's some other approach to what I'm trying to do. Mainly, I'd like the above object to have "bookends" on each end, with a repeated portion inside.

    Or, consider a cube with nice, rounded corners. Could you somehow scale that cube without changing the radius of the round corners?
     
  2. Olmi

    Olmi

    Joined:
    Nov 29, 2012
    Posts:
    1,553
    I think it might be easiest to make a procedurally generated mesh. After all, it's not that many vertices if you want to get 45 degree corners, for example. Just lay out the mesh vertices on paper/photoshop and then write it in code? But I would just get coordinates of the start and end position, set them to desired position and then create the end-part with a specified scale, so that it's just offset to the correct location.
     
  3. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,196
    I'll actually give that some thought. The specific use-case I had in mind is for a fairly simple object, mainly a cube with round rectangles, no custom detail, so that could actually be reasonable in this case.
     
  4. Shane_Michael

    Shane_Michael

    Joined:
    Jul 8, 2013
    Posts:
    158
    You could do it by baking a "stretch factor" into each vertex, and then pre-scaling the object space position by some "stretch amount" before you apply your regular matrix transform. The entire right bevel would be 0, the entire left bevel would be 1, and any vertex in the middle gets a value proportional to its position. You could process the mesh in Unity using a bounding box to assign the "stretch factor" to each vertex, but if you only need one or two dimensions, it may be more convenient to UV map it in a modelling program and clamp the values to (0,1) either on import or in the shader itself.

    Then define a "_StretchAmount" shader property that controls how much you want to stretch your geometry.

    If you mapped the "stretch factor" in the UV1 channel in a modelling program, it would look something like this. Anything mapped outside of the (0,1) UV-space should move together and anything inside should get scaled proportionally. Anything that needs dynamically generated UVs should be mapping exactly at or inside the 0...1 range.

    Code (csharp):
    1.  
    2. o.vertex.xy = v.vertex.xy + saturate(v.uv1.xy) * _stretchAmount.xy;
    3. // Only calculate dynamic UVs for anything that is stretched. This is a bit of ugly conditional
    4. // and would only get uglier if you were stretching along the z-axis as well
    5. // but it could be pre-calculated on mesh import into a single value
    6. // I would probably pack it into v.uv1.w
    7. o.uv0 = (v.uv1.x < 0 || v.uv1.x > 1 || v.uv1.y < 0 || v.uv1.y > 1) ? v.uv0 : CalculateDynamicUV(o.vertex.xyz);
    8. o.vertex = UnityObjectToClipPos(o.vertex);
    9.  
    And, of course, this is just off the top of my head and I have a bit of a headache right now, so it probably doesn't work exactly as written, but the general idea should be sound. It's good for anything that is too complicated to generate procedurally, or maybe has complicated hand-painted texturing for the "book ends" that you want to preserve.
     
  5. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,196
    Thanks, I'll look into this. It seems I didn't notice the notification of your reply to this thread, so I went off and came up with an approach to solving this. There's some tradeoff to it, as it requires multiple extra objects for all the pieces I want to move without being scaled, but it seems to suit my current needs well.

    I wrote up the approach on my blog here: https://www.graviagame.com/blog/viewPost/59/Unity-shader-graph-bookend-shader. The basic idea is that simply dividing the object's position by its scale will keep it in the same relative position as its parent is scaled, without actually changing the scale of the child object.