- Home /
Changing the Play Rate (Frequency) of a sine wave in real-time
I am trying to create a shader similar to the one used in ABZU to animate fish. So far I have this: https://gyazo.com/e5de88a8292bbd6a99ebfa6d69ab2ed8
And here is my code so far:
v.vertex.x += sin((v.vertex.z + _Time.y * _PlayRate) * _Frequency) * _RollAmplitude;
if (v.vertex.z > _MaskOffset)
v.vertex.x += sin((.05 + _Time.y * _PlayRate) * _Frequency) * _TranslateAmplitude * _MaskOffset;
else
v.vertex.x += sin((v.vertex.z + _Time.y * _PlayRate) * _Frequency) * _TranslateAmplitude * v.vertex.z;
I have the movement down fine, but I want to be able to change the "Play Rate" of the "animation" that the shader is making in real-time based on what the fish's actual speed is. Doing this now, creates a jitter, seen here: https://gyazo.com/c7a1c0c4b0c8ead8cac9523dea55b57c
This gif doesn't do it justice. There is much more jerkiness when changing the play rate variable. I have been trying a bunch of different solutions including using a different variable for time and such, but I can't seem to get it. Many solutions I find are for C# scripts and can't be translated to the shaders well, if at all. Is there any way to get a sooth transition in the shader?
Answer by elenzil · Jan 21, 2018 at 08:16 PM
this is a common issue.
sin(t * rate) will naturally give you significant discontinuities when t is non-zero and rate changes.
i'm not sure about doing this in a shader context, but in general code you should instead keep a separate variable that increments as dt * rate.
so eg
private float _f = 0.
....
void updateStuff():
_f += dt * rate.
displacement = sin(f).
apologies for the brevity & formatting, i'm on mobile
Unfortunately, this solution does not work for me. I have tried to implement this countless times and I still cannot get a smooth transition. I have also tried to use delta time ins$$anonymous$$d of time, but that renders my fish almost completely static, except for a little twitch every now and then. I wonder If I am maybe putting the sin(f) in the wrong place? Either way, thank you for your reply.
hm. well the key is you have a persistent variable which increments each frame according to dt times rate.
I think there are some misunderstandings on both sides here. The code the OP posted is inside a shader while the code elenzil provided is not inside a shader but inside a script. The key is that you should not pass the rate into the shader but ins$$anonymous$$d pass your own "time" to the shader. This time you would update in a script and pass it to the shader. Inside your shader you would get rid of your rate multiplier and use your own time ins$$anonymous$$d of Unity's _Time.y
variable.
It would only work that way since you can't update a persistent variable inside a shader.
Thank you so much @Bunny83 !! That did the trick! Passing a new "time" variable into the shader ins$$anonymous$$d of the "speed". If you could write this out as an answer I will mark you as correct. Thank you again for your help!