{"id":453,"date":"2021-07-10T17:06:03","date_gmt":"2021-07-10T17:06:03","guid":{"rendered":"https:\/\/bronsonzgeb.com\/?p=453"},"modified":"2021-07-10T17:06:04","modified_gmt":"2021-07-10T17:06:04","slug":"mesh-deformation-in-unity","status":"publish","type":"post","link":"https:\/\/bronsonzgeb.com\/index.php\/2021\/07\/10\/mesh-deformation-in-unity\/","title":{"rendered":"Mesh Deformation in Unity"},"content":{"rendered":"\n<p>In this article, I explore mesh deformation using a custom vertex shader. Andy Saia&#8217;s GDC talk on mobile effects inspired this post. If you&#8217;re interested, I linked that talk at the end of the post.<\/p>\n\n\n\n<p>I&#8217;m a bit embarrassed to admit that I didn&#8217;t check if there was a companion project for the GDC presentation until after I wrote my version. So imagine my surprise when I found that project! Consequently, this is my recreation of the technique based on Andy&#8217;s description in the video. Let&#8217;s dive in!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How does mesh deformation work?<\/h2>\n\n\n\n<p>We define the stretch of the mesh using an anchor and a manipulator. The anchor is a transform that determines the resting position and the point from which we&#8217;re stretching. The manipulator is a transform to calculate the delta, or in other words, how much we&#8217;re pulling relative to the anchor. For example, if you stretched your cheek, the anchor is the point where you grab your cheek before pulling it, and the manipulator is the new position after pulling your cheek.<\/p>\n\n\n\n<p>After defining these two transforms, we&#8217;ll create a transformation matrix to represent the move, scale, and rotation from the anchor to the manipulator. After we pass this matrix into the vertex shader, we can use it to displace vertices. The original project uses a sphere falloff function to determine which vertices to move. In other words, any vertices within a given distance from the anchor will move. We&#8217;ll use that same approach for now, but it would be interesting to use our voxel-based falloff from a&nbsp;<a rel=\"noreferrer noopener\" target=\"_blank\" href=\"https:\/\/bronsonzgeb.com\/index.php\/2021\/06\/26\/volumetric-heat-diffusion-for-automatic-mesh-skinning\/\">previous post<\/a>&nbsp;in a future update.<\/p>\n\n\n\n<p>The last step is to recalculate new normals. If we move the vertices without recalculating the normals, we&#8217;ll see the original lighting, which is wrong. To recalculate the normals, we take a point in space along the vertex tangent and transform it with the manipulator transformation matrix. Then, we calculate the vertex binormal and do the same to that. Next, we use these new points to calculate our new tangent and binormal, which we use to calculate our final normal. I break this down into more detail further down.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Creating the transformation matrix<\/h2>\n\n\n\n<p>Create a new C# script called <code>Manipulator.cs<\/code> to start. As previously mentioned, we need an anchor and a handle to start. Additionally, we need a reference to the mesh&#8217;s renderer. Why? We&#8217;ll use the renderer to access the material and set shader variables like the transformation matrix, the anchor position, etc.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class Manipulator : MonoBehaviour\n{\n    public Transform Anchor;\n    public Transform Handle;\n    public Renderer Renderer;\n\n    static readonly int TransformationMatrixId = Shader.PropertyToID(\"_TransformationMatrix\");\n    static readonly int AnchorPositionId = Shader.PropertyToID(\"_AnchorPosition\");\n\n    void Update()\n    {\n        var transformationMatrix = Handle.localToWorldMatrix * Anchor.worldToLocalMatrix;\n        \n        var softbodyMaterial = Renderer.sharedMaterial;\n        \n        softbodyMaterial.SetMatrix(TransformationMatrixId, transformationMatrix);\n        softbodyMaterial.SetVector(AnchorPositionId, Anchor.position);\n    }\n}<\/code><\/pre>\n\n\n\n<p>The transformation matrix is straightforward; it converts from the anchor&#8217;s local space into the handle&#8217;s local space. We create the matrix by multiplying the handle <code>localToWorldMatrix<\/code> by the anchor <code>worldToLocalMatrix<\/code>. Then, grab the material from the object&#8217;s renderer and set the transformation matrix and anchor position (in world space) on the shader.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">&#8220;Soft body&#8221; shader<\/h2>\n\n\n\n<p>Time to write the shader. I created a surface shader because we&#8217;re focusing on the vertex shader, and I don&#8217;t want to worry about the other details. You could also use Shader Graph and write a custom vertex shader with a custom node. In the surface shader, modify the pragma statement to specify a vertex function.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#pragma surface surf Standard <strong>vertex:vert<\/strong> fullforwardshadows addshadow<\/code><\/pre>\n\n\n\n<p>Then, add the new fields that we set from C#.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sampler2D _MainTex;\nhalf _Glossiness;\nhalf _Metallic;\nfixed4 _Color;\n<strong>float4x4 _TransformationMatrix;\nfloat4 _AnchorPosition;<\/strong><\/code><\/pre>\n\n\n\n<p>Finally, add the vertex function.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void vert(inout appdata_full v, out Input data)\n{\n    UNITY_INITIALIZE_OUTPUT(Input, data);\n    \n    float4 vertexPositionWS = mul(unity_ObjectToWorld, v.vertex);\n    float3 manipulatedPositionWS = ApplyManipulator(vertexPositionWS, _TransformationMatrix, _AnchorPosition, 1.0, 0.1);\n    v.vertex = mul(unity_WorldToObject, float4(manipulatedPositionWS, 1));\n}<\/code><\/pre>\n\n\n\n<p>Of course, we haven&#8217;t written the <code>ApplyManipulator<\/code> method yet, so let&#8217;s do that. By the way, I copied this method from the GDC talk. Thanks, Andy.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float3 ApplyManipulator(float3 position, float4x4 transformationMatrix, float3 anchorPosition, float maskRadius, float maskHardness)\n{\n    float3 manipulatedPosition = mul(transformationMatrix, float4(position, 1)).xyz;\n\n    const float falloff = SphereMask(position, anchorPosition, maskRadius, maskHardness);\n    manipulatedPosition = lerp(position, manipulatedPosition, falloff);\n    \n    return manipulatedPosition;\n}<\/code><\/pre>\n\n\n\n<p>Primarily, all we&#8217;re doing is multiplying our vertex position by the <code>_TransformationMatrix<\/code>. However, we also add a falloff based on a sphere mask. The reason is that otherwise, every single vertex would move with our manipulator, and as a result, we&#8217;d just be manipulating the entire mesh. The falloff defines a radius from the anchor position, where only vertices within that radius are affected. You&#8217;re undoubtedly wondering what the <code>SphereMask<\/code> function looks like too, so here it is.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float SphereMask(float3 position, float3 center, float radius, float hardness)\n{\n    return 1 - saturate((distance(position, center) - radius) \/ (1 - hardness));\n}<\/code><\/pre>\n\n\n\n<p>That&#8217;s all we need to start pulling stuff around. If you want to try it, create a new material with the soft body shader and attach it to an object. Then, set up a manipulator component and give it an anchor and a handle (two empty transforms will do). Finally, set the reference to the renderer with the correct material. Now, if you enter Playmode, you can drag the handle around and see the results. I recommend attaching physics springs to the handle as well to get some fun physical behaviour. I created a dripping nose that you can play with in the Github project linked at the end of the post.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"902\" height=\"507\" src=\"https:\/\/bronsonzgeb.com\/wp-content\/uploads\/2021\/07\/Drip.png\" alt=\"\" class=\"wp-image-456\" srcset=\"https:\/\/bronsonzgeb.com\/wp-content\/uploads\/2021\/07\/Drip.png 902w, https:\/\/bronsonzgeb.com\/wp-content\/uploads\/2021\/07\/Drip-300x169.png 300w, https:\/\/bronsonzgeb.com\/wp-content\/uploads\/2021\/07\/Drip-768x432.png 768w\" sizes=\"auto, (max-width: 902px) 100vw, 902px\" \/><figcaption>Using a capsule and a spring I made this drippy nose<\/figcaption><\/figure>\n\n\n\n<p>As I mentioned before, the current setup doesn&#8217;t correct the normals when the vertices move, which leads to incorrect lighting. Let&#8217;s fix that next.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Fixing the lighting<\/h2>\n\n\n\n<p>We&#8217;ll calculate new normals based on the modified position of our vertices in the vertex shader. To do so, we need to understand the relationship between a vertex&#8217;s normal, tangent and binormal. A vertex normal is a direction that points away from a vertex. We compare this direction to the oncoming light direction to determine how much this part of the mesh faces the light. The more it faces the light, the more lit it is. The tangent is a direction along the surface of the mesh. The binormal is a direction that&#8217;s perpendicular to the normal and the tangent. In other words, imagine a little translation gizmo at your vertex position with the Y-axis pointing along the normal and the X-axis pointing along the tangent. In this case, the Z-axis would point along the binormal. Why does this matter? Because we&#8217;re going to use the tangent and binormal to calculate a new normal.<\/p>\n\n\n\n<p>Let&#8217;s start by calculating a new tangent. Here&#8217;s how we do this:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Convert the existing tangent to world space.<\/li><li>Calculate a position from the vertex position to an arbitrary distance away in the tangent direction.<\/li><li>Apply the manipulator to this position.<\/li><li>Calculate the direction from the manipulated vertex position to the manipulated tangent position, our new tangent.<\/li><\/ol>\n\n\n\n<p>Here&#8217;s the code for that.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float3 tangentWS = UnityObjectToWorldDir(v.tangent);\nfloat3 manipulatedTangentWS = ApplyManipulator(vertexPositionWS + tangentWS * 0.01, _TransformationMatrix, _AnchorPosition, _Radius, _Hardness);\nfloat3 finalTangent = normalize(manipulatedTangentWS - manipulatedPositionWS);\nv.tangent = float4(UnityWorldToObjectDir(finalTangent), v.tangent.w);<\/code><\/pre>\n\n\n\n<p>The process for the binormal is similar, except we have to calculate the binormal first because Unity doesn&#8217;t store it. To calculate the binormal, take the cross product of the normal and the tangent, and multiply it by the tangent&#8217;s w value. Unity stores either -1 or 1 in the tangent&#8217;s w to signify the binormal direction, which changes depending on the renderer. Otherwise, the process is the same.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float3 binormal = cross(normalize(v.normal), normalize(v.tangent.xyz)) * v.tangent.w;\nfloat3 binormalWS = UnityObjectToWorldDir(binormal);\nfloat3 manipulatedBinormalWS = ApplyManipulator(vertexPositionWS + binormalWS * 0.01, _TransformationMatrix, _AnchorPosition, _Radius, _Hardness);\nfloat3 finalBinormal = normalize(manipulatedBinormalWS - manipulatedPositionWS);<\/code><\/pre>\n\n\n\n<p>The last step is to calculate the final normal. All we do is take the cross product of the manipulated tangent by the manipulated normal and multiply that by our tangent&#8217;s w component. In case you forgot, the cross product of two vectors returns a new perpendicular vector.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float3 finalNormal = normalize(cross(finalTangent, finalBinormal)) * v.tangent.w;\nv.normal = UnityWorldToObjectDir(finalNormal);<\/code><\/pre>\n\n\n\n<p>Put it all together for the final vertex function.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void vert(inout appdata_full v, out Input data)\n{\n    UNITY_INITIALIZE_OUTPUT(Input, data);\n    \n    float4 vertexPositionWS = mul(unity_ObjectToWorld, v.vertex);\n    float3 manipulatedPositionWS = ApplyManipulator(vertexPositionWS, _TransformationMatrix, _AnchorPosition, _Radius, _Hardness);\n    v.vertex = mul(unity_WorldToObject, float4(manipulatedPositionWS, 1));\n\n    float3 tangentWS = UnityObjectToWorldDir(v.tangent);\n    float3 manipulatedTangentWS = ApplyManipulator(vertexPositionWS + tangentWS * 0.01, _TransformationMatrix, _AnchorPosition, _Radius, _Hardness);\n    float3 finalTangent = normalize(manipulatedTangentWS - manipulatedPositionWS);\n    v.tangent = float4(UnityWorldToObjectDir(finalTangent), v.tangent.w);\n\n    float3 binormal = cross(normalize(v.normal), normalize(v.tangent.xyz)) * v.tangent.w;\n    float3 binormalWS = UnityObjectToWorldDir(binormal);\n    float3 manipulatedBinormalWS = ApplyManipulator(vertexPositionWS + binormalWS * 0.01, _TransformationMatrix, _AnchorPosition, _Radius, _Hardness);\n    float3 finalBinormal = normalize(manipulatedBinormalWS - manipulatedPositionWS);\n    float3 finalNormal = normalize(cross(finalTangent, finalBinormal)) * v.tangent.w;\n    v.normal = UnityWorldToObjectDir(finalNormal);\n}<\/code><\/pre>\n\n\n\n<p>If you wanted to use Shader Graph, you could convert this code block into a custom node or subgraph. With that sorted, we can play with the new squishy meshes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Adding Mouse Control<\/h2>\n\n\n\n<p>I created a new script called <code>ManipulatorMouseControl.cs<\/code> and added it to the camera in my scene. In this script, on mouse click, we raycast from the camera into the scene. If we hit an object and that object has a <code>Renderer<\/code> with our custom material, we can manipulate it. So then, create a new manipulator and two new transforms, the anchor and the handle. Place both of the new transforms at the raycast hit point, and assign the handle, anchor and renderer in the newly created manipulator. At this point, we drag the handle around as we move the mouse.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"902\" height=\"507\" src=\"https:\/\/bronsonzgeb.com\/wp-content\/uploads\/2021\/07\/Wario.png\" alt=\"\" class=\"wp-image-457\" srcset=\"https:\/\/bronsonzgeb.com\/wp-content\/uploads\/2021\/07\/Wario.png 902w, https:\/\/bronsonzgeb.com\/wp-content\/uploads\/2021\/07\/Wario-300x169.png 300w, https:\/\/bronsonzgeb.com\/wp-content\/uploads\/2021\/07\/Wario-768x432.png 768w\" sizes=\"auto, (max-width: 902px) 100vw, 902px\" \/><\/figure>\n\n\n\n<p>Admittedly this system is simplistic, but it demonstrates interactivity. If I had more time, I would add interactable springs and other physics joints.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class ManipulatorMouseControl : MonoBehaviour\n{\n    public Camera Camera;\n    public float Radius = 1f;\n    &#91;Range(0, 1)] public float Hardness = 0.1f;\n\n    Manipulator _manipulator;\n    GameObject _manipulatorAnchor;\n    GameObject _manipulatorHandle;\n\n    Vector3 _prevMousePosition;\n\n    bool _dragging;\n\n    void Update()\n    {\n        if (Input.GetMouseButtonDown(0))\n        {\n            var ray = Camera.ScreenPointToRay(Input.mousePosition);\n\n            if (Physics.Raycast(ray, out RaycastHit hit, 100f))\n            {\n                var hitRenderer = hit.collider.GetComponentInChildren&lt;Renderer&gt;();\n                if (hitRenderer != null)\n                {\n                    _manipulator = gameObject.AddComponent&lt;Manipulator&gt;();\n                    _manipulatorAnchor = new GameObject(\"MouseAnchor\");\n                    _manipulatorAnchor.transform.position = hit.point;\n\n                    _manipulatorHandle = new GameObject(\"MouseHandle\");\n                    _manipulatorHandle.transform.position = _manipulatorAnchor.transform.position;\n\n                    _manipulator.Anchor = _manipulatorAnchor.transform;\n                    _manipulator.Handle = _manipulatorHandle.transform;\n                    _manipulator.Renderer = hitRenderer;\n                    _manipulator.Hardness = Hardness;\n                    _manipulator.Radius = Radius;\n\n                    _prevMousePosition = Input.mousePosition;\n                    _dragging = true;\n                }\n            }\n        }\n        else if (_dragging &amp;&amp; Input.GetMouseButton(0))\n        {\n            var mouseDelta = Input.mousePosition - _prevMousePosition;\n            _manipulatorHandle.transform.Translate(mouseDelta * 0.01f);\n            _prevMousePosition = Input.mousePosition;\n        }\n        else if (Input.GetMouseButtonUp(0))\n        {\n            if (_dragging)\n            {\n                Destroy(_manipulator);\n                Destroy(_manipulatorAnchor);\n                Destroy(_manipulatorHandle);\n            }\n\n            _dragging = false;\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Closing Thoughts<\/h2>\n\n\n\n<p>That wraps up this experiment. Initially, I wondered if this was useful because you could achieve the same results by manipulating bones on a skinned mesh. However, this system allows us to define bone-like behaviour at runtime, which opens the door for new types of interactivity. Additionally, I think we could turn this into a simple sculpting tool with a bit more work. But let&#8217;s save that for a future project.<\/p>\n\n\n\n<p><strong>Play with the project&nbsp;<\/strong><a rel=\"noreferrer noopener\" target=\"_blank\" href=\"https:\/\/github.com\/bzgeb\/UnitySoftbodyDeformation\"><strong>here<\/strong><\/a><strong>&nbsp;on GitHub. Check out the inspiration for this post, Andy Saia&#8217;s GDC presentation,&nbsp;<\/strong><a rel=\"noreferrer noopener\" target=\"_blank\" href=\"https:\/\/www.youtube.com\/watch?v=c7HBxBfCsas\"><strong>here<\/strong><\/a><strong>. If you appreciate my work, why not&nbsp;<\/strong><a rel=\"noreferrer noopener\" target=\"_blank\" href=\"https:\/\/bronsonzgeb.com\/index.php\/join-my-mailing-list\/\"><strong>join my mailing list<\/strong><\/a><strong>? If you do, I&#8217;ll notify you whenever I release a new post.<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this article, I explore mesh deformation using a custom vertex shader. Andy Saia&#8217;s GDC talk on mobile effects inspired this post. If you&#8217;re interested, I linked that talk at the end of the post. I&#8217;m a bit embarrassed to admit that I didn&#8217;t check if there was a companion project for the GDC presentation [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":457,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[17,1],"tags":[8,37,39,38,5],"class_list":["post-453","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-graphics","category-unity-programming","tag-game-development","tag-mesh-deformation","tag-shader","tag-soft-body","tag-unity"],"_links":{"self":[{"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/posts\/453","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/comments?post=453"}],"version-history":[{"count":4,"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/posts\/453\/revisions"}],"predecessor-version":[{"id":459,"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/posts\/453\/revisions\/459"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/media\/457"}],"wp:attachment":[{"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/media?parent=453"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/categories?post=453"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/tags?post=453"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}