Wednesday, December 15, 2021

Vertex Animation Texture part 2

Soooo... we have our beautiful Vertex Animation Texture. Would look great on a shirt (seriously, try it) but we want to use it to move some pixels. So let's fire up Unity and see how far we get.

So first let's figure out what we need:
  • Main texture  (2d texture, the albedo of my animated object)
  • Animation texture (2d texture)
  • Normal texture (2d texture) Not yet implemented
  • Playback speed to control the speed of the animation
  • Scale factor (float) Even though the shader will get the correct scale factor from the texture, it's still nice to be able to scale your animation if needed
  • Start and endframe (int) Useful when you have an image with multiple animations in it. If both are 0 the shader assumes it is a single animation and uses the default _AnimTex_TexelSize 
  • Playmode (dropdown) Don't know if this will actually be handy, but added it in just in case. You can scrub through the animation, ping-pong or loop.
  • Scrub to scrub the animation when that mode is elected 

I was hoping to use uint vertID : SV_VertexID to automatically take the models vertex numbers and reading the texture that way but that messed up the model. Luckily, to create the texture in Maya, I created a new UVset which laid out all vertices from left to right in the correct column. So I use that UV coordinate to assign the correct pixel to the right vertex.

Another thing that's really important here is _AnimTex_TexelSize. This will give you the texture's pixel dimensions which gives us complete control over where the animation should start and end. Firstly, I use this to go to the middle of the first and second pixel of my first texture row and read out the RGBA values to reconstruct the correct scale value. We need this because to create the pixel color values, we had to bring all translation values between 0-1. We did this in Maya by looping through al the vertices and find the highest positional value (positive or negative) and divided all values by this. So now in the shader we need reapply this value to get back to the correct scale. Obviously this will be different for each animation, depending on the highest positional value of all the vertices.

By the way, since we're in the vertex shader we can't use tex2D, so we have to use tex2Dlod to sample our texture. 

So now that we got our scale back we can start playing this sucker. Since I'm no match prodigy, I really needed to dig in. What helped me a lot is to visualize it as a timeline. I then tried to define the pattern I wanted to create and go from there. I'll use _Time.y combined with the _AnimTex_TexelSize values and do some tricks based on that.

The default looping playmode is the easiest:



lerp(startPixelRow, endPixelRow, frac(_Time.y * _TimeSlider * timeFactor))

The timeslider is the playback speed (naming could be better now that I look at it 😅) The time factor is something I added because the overall speed seemed off. I know we can use the Playback speed to tweak this, but you don't want to have to find the sweetspot for every animation. I defined timeFactor as 30/clipLength, where 30 is the framerate and the cliplength is _AnimTex_TexelSize.y. I use frac to keep the time values between 0 and 1.

The Ping-Pong play mode was a bit trickier:

lerp(startPixelRow, endPixelRow, abs(1-fmod(_Time.y * _TimeSlider * timeFactor, 2)))

So the fmod function gives me a range between -1 and 1 and the abs flips the negative resulting in the ping pong graph you see above.

The third playmode is an easier one, we basically lose the time component and use the scrubber value.

lerp(startPixelRow, endPixelRow, startPixelRow + _TimeScrubber);

And that's it basically. I still need to look at the normals at some point, but for now this works fine.



 



No comments:

Post a Comment