One very cheap way I achieve grass animation is by making some assumption about the alignment of the texture.
This may not be the same in your case however you can adjust values appropriately.
Assuming that the grass texture is created in such a way that the base of the grass texture is along the bottom edge of the texture. Using this assumption, we can take the vertex information we have for that vertex’s Texture coordinate and use that as a ratio between transformed animated offset and original position.
In this case, if we assume the base UV coordinate is v=1.0, we can first generate a random offset vector to modify our models position (This can either be done in model space of world space, it doesn’t necessarily matter.)
example:
if we have these 3 uniform variables:
// Input variables
uniform float time; // A constantly increasing/changing time value which we can use to create difference over time.
uniform float random; // a random value in case you want to change the "start point" of the animation for different objects.
uniform Intensity; // wind strength essentially
We can then generate an offset value using some simple trigonometrical functions:
vec3 offset = vec3( Intensity * (cos( time/1.5 ) * (1.0 - TextureCoord.y)) * sin( in_Position.x + random ),
0.25 * sin( 1.5*in_Position.x + 2.0*in_Position.z )* (1.0 - in_TextureCoord.y),
Intensity * (cos( time/2.0 ) * (1.0 - TextureCoord.y)) * sin( in_Position.z + 2.0*random),
);
Modifying the up-component of the data is optional, by default this can be 0, however having some basic random function can be used to improve the visuals of the grass by adding modifications to the height. This will need to be adjusted to the scale of your game if you do use it.
Finally, you simply add the offset onto the position:
vec4 object_space_pos = vec4( in_Position.x + offset.x, in_Position.y + offset.y, in_Position.z + offset.z + offset.x/5.0 + offset.y/2.0, 1.0);
( I also added the offset x and y to the z to create a little more variation in animation. )
The important part of code here is the fact that the offset is multiplied by 1.0-TextureCoord.y. What this does is it makes the offset fully effective at the point where the v coordinate is 0, (i.e at the top of grass), but makes it ineffective at the base of grass where TextureCoord.y = 1.0. This is a really nice way of creating quick and easy grass animation off simple data.
Trig functions are used to make a somewhat natural looking oscillation. The current model space position is used as an offset (realistically, you would probably want to use a world-space offset here then add this onto the world space coordinate after it has been calculated.) As the time value increases inside your program, the grass will oscillate.
In its current form, this will create a relatively simple form of animation. You can make the animation more complex by introducing additional trig functions, and perhaps rotating the offset such to apply it in the direction of the wind, however this is a little trickier. This works very nicely for basic grass animation in light breezes.