Unity plays sequence frame animation by offsetting UV

  Hello everyone, I am Zhao.
  Using the shader to play the sequence diagram in the Unity engine, it is estimated that many people have used it, and I have written several versions myself. Here is a brief introduction.

1. Principle

  Let me talk about the purpose first, I now have a picture like this:
insert image description here

  On this picture, there are 9 grids, which can be understood as 9 sequence frames of an animation. Next, by writing a simple Shader, they are displayed one by one in order to form a circular animation:
insert image description here

There is such a node in ASE that plays sequence frame animation directly, called Flipbool UV Animation node:
insert image description here

It can be seen from the nodes that to do this UV animation, the parameters required are:
1. The original UV coordinates
2. The number of rows and columns of the sequence diagram. For example, the diagram I just had is a 3x3 determinant
.
The frame starts to play
5. The current playing time.
Then the returned result is a new UV coordinate.
  So in principle, this sequence frame playback is actually based on the current time to calculate the number of frames that need to be played, and then calculate the UV of the picture where the first frame is intercepted through the number of rows and columns. coordinates, and return.

2. Implemented code

  I have written several versions of the code for this UV sequence frame animation, but I feel that ASE still looks more standard, so I refer to ASE's Flipbool UV Animation node and translate it into a method:

float2 GetSequenceAnimUV(float2 uv,float cols,float rows,float speed,float startFrame)
{
	float totalTiles = cols * rows;

	float colsOffset = 1.0f / cols;
	float rowsOffset = 1.0f / rows;
	float speedVal = _Time.y * speed;
	float2 offsetTiling = float2(colsOffset, rowsOffset);
	float currentIndex = round(fmod(speedVal + startFrame, totalTiles));
	currentIndex += (currentIndex < 0) ? totalTiles : 0;
	float lineNum = round(fmod(currentIndex, cols));
	float offsetX = lineNum * colsOffset;
	float rowCount = round(fmod((currentIndex - lineNum) / cols, rows));
	rowCount = (int)(rows - 1) - rowCount;
	float offsetY = rowCount * rowsOffset;
	float2 offsetXY = float2(offsetX, offsetY);
	float2 result = uv*offsetTiling +offsetXY;
	return result;
}

  When using it, after passing in the parameters of uv, number of rows and columns, speed, and start frame, you can return a UV of the current frame, and then use this UV to sample the entire picture;

3. Extended application

1. Control the passage of time by yourself

As can be seen from the above code, this sequence frame animation will play by itself because _Time.y is used, which is a time, representing the time from the completion of loading the scene to the current time, and it will increase by itself.
If you don't want to use the time of this system, but control the time by yourself, there are two ways:

1. Change the speed parameter

speed can be positive, negative or 0. When the speed is larger, the playback will be faster. When the speed is negative, the animation will play backwards. When the speed is 0, the animation playback stops.
But I think the speed parameter is just a means to control the playback speed, not a way to control the time.

2. Control the time yourself

  This method is, instead of using _Time.y, pass in the timeVal parameter yourself. In this way, we need to maintain a time variable in other scripts such as C#.
  The advantage of this is that we can arbitrarily jump to a certain point in time without changing the normal playback speed. For example, now we need to make a time regression effect, and suddenly the whole world goes back to the previous few seconds. By uniformly passing in a certain timestamp, all animations can be rolled back to the previous state together.
  Of course, the time parameter can also be used to achieve acceleration, deceleration and pause.
The way to control the time parameter yourself is as follows:

float2 GetSequenceAnimUVByTime(float2 uv, float cols, float rows, float speed, float startFrame,float timeVal)
{
	float totalTiles = cols * rows;

	float colsOffset = 1.0f / cols;
	float rowsOffset = 1.0f / rows;
	float speedVal = timeVal * speed;
	float2 offsetTiling = float2(colsOffset, rowsOffset);
	float currentIndex = round(fmod(speedVal + startFrame, totalTiles));
	currentIndex += (currentIndex < 0) ? totalTiles : 0;
	float lineNum = round(fmod(currentIndex, cols));
	float offsetX = lineNum * colsOffset;
	float rowCount = round(fmod((currentIndex - lineNum) / cols, rows));
	rowCount = (int)(rows - 1) - rowCount;
	float offsetY = rowCount * rowsOffset;
	float2 offsetXY = float2(offsetX, offsetY);
	float2 result = uv * offsetTiling + offsetXY;
	return result;
}

2. Control the playback of specified frames by yourself

  Sometimes when doing some special sequence frame animations, it is necessary to play a specific and accurate frame according to the situation, or repeat between certain frames.
  In such a situation, if Time is used as the control, it seems that it is not suitable. Therefore, according to the actual situation, you can remove the incoming speed parameter and time parameter, and change it to the incoming frame you want to play:

float2 GetSequenceAnimUVByIndex(float2 uv, float cols, float rows, float currentFrame)
{
	float totalTiles = cols * rows;

	float colsOffset = 1.0f / cols;
	float rowsOffset = 1.0f / rows;
	float2 offsetTiling = float2(colsOffset, rowsOffset);
	float currentIndex = currentFrame;
	currentIndex += (currentIndex < 0) ? totalTiles : 0;
	float lineNum = round(fmod(currentIndex, cols));
	float offsetX = lineNum * colsOffset;
	float rowCount = round(fmod((currentIndex - lineNum) / cols, rows));
	rowCount = (int)(rows - 1) - rowCount;
	float offsetY = rowCount * rowsOffset;
	float2 offsetXY = float2(offsetX, offsetY);
	float2 result = uv * offsetTiling + offsetXY;
	return result;
}

Guess you like

Origin blog.csdn.net/liweizhao/article/details/132635716