MONOGAME Grass:

This version of the original project(XNA version)is a bit more complex since I've added a bit of spring or squish-effect to the grass for when a character jumps into it.
Also this version includes a vertex shader and some unique drawing methods to make the grass sway naturally in the breeze.
By the way, I should mention that if you're using this in an XNA project, you may need to change the shader version in each shader. So where it says TECHNIQUE, you may want to replace:
technique name { pass { VertexShader = compile vs_4_0_level_9_1 vsname (); PixelShader = compile ps_4_0_level_9_1 psname(); } }
with { pass { VertexShader = compile vs_2_0 GrassVertexShader(); PixelShader = compile ps_2_0 GrassPixelShader(); } }


moving_grass
Full MonoGame Project Source (with vertex shader animation)

Note that in XNA we would set the texture for drawing with this:
device.Textures[0] = tex;
In Monogame, to ensure the texture is set correctly, we need to do this:
Shader.Parameters["Tex"].SetValue(tex);

"Tex" (if shader has something like this in it):
Texture2D Tex : register(t0);
sampler TexSampler : register(s0)
{
Texture = <Tex>;
AddressU = clamp; AddressV = clamp;
};

So the following vertex shader is used in conjunction with some draw methods to create waving grass. The sin wave (0 to 1) is updated with fTimer which is just some changing floating point number (could work on a timer) but to alter where in the wave function it is, the uv coordinate is used (multiplied by 4 for variety purposes) .. so that way different horizontal positions in the texture (different u coordinate) will have a different amount of waviness... and the final number is amplified (or made smaller) by multiplying it by how green the vertex was painted (not the pixel color - just the vertex color provided for the VS) ... I multiply this by 6 to boost how much it effects the final sway. So for parts of grass (bottoms) which I don't want much swaying, I set the green part of it's vertex data to 0 but for the tops I set it to 1.0f[or 255 in bytes] for maximum sway.

Code Snippet
  1. #define TECHNIQUE(name, vsname, psname ) technique name { pass { VertexShader = compile vs_4_0_level_9_1 vsname (); PixelShader = compile ps_4_0_level_9_1 psname(); } }
  2. #define BEGIN_CONSTANTS cbuffer Parameters : register(b0) {
  3. #define END_CONSTANTS };  
  4.  
  5. Texture2D Tex : register(t0);
  6. sampler TexSampler : register(s0)
  7. {
  8.     Texture = <Tex>;
  9.     AddressU = clamp;
  10.     AddressV = clamp;
  11. };
  12.  
  13. BEGIN_CONSTANTS
  14. float4x4 MatrixTransform;
  15. float fTimer;
  16. END_CONSTANTS
  17.  
  18. struct VSOutput
  19. {
  20.     float4 position        : SV_Position;
  21.     float4 color        : COLOR0;
  22.     float2 texCoord        : TEXCOORD0;
  23. };  
  24. VSOutput GrassVertexShader(float4 position    : SV_Position, float4 color : COLOR0, float2 texCoord : TEXCOORD0)
  25. {
  26.     VSOutput output;
  27.     float offset = sin(fTimer+texCoord.x*4) * (color.g*6);
  28.     position.x += offset;
  29.     output.position = mul(position, MatrixTransform);
  30.     output.color = color;
  31.     output.texCoord = texCoord;
  32.     return output;
  33. }
  34. float4 GrassPixelShader(VSOutput input) : SV_Target0
  35. {          
  36.     return saturate(tex2D(TexSampler, input.texCoord) *input.color);    
  37. }
  38. TECHNIQUE( SpriteBatch, GrassVertexShader, GrassPixelShader );

Note: if you are using a cross-platform DesktopGL project then use vs_3_0 and ps_3_0 instead... the naming of these is a bit weird to me... my understanding is that 4_0_level_9_1 is directX 9 compatible and you should use 5_0 for most shaders (DX11+) but if you are doing a cross-platform project, 3_0 is telling it that you're using openGL (ie: translates automatically to GLSL first)... if I understand correctly.

[Btw - many parts of Quadbatch aren't used in this project - but it can be used like SpriteBatch... It's something I use for rendering quads like with SpriteBatch and I can customize it on the fly... it's a work in progress... it assumes most of your art is on a single sheet to avoid texture swapping delays on the GPU but to switch texture sheets, you can start another Begin(...) End() section and set the texture using Begin(texture,...)]
Here is the added drawing method in Quadbatch which draws a rectangle in 4 sections ( [][][][] )
and you will notice that I'm setting the green value on the bottoms as a different color then for the tops to ensure the tops will sway more than the bottoms:

Code Snippet
  1. //----------------
  2. /// GRASS (waving)  D R A W  4 H
  3. //----------------
  4. // basically draws a single horizontal shaped rectangle in 4 sections (like 4 books beside each other)
  5. // this is useful for vertex-shader effects in which we may want to later animate the vertices in unique ways
  6. public void Draw4H(Rectangle sourceRect, Vector2 pos, Vector2 origin, Vector2 scale, float rot, Color c1, Color c2, SpriteEffects flip = SpriteEffects.None)
  7. {
  8.     float w, h, z=newQuad.z; // generate a TARGET WIDTH, HEIGHT based on the source (and scale it)
  9.     float o_x, o_y;  // the origin for rotations also needs to be scaled            
  10.     float x1, y1, x2, y2, x3, y3, x4, y4, u1, v1, u2, v2;
  11.  
  12.     if ((scale.X != 1) || (scale.Y != 1))
  13.     {
  14.         w = sourceRect.Width * scale.X; h = sourceRect.Height * scale.Y;
  15.         o_x = origin.X * scale.X; o_y = origin.Y * scale.Y;
  16.     }
  17.     else
  18.     { // no scaling:
  19.         w = sourceRect.Width; h = sourceRect.Height;
  20.         o_x = origin.X; o_y = origin.Y;
  21.     }
  22.     //need positions of destination                
  23.     x1 = pos.X; y1 = pos.Y;         //upper-left
  24.     x2 = pos.X + w; y2 = pos.Y;     //upper-right
  25.     x3 = pos.X + w; y3 = pos.Y + h; //lower-right
  26.     x4 = pos.X; y4 = pos.Y + h;     //lower-left            
  27.     if (rot != 0f)
  28.     {
  29.         float ox = pos.X + o_x, oy = pos.Y + o_y;
  30.         float cos = (float)Math.Cos(rot), sin = (float)Math.Sin(rot); //this is actually quite fast on a modern computer
  31.         float hd = x1 - ox, vd = y1 - oy;
  32.         x1 = ox + hd * cos - vd * sin; y1 = oy + hd * sin + vd * cos;
  33.         hd = x2 - ox; vd = y2 - oy;
  34.         x2 = ox + hd * cos - vd * sin; y2 = oy + hd * sin + vd * cos;
  35.         hd = x3 - ox; vd = y3 - oy;
  36.         x3 = ox + hd * cos - vd * sin; y3 = oy + hd * sin + vd * cos;
  37.         hd = x4 - ox; vd = y4 - oy;
  38.         x4 = ox + hd * cos - vd * sin; y4 = oy + hd * sin + vd * cos;
  39.     }
  40.     Rectangle tempRect = sourceRect;
  41.     u1 = tempRect.X / (float)tex.Width; //gets the texture coords in terms of (0.0f-1.0f, 0.0f-1.0f)
  42.     v1 = tempRect.Y / (float)tex.Height;
  43.     u2 = (tempRect.X + tempRect.Width) / (float)tex.Width; v2 = (tempRect.Y + tempRect.Height) / (float)tex.Height;
  44.     if ((flip & SpriteEffects.FlipVertically) != 0)
  45.     {
  46.         var temp = v2; //BR_Y
  47.         v2 = v1; v1 = temp;
  48.     }
  49.     if ((flip & SpriteEffects.FlipHorizontally) != 0)
  50.     {
  51.         var temp = u2; //BR_X
  52.         u2 = u1; u1 = temp;
  53.     }
  54.     float xrate1 = (x2 - x1) / 4.0f, yrate1 = (y2 - y1) / 4.0f, xrate2 = (x3 - x4) / 4.0f, yrate2 = (y3 - y4) / 4.0f;
  55.     float urate = (u2 - u1) / 4.0f;//, vrate = (v2 - v1) / 4.0f;
  56.     float uu2 = u1 + urate;//, vv2 = v1 + vrate;
  57.     vertices[vertex_count].Position = new Vector3(x1, y1, z); vertices[vertex_count].TextureCoordinate = new Vector2(u1, v1);   //upper-left texCoord
  58.     vertices[vertex_count].Color = c1; vertex_count++;
  59.     vertices[vertex_count].Position = new Vector3(x1 + xrate1, y1 + yrate1, z); vertices[vertex_count].TextureCoordinate = new Vector2(uu2, v1);   //upper-right texCoord
  60.     vertices[vertex_count].Color = c1; vertex_count++;
  61.     vertices[vertex_count].Position = new Vector3(x4 + xrate2, y4 + yrate2, z); vertices[vertex_count].TextureCoordinate = new Vector2(uu2, v2);   //lower-right texCoord
  62.     vertices[vertex_count].Color = c2; vertex_count++;
  63.     vertices[vertex_count].Position = new Vector3(x4, y4, z); vertices[vertex_count].TextureCoordinate = new Vector2(u1, v2);   //lower-left texCoord
  64.     vertices[vertex_count].Color = c2; vertex_count++;
  65.     x1 += xrate1; y1 += yrate1; x4 += xrate2; y4 += yrate2; u1 += urate; uu2 += urate;
  66.     vertices[vertex_count].Position = new Vector3(x1, y1, z); vertices[vertex_count].TextureCoordinate = new Vector2(u1, v1);   //upper-left texCoord
  67.     vertices[vertex_count].Color = c1; vertex_count++;
  68.     vertices[vertex_count].Position = new Vector3(x1 + xrate1, y1 + yrate1, z); vertices[vertex_count].TextureCoordinate = new Vector2(uu2, v1);   //upper-right texCoord
  69.     vertices[vertex_count].Color = c1; vertex_count++;
  70.     vertices[vertex_count].Position = new Vector3(x4 + xrate2, y4 + yrate2, z); vertices[vertex_count].TextureCoordinate = new Vector2(uu2, v2);   //lower-right texCoord
  71.     vertices[vertex_count].Color = c2; vertex_count++;
  72.     vertices[vertex_count].Position = new Vector3(x4, y4, z); vertices[vertex_count].TextureCoordinate = new Vector2(u1, v2);   //lower-left texCoord
  73.     vertices[vertex_count].Color = c2; vertex_count++;
  74.     x1 += xrate1; y1 += yrate1; x4 += xrate2; y4 += yrate2; u1 += urate; uu2 += urate;
  75.     vertices[vertex_count].Position = new Vector3(x1, y1, z); vertices[vertex_count].TextureCoordinate = new Vector2(u1, v1);   //upper-left texCoord
  76.     vertices[vertex_count].Color = c1; vertex_count++;
  77.     vertices[vertex_count].Position = new Vector3(x1 + xrate1, y1 + yrate1, z); vertices[vertex_count].TextureCoordinate = new Vector2(uu2, v1);   //upper-right texCoord
  78.     vertices[vertex_count].Color = c1; vertex_count++;
  79.     vertices[vertex_count].Position = new Vector3(x4 + xrate2, y4 + yrate2, z); vertices[vertex_count].TextureCoordinate = new Vector2(uu2, v2);   //lower-right texCoord
  80.     vertices[vertex_count].Color = c2; vertex_count++;
  81.     vertices[vertex_count].Position = new Vector3(x4, y4, z); vertices[vertex_count].TextureCoordinate = new Vector2(u1, v2);   //lower-left texCoord
  82.     vertices[vertex_count].Color = c2; vertex_count++;
  83.     x1 += xrate1; y1 += yrate1; x4 += xrate2; y4 += yrate2; u1 += urate; uu2 += urate;
  84.     vertices[vertex_count].Position = new Vector3(x1, y1, z); vertices[vertex_count].TextureCoordinate = new Vector2(u1, v1);   //upper-left texCoord
  85.     vertices[vertex_count].Color = c1; vertex_count++;
  86.     vertices[vertex_count].Position = new Vector3(x1 + xrate1, y1 + yrate1, z); vertices[vertex_count].TextureCoordinate = new Vector2(uu2, v1);   //upper-right texCoord
  87.     vertices[vertex_count].Color = c1; vertex_count++;
  88.     vertices[vertex_count].Position = new Vector3(x4 + xrate2, y4 + yrate2, z); vertices[vertex_count].TextureCoordinate = new Vector2(uu2, v2);   //lower-right texCoord
  89.     vertices[vertex_count].Color = c2; vertex_count++;
  90.     vertices[vertex_count].Position = new Vector3(x4, y4, z); vertices[vertex_count].TextureCoordinate = new Vector2(u1, v2);   //lower-left texCoord
  91.     vertices[vertex_count].Color = c2; vertex_count++;
  92.     if ((vertex_count + 1) >= 8192) {
  93.         EndVerts_CUSTOMIZED();
  94.         beginCalled = true;
  95.         vertex_count = 0;
  96.     }
  97. }//Draw4H

You'll also notice that unlike my other draw methods in quadbatch, I call EndVerts_CUSTOMIZED instead of End() because I wish to skip dealing with the regular vertex shader since I'm using a completely different vertex shader and pixel shader... So for example End_CUSTOMIZED looks like below.... and note the order in which you do things is important for this part or your code may not work:

Code Snippet
  1. //E N D  C U S T O M I Z E D-----------------------------------------        
  2. public void End_CUSTOMIZED() ///(Customized vertex and pixel shader only - doesn't deal with regular shader at all)
  3. {
  4.     if (!beginCalled) { Console.WriteLine("call to END without begin called. Aborting End."); return; }
  5.     if (vertex_count >= 3)
  6.     { //nothing to draw
  7.         Setup();                
  8.         int triangle_count = vertex_count / 2; //int triangle_count = sprite_count * 2;                
  9.         vertexBuffer.SetData(vertices, 0, vertex_count);
  10.         device.SetVertexBuffer(vertexBuffer);
  11.         WorldViewProj = ViewProj = wrld * view * proj;
  12.         fx.Parameters["MatrixTransform"].SetValue(WorldViewProj);                
  13.         fx.CurrentTechnique.Passes[0].Apply();                 
  14.         fx.Parameters["Tex"].SetValue(tex);
  15.         device.DrawUserIndexedPrimitives<VertexPositionColorTexture>(PrimitiveType.TriangleList, vertices, 0, vertex_count, indices, 0, triangle_count);
  16.     }
  17.     if (texvert_count > 0) EndText();
  18.     beginCalled = false;
  19. }


Again... the order the above instructions are done in is important. Btw - for most effects, most parameters can be set prior to applying or using any effects if they are not changed often (when initializing for example).

The XNA tutorial page for this explains how the other dynamics of grass are controlled.

(Full Monogame Source Code is here)

UPDATE:
SPRITE-BATCH-ONLY VERSION IS HERE:

(Full SpriteBatch Version Source Code)

Home Page