Grass generation

88dc730f0f71e55be39de0ad103bd9ff
0
Alienizer 109 Jul 15, 2012 at 20:10

Is there a better way to generate grass on a terrain without generating a zillion grass blade? I want to do this for static scenes, not real time.

19 Replies

Please log in or register to post a reply.

6837d514b487de395be51432d9cdd078
0
TheNut 179 Jul 15, 2012 at 20:59

What’s wrong with a zillion grass blades? It’s how nature renders grass :D

Or you can render grass using billboards. Check out the article on GPU Gems. There’s quite a few grass textures out there you can use that look really nice. Mix it up with some flora to liven it up. For distant lands, I would suggest rendering impostors. You don’t have to because many (all?) professional games don’t, but I think for such a small feature it could add a lot in return.

88dc730f0f71e55be39de0ad103bd9ff
0
Alienizer 109 Jul 15, 2012 at 21:22

Nature can render at the speed of light :lol: my render is 1/C² :blink:

I’ve been using billboards, but I thought there should be a better way, especially for non-flat terrain.

B20d81438814b6ba7da7ff8eb502d039
0
Vilem_Otte 117 Jul 16, 2012 at 00:25

You can also make some good-looking grass by geometry shaders (e.g. generating those zillions of grass run-time from just points “where grass grows”). I’m using a bit different technique and I’ll post tomorrow if I’ll get some time… for now I’ll link to this one:

screen\_1337956534.png

This one is from Outerra game, some details are given here http://outerra.blogspot.cz/2012/05/procedural-grass-rendering.html and all credits go to Brano Kemen

88dc730f0f71e55be39de0ad103bd9ff
0
Alienizer 109 Jul 16, 2012 at 02:37

Very impressive! But can this technique be implemented on a CPU based render?

B5262118b588a5a420230bfbef4a2cdf
0
Stainless 151 Jul 16, 2012 at 08:34

You could implement the same technique in software, but it would be as slow as a dog.

The only way I can think of to do realtime large scale grasslands would be some kind of voxel approach with pre-generated 2d grass tiles

88dc730f0f71e55be39de0ad103bd9ff
0
Alienizer 109 Jul 16, 2012 at 16:32

I wasn’t planning for real-time, but for a simple CPU-render.

Is there a way to do this by generating a double plane? What I mean is, duplicate the ground plane and move it up by the length of the grass. Then when a ray hit the top plane, an algo is used to determine what part of the grass is hit. Kind of like refraction in glass, or subsurface scattering. This way, we can have true elevation without creating zillions of poly. I guess the algo must decide (when hitting the top plane) if the ray hit the tip of a grass blade, or the ray continue a little bit more down until it hit a blade, or the ground plane. There is no poly for the grass blades, so the algo would decide where they are. Am I making any sense?

B20d81438814b6ba7da7ff8eb502d039
0
Vilem_Otte 117 Jul 16, 2012 at 22:32

So I finally get to reply (had 2 ranked matches at LoL :D - so it took most part of my evening - and most relevant information - won them :ph34r:). I also recommend quickly going through Kevin Boulangers work on grass rendering… http://www.kevinboulanger.net/grass.html

So actual idea is in dividing whole terrain into grid for grass coverage - this whole technique is just very good lod - near the camera you use actual grass blade meshes (for whole grid cell there is one mesh - transformed to fit the terrain), for further grass there are alpha tested “slices” (e.g. just textured quads) - horizontal and vertical planes - so it looks like actual mesh. The whole idea is in good transition of these LODs - so you get really nice looking grass.

What is so awesome on this technique is, that the pre-computed grass blade meshes for cell doesn’t need to be just uniform grass - bam it looks way better with dozen of different “grassy” plants in it. :)

I actually implemented in on my own before reading the Kevin Boulanger’s work (so imho it differs in some aspects), and I fine tuned it after I read it (if he reads this - then thanks Kevin, your tips in the thesis were very helpful).

So how it looks: (contrast for mesh has been increased so you see the border between mesh and slices)
2qcht3m.jpg

And how it looks without border contrast increase:
rldk5v.jpg
Note: See the areas in shadow of the tree - slices can be visible in distance - you can avoid this by smooth transition between mesh and slices - though it’s a bit slower than hard transition

Also don’t worry about performance of this technique - it was running suprisingly fast on GeForce 8600GT and extremely fast on Radeon HD 2900 - in time when I started using this technique. On current HD6870 (I’m using that now) it runs amazingly fast, and it is also running good on HD5470 in my laptop.

So it might aswell run pretty good in software (especially if sse is used).

If you have any question on how-to or something isn’t clear - feel free to ask, I’ll discuss details of this technique :) + I can write some how-to + post shaders I’ve used, and/or meshes + textures I’ve used (though they’re just grass blades for now).

88dc730f0f71e55be39de0ad103bd9ff
0
Alienizer 109 Jul 17, 2012 at 18:00

What did you win Vitem?

I think this is the way the game Oblivion does it, isn’t it?

But what about non-real time, on the CPU? Which is best way to go about rendering grass?

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 Jul 17, 2012 at 18:37

Well, you could start with this same type of modeling and just throw offline-quality lighting, AA, etc. at it. That’d be a good start. :)

B20d81438814b6ba7da7ff8eb502d039
0
Vilem_Otte 117 Jul 17, 2012 at 20:05

I won just ranked Leagues of Legends - http://eune.leagueoflegends.com/ - ranked game :D. You know, one does need to “turn off” from programming by doing something else than programming.

Oblivion actually don’t do it this way - it just renders the grass mesh where game creators specified that there is grass mesh (note that you can use brushes to draw them effeciently), whereas what I posted is a lot better in my opinion.

Basically for non-real-time graphics you would apply the same - although you will use full physically based BRDF/BSSRDF for lighting (actually you can do high-quality lighting in realtime, even with shadows - see Kevin Boulanger’s work about that). So basically what Reedbeta wrote is the way to go.

88dc730f0f71e55be39de0ad103bd9ff
0
Alienizer 109 Jul 18, 2012 at 00:59

VilemI thought you won a gold medal or something :D

88dc730f0f71e55be39de0ad103bd9ff
0
Alienizer 109 Jul 18, 2012 at 01:04

@Reedbeta

Well, you could start with this same type of modeling and just throw offline-quality lighting, AA, etc. at it. That’d be a good start. :)

ok, but I’m not sure how to do that. I’m not that good (just yet :lol: )

88dc730f0f71e55be39de0ad103bd9ff
0
Alienizer 109 Jul 20, 2012 at 23:44

ok, I’ve tried but it’s so slow I get 0.01 FPS it’s crazy!! It should be at least 60 right? And it doesn’t look anything like yours. Mine looks bad. I have a hard time to get the PNG transparency to work in GL, and then it doesn’t get sorted properly or something, it look like crap. I give up. I need to go to OpenGL school. :ph34r:

B20d81438814b6ba7da7ff8eb502d039
0
Vilem_Otte 117 Jul 21, 2012 at 02:31

Actually it depends on machine and whether the algorithm is optimized (and correct). I can write a little article how-to here - because I have all sources (models, textures, shaders) on my HDD, and I can share them along with my algorithm implementation.

And also I can second that no OpenGL school is needed (I don’t actually have any - though I had some OpenGL basics in school, though they all were deprecated stuff which is actually unusable… all you can do is self-learning, asking on forums, etc. (Note. I can’t judge all the schools, but I think most would do it this way).

88dc730f0f71e55be39de0ad103bd9ff
0
Alienizer 109 Jul 21, 2012 at 03:33

I would really appreciate it Vilem. I’ve been pulling my hair for a long time trying to make something, and it seems so complicated, but yet, people like you just do it like it’s easy! I feel kind of stupid. :wacko:

B20d81438814b6ba7da7ff8eb502d039
0
Vilem_Otte 117 Jul 21, 2012 at 14:18

So I’ll start by giving basic how-to…

The target of this post is to show how-to implement basic tiled grass, I won’t show how to optimize it further (even though I already had it implemented in program from which I posted screenshot), I’ll give tips though; Also note I won’t give lighting tips here (like implementing shadows, self-shadowing, global illumination, etc.) - why? Simply because this post will be huge as it is. Note that I can make a post or two about how to implement realistic lighting on grass, etc.

Okay the goal is to implement cool LODded grass like in my previous post on height map, it won’t be as optimized or as nice (because that would just need another huge article-post) but it will work. So I assume we got mesh loading, texture loading, shader loading and camera classes (e.g. the base of “game engine” ready to work) - I’m implementing this in my own game engine, though I think it’s easily adaptable to any other (like Unity, C4, etc. … though I assume you have your own one)…

So basically we need our CGrass object that is used to store/render single grass cell (my meshes have 5x5 units):

1.) CGrass class

/* Grass LODding variable */
float mGrassLod[2] = {25.0f, 100.0f};

/* CGrass class */
/* Contains all things needed for grass rendering */
class CGrass
{
public:
float mPos[2];  //< Position (float[2])

/* Constructor */
CGrass(float mPositionX, float mPositionY)
{
  // Just store the passed position
  mPos[0] = mPositionX;
  mPos[1] = mPositionY;
}

/* Render method */
void Render(CCamera* mCamera, CShader* mShader)
{
  // Compute grass cell distance to camera
  float mDist[2];
  mDist[0] = mCamera->mPosition.x - mPos[0];
  if(mDist[0] < 0.0f) mDist[0] = -mDist[0];
  mDist[1] = mCamera->mPosition.z - mPos[1];
  if(mDist[1] < 0.0f) mDist[1] = -mDist[1];

  // Pass float2 containing positions to shader
  mShader->SetFloat2(mShader->GetVariable("mPosition"), mPos[0], mPos[1]);

  // Now based upon distance in X and Z axes (we really dont want to do SQRTs ;-) )
  // decide which LOD to use
  if(mDist[0] < mGrassLod[0] && mDist[1] < mGrassLod[0])
  {
   // Render grass high-detail mesh (it actually don't have to be exactly grass blades, but in my case they are)
   mGrass->Render_GL2();
  }
  else if(mDist[0] < mGrassLod[1] && mDist[1] < mGrassLod[1])
  {
   // If we're further, draw grass slices (I got them divided in 3 separate
   // DON'T do this in release - put them into single one if possible) -
   // actually only reason I got 3 separate here is that in older applications
   // I blended between LODs a little more so it could look more pretty
   mGrassV1->Render_GL2();
   mGrassV2->Render_GL2();
   mGrassV3->Render_GL2();
  }
}
};

Now this is actually very unoptimized - simply because we’re actually just doing LOD + at some distance cutting it. Even though this will run enough fast even on my Core i3 + Radeon HD 5470 notebook (actually I’ve just been developing this on it). Optimizing it further by separating high LOD (mesh of blades) and low LOD (slices) each with separate shader and drawing them with instancing also gives quite a huge boost, I’ll comment on optimizations further down.

2.) Rendering grass cells

So now we can render a field of N x N cells of grass (some 100 x 100 is quite huge for 500x500 terrain in my case - which is actually quite huge). I assume that the terrain rendering isn’t the problem, so let’s jump ahead to grass rendering…

And so here is the grass rendering code:

/* Grass rendering in rendering part of game loop */
// Enable Sample alpha to coverage (antialiased transparency! :P)
glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
// Bind height map to texel unit 1
mHeightMap->Bind(1);
// Turn on grass shader
mGrassShader->TurnOn();
// Bind Projection and Modelview matrices
mGrassShader->SetMat4(mGrassShader->GetVariable("mProjection"), 1, false, (float*)(mProjection.m));
mGrassShader->SetMat4(mGrassShader->GetVariable("mView"), 1, false, (float*)(mView.m));
// Diffuse maps will be in texel unit 0 (texture is binded when Render_GL2 method is called on mesh object)
mGrassShader->SetInt(mGrassShader->GetVariable("mDiffuseMap"), 0);
// Height map in texel unit 1
mGrassShader->SetInt(mGrassShader->GetVariable("mHeightMap"), 1);
// Pass grass LOD variables (for smoothing transition at the end of grass a bit)
mGrassShader->SetFloat2(mGrassShader->GetVariable("mGrassLod"), mGrassLod[0], mGrassLod[1]);
// And pass camera position
mGrassShader->SetFloat4(mGrassShader->GetVariable("mCameraPos"), mCamera->mPosition.x, mCamera->mPosition.y, mCamera->mPosition.z, mCamera->mPosition.w);
// Render all the cells (now this almost calls for using instancing!)
for(int i = 0; i < GRASS_CELLS_COUNT; i++)
{
  mCell[i].Render(mCamera, mGrassShader);       // Lets say that mCell is CGrass array holding whole grid of grass cells
}
// Turn off shader
mGrassShader->TurnOff();
// And disable Sample alpha to coverage
glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);

So basically all work in C++ language is done (ehm… don’t forget to free used memory in the end, if you use C++ or like language). Now comes the second important part - the GLSL shader for grass…

3.) GLSL shader for grass rendering

[vertex shader]
#version 410 core      //< GLSL 4.10 Core profile
/* From application */
// ATTRIBUTES //
in vec4 Gpu_Vertex;   //< Vertices from application
in vec2 Gpu_MultiTexCoord[2]; //< Texcoords from application
in vec3 Gpu_Normal;   //< Normals from application
// UNIFORMS //
uniform mat4 mProjection;   //< Projection matrix
uniform mat4 mView;   //< View matrix
uniform vec2 mPosition; //< Current grass cell position
uniform vec2 mGrassLod; //< Grass LOD variables from application (could be also 2x const float or const vec2)
uniform vec4 mCameraPos;   //< Camera position from application
uniform sampler2D mHeightMap; //< Height map sampler
/* To Fragment shader (as there is no tessellation shader, neither geometry shader) */
out vec3 Vert_Normal;    //< Vertex normals
out vec2 Vert_TexCoord; //< Vertex texture coordinates
/* Constants (Could be uniforms for general height field) */
const float mHeightMapSize = 500.0; //< Height map scale (XZ dimension)
const float mHeightScale = 75.0;  //< Height map scale (Y dimension)
/* Main! */
void main()
{
  // Just store vertex normals and texcoords - note they have flipped y coord ;)
  Vert_Normal = Gpu_Normal.xyz;
  Vert_TexCoord = vec2(Gpu_MultiTexCoord[0].x, 1.0 - Gpu_MultiTexCoord[0].y);

  // Distance from current vertex to camera
  float mDist = length(vec3(Gpu_Vertex.x + mPosition.x, 0.0, Gpu_Vertex.z + mPosition.y) - vec3(mCameraPos.x, 0.0,  mCameraPos.z));

  // Height multiplier (makes transition from grass to terrain a little more smooth)
  // It actually computes white circle fading at its borders - ideal to multiply with grass height
  // So that transition is a lot smoother
  float mMult = 1.0 - pow(min(mDist / mGrassLod.w, 1.0), 10.0);

  // On which texcoord is current position in height map? - compute it
  // It's just simple transformation from world space to height map texture space
  // it could also be done with a single matrix (that would be more operations though)
  vec2 mSamplePos = vec2(Gpu_Vertex.x + mPosition.x, Gpu_Vertex.z + mPosition.y);
  mSamplePos -= mHeightMapSize;
  mSamplePos /= mHeightMapSize;
  mSamplePos += 0.5;

  // Heightmaps height at current vertex position
  float mHeight = textureLod(mHeightMap, mSamplePos, 0.0).x * mHeightScale;

  // Output position - notice we're multiplying to get smooth transition and adding height of current vertex and position of current cell
  gl_Position = mProjection * mView * vec4(Gpu_Vertex.xyz * vec3(1.0, mMult, 1.0) + vec3(mPosition.x, mHeight, mPosition.y), 1.0);
}
[pixel shader]
#version 410 core       //< GLSL 4.10 Core profile
/* From vertex shader */
in vec3 Vert_Normal;      //< Vertex normals
in vec2 Vert_TexCoord;   //< Vertex texture coordinates
/* From application */
uniform sampler2D mDiffuseMap; //< Diffuse map sampler
/* To frame buffer  */
out vec4 mFragColor;
void main()
{
  // Just sample diffuse texture and output it
  // NOTE: most of fancy stuff is to be done here - like illumination, and such
  vec4 mDiffuseTex = texture(mDiffuseMap, Vert_TexCoord).xyzw;
  mFragColor = vec4(mDiffuseTex.xyz, mDiffuseTex.w);
}

4.) Result

Altogether this can give you quite fast and simple grass. The result on simple height map can look like this:
aaxgde.jpg

This runs at some 50 - 60 fps on Radeon HD 5470 (while there is still so huge space to optimize). Here is another one with smooth transition to ground in action (lowered grass view distance):
119v4o9.jpg

In motion it looks like the ground is eating the grass :D (it’s just scaling to 0). So I also will give you all the data files i’ve used (height map, obj model of terrain (which is actually just height map applied to plane and saved - in case u want separate model), all grass meshes & textures i’ve used, note that my grass blades are color-tuned in pixel shader (you can also fine-tune the texture, which is better for performance reasons)) - here u go http://www.otte.cz/grass.tar.gz

Okay as far as I can tell this works pretty well, although as I mentioned I’ll discuss optimizations and fancyness a bit…

5.) The Optimization

5.1 Listing the cells
Even though this is fast enough, it can be well a LOT faster. Start with separating grass cells that are meshes and slices to two arrays, on camera move push new inside and remove old out - you actually have two lists - mesh cells list and slices cells list. Render them separately with pre-binding all the textures needed (saves texture unit changes, because now texture bind changes every time a cell is rendered).

5.2 Instancing
Second thing is to add instancing - this gives quite a huge speed boost and I would add it as one of the first features if i were you (the speedup isn’t quite obvious in the first moment, but when you start adding more stuff (and I mean lots of more stuff) the speed difference between using instanced grass cells and uninstanced starts to be really obvious) - in scene I posted before (with the cottage and the stuff, instancing version is more than 2.5 times faster than non-instancing version).

5.3 Frustum culling
Third thing is also speeding up everything - and thats frustum culling. Quite simple to implement (because this is actually a grid) - it gives a little boost (you actually render a lot less grass cells when camera is at ground level). If you really want to favor speed (as I do), make a quadtree on top of it - because this will decrease the number of frustum cull tests, by quite a huge factor.

5.4 Optimization summary
So basically whole optimization can be shortened to - use some kind of hierarchy and frustum culling, render all cells in high detail LOD in single call (through instancing), the same goes for low detail LOD … to achieve this you will have to keep lists of active grass cells. Note that you will need to rebuild the hierarchy often as you add/remove cells to lists - but it’s actually quite fast, because building quadtree on N x N grid is almost instant (also you can guarantee that quadtree is easily buildable by merging 4 closest cells with little fine-tuning - e.g. using 16x16, 32x32, 64x64, etc. total cell counts).

6.) The Fancy Stuff

E.g. the hard part. Everything goes up and down with textures - my textures for the grass are bad (they don’t have same colors for all LODs - although I fine-tune this in pixel shader). If you want to get this work good, create good textures.
Then we can divide the work to lights & shadows and movement.

6.1 Shadows & Lighting
Basically you divide the light to direct, indirect (from sky and other items) and transmitted (part of indirect). Direct lighting is simple (you can use normal maps to do it nice and easy, and still good looking) - using lamberts cosine law + phong for specular works quite well (note: don’t forget that you need to light both sides of either grass blades and grass slices - so take absolute value of dot product). Indirect lighting is quite tricky - I personally use realtime instant radiosity with secondary shadows for everything but sky + visibility test for sky indirect lighting, for slower computers ambient constant or good SSAO works awesome … for offline renderers you can actually use path tracing and the result will be very nice. Transmitted lighting is solved by either constant or texture - note that I ignore this effect on grass because it actually isn’t visible enough.
Shadows are quite tricky, because you can’t use shadow volumes, neither shadow maps. What is quite useable are plannar projected shadows (or some texture hack is also useable) and texture for self shadowing (see Kevin Boulangers work on this). I’ve dropped shadows out, because I actually don’t think that they’re visible enough (e.g. it’s not worth it - it actually doubles the polycount not giving good effect - it’s barely visible)

6.2 Animation
It’s actually try&fail (in my case) - but for sure take grass position into account and do some sin/cos functions to get nice waving grass (don’t forget using gradient texture as weight for grass! - so you wave just the tips of grass, not the whole blade/slice). You can also try using “wind maps” (I think I made a post here some time ago about them) with this to get waving in wanted direction.
And dont try moving grass somehow differently when character moves in it - it just looks weird (I mean like in Two Worlds 2 for example).

So well, I hope thats describing all the stuff you might need for basic grass system implementation. If you would have any problem - feel free to ask, I’ll try to help (even with optimizations & fancyness - because I’ve got mentioned things already implemented I can post a little how-to like this one).

Okay, time to continue on my crusade of making ProjectRPG!

EDIT: Some little changes in bold text, etc. - so it’s better looking

EDIT 2: One more last note - you need to have multisampling enabled and some 2 or 4 samples - because I use transparency multisampling (sample alpha to coverage). In case you don’t have, just use basic alpha testing instead of that - glEnable/glDisable GL_ALPHA_TEST and set glAlphaFunc to GL_GREATER and value to some 0.5f … it will be more aliased, but I assumed that transparency multisampling is available on most computers nowadays.

Cd653bafe28ea04f87535b0e30928d91
0
Leon 101 Nov 21, 2013 at 07:40

Oops,this link of data files seems broken, can u send me a copy of that, lxl_pc@126.com, thx

88dc730f0f71e55be39de0ad103bd9ff
0
Alienizer 109 Jul 21, 2012 at 17:20

oh WOW! Big tutorial, thank you :) . Let me read this carefully now…….

88dc730f0f71e55be39de0ad103bd9ff
0
Alienizer 109 Jul 22, 2012 at 21:39

ok, I have almost getting it to work. I had to change my ways of coding glsl here. TheNut has been helping me get on the right track. I keep on doing the v1.0 deprecated stuff, and yours uses #version 410 and all my code errors out with tons of messages!