Sign in to follow this  

a call for opinions - drawing grass

Recommended Posts

Posted (edited)

a call for opinions - drawing grass

 

i'm working on drawing better grass plants in caveman.

 

here's where i'm at right now:

 

gallery_197293_593_1278146.jpg

 

 

i cut it back from one plant every 2 feet to one every 3 feet.  coverage is decent, but there are some gaps compared to one every two feet.  one plant every two feet with a clip range of 1000 feet occasionally dropped below 60 fps.

 

the plants are about twice as tall as i'd like them to be.  making them wide and short improves coverage, but then the plants look "squashed".  so i can't make them any shorter without making them smaller, which doesn't cover the ground mesh as well.

 

right now i've been doing this with one plant per draw call, so i can rotate them around x and z to make them sway in the wind.

 

since draw calls is the big bottleneck, one option is to make a model with 4 or 9 plants in it. more plants with fewer draw calls.  but then you can't tilt it in the wind without it looking funny as the plants in the "array of pants" move in and out of the ground.  

 

so i was thinking about doing a combo of the two. make half of them single plants that sway, and the rest multiple plants that don't.   or is what i have now good enough? the grass is technically too tall for photo-realism - even if i do manage to get a good model and texture and material  and lighting going.

 

another question  is: how far out do games typically draw highest LOD?  playing mad max recently, it seems its pretty short in that game, maybe 10-20 meters?  and other shooters too as i recall...  i've been testing mostly with high LOD out to 1000 feet (304.8 m).

 

i did some tests with a single wide short qaud (10 foot wide, 2 foot tall) with 6 plant textures on it.   looks great at long range.   a distinct possibility for impostor use.

 

so should i stick with what i have?

 

or thicken it up with some static plants and make all the plants a more realistic scale, but not all animate-able? 

 

or go for impostors at long range?  one consideration there is i let the player zoom the 3rd person view camera waaaaay out (50 -75 feet?). so impostors couldn't kick in until 100-200 feet.  should i cut the zoom out if needed?

 

i'd like to get as realistic as possible, in which case the grass in the screenshot may just be too big.  i'm thinking it should only come up to between mid-calf and knee height.   

 

and plants that aren't as wide look better. but don't cover as well, which means you need more plants.

 

is it crazy to try to draw high LOD grass out to 300 m ? should i set a more realistic goal like 10 m + impostors or fadeout?

Edited by Norman Barrows

Share this post


Link to post
Share on other sites

Personally, here are my thoughts on grass:

1) Really crazy: Grass should not be "cards". Instead, render each blade of grass as a polygon and use vertex colors instead of textures.

Why? Won't that create millions of triangles? Sort of, but graphics cards are really good at rendering lots of triangles. The scene gets more expensive when you use cards with masked transparency and you get layers of overdraw on the cards. If each blade of grass is rendered as a polygon, then you can make the grass sway nicely with vertex shaders using position offsets. Modern graphics cards are quite capable of rendering all the triangles you can throw at it. You can get away with this by swapping out shaders at greater distances to reduce GPU instructions, and you can also start to reduce the grass density as a function of distance.  

 

2) Grass is a *perfect* candidate for instancing. You can render all of your grass in about 3-5 draw calls (one draw call per batch of LOD's). Your GPU will love you if you can instance anything.

 

3) In principle, LOD's should be swapped out when the visual difference is unnoticeable. Distance based LOD's is somewhat the standard goto technique, but you can also do LOD swapping based on the object size in screen space. The screen space technique lets you have higher resolution objects for large objects in the distance (such as mountains) but quickly lets you switch LOD's for small objects in the near by zone (think of a thimble at 1 foot vs 1 yard). Generally, if you see noticeable popping between one LOD and the next, either you're switching too soon or you need more LOD's with less variation.

Share this post


Link to post
Share on other sites

one big design constraint i forgot to mention:

 

dx9, fixed function, no shaders, no true instancing.

 

i may be forced to go to a later version of DX or start coding shaders, but i'd REALLY rather not do that.

 

so i'm still stuck with all the constraints that entails: draw calls are expensive. no instancing. static meshes only.

 

using the test of "change LOD when un-noticeable" i'd never change lod based on my early experiments with impostors.  but they were't blended in/out.  so the change from 3d model to 2d billboard was always noticeable.  and impostors need to be rendered from multiple views to match the object if its not symmetric. i wasn't doing that either.  

 

instead i chose a different course of seeing how far out i could push high LOD. with the answer being about 1000 feet at 60 fps and 22.5K grass plants, or 300 feet at 30-40 fps and as many as 100,000+ grass plants (over 100,000 draw calls per frame - yes i know that's crazy). seems  i can almost but not quite get where i want to be with the number of plants drawn - all at high LOD. 

 

and to top it all off, i'm not just going for "good looking" like skyrim SE or mad max, i'm going for photo realistic. although how i'll manage that in dx9 remains to be seen, and may not be possible.  shadows are the big concern, even if i find solutions for grass and water.

 

so yeah, extra challenging problem this one.  unless i port to dx10.

Share this post


Link to post
Share on other sites

use vertex colors instead of textures

 

not so sure about that for photo realism.

 

check out this grass...   a nice example of poly based

 

https://www.youtube.com/watch?v=B4j5tzAIflU

 

vs this grass - procedural by nvidia - turf effects.

 

https://www.youtube.com/watch?v=kLVa0NOFdwM


Use instancing and shaders

 

which leads to my next question....

 

https://www.gamedev.net/topic/685102-porting-dx9-to-dx10-or-later-how-hard/

Share this post


Link to post
Share on other sites

Alternatively, you can manually process the verts in software. Make a big dynamic vertex buffer and MAP_DISCARD it every frame.

 

i've actually gotten great results with generating static (not dynamic) ground meshes in realtime (with caching).  mesh animation using one static mesh per frame is another possibility. but i was thinking that simply tilting the plants might be enough. if the base of the texture image is narrow, the bottom corners  won't pop up out of the ground when it tilts. or i could draw them down lower into the ground so they never appear above ground when tilted.  but that still wouldn't solve the problem of needing more instances if the grass is of proper scale (ie smaller).  i may have to go to dx 10, just for instancing. the problem is i don't know how big of a plunge that is: days, weeks or months to port a dx9 api wrapper to dx10.  but it looks like i've already gotten a few responses on my question of porting dx9 to dx10, so maybe i'll find out.

Share this post


Link to post
Share on other sites

Yeah, what I was describing with polygon grass is in that NVidia video with turf effects, not the preceding one. The first video you posted is still using cards to render a masked grass texture onto but they're using a shader to mask the grass from an opacity layer. This is what causes the expensive overdraw I was talking about: If the camera is looking through the grass at an angle where a line of sight could pass through 20 cards, the GPU has to visit each card and run the shader to see if the card blocks line of sight. In terms of performance, it's decent for traditional games, but it can slow down your framerate if you have too much. This is particularly a problem for VR games.

 

The NVidia turf video is insane. Did you see how each blade of grass is casting shadows onto other blades of grass in real time?!? And then they zoom out and show you a whole field of grass. Unbelievable. I want to see that run at 90FPS.

 

Anyways, D3D9 supports instancing. I've done it. It also supports HLSL shaders. So, whether you're doing XNA or DX9 with C++, there's no reason why you can't use instanced draw calls for grass and combine it with shaders to create good looking grass which doesn't rely on cards.

Share this post


Link to post
Share on other sites

Out of curiosity how many draw calls are you using on grass per frame right now?

 

i've tested it at one plant per foot, per 2 feet and per 3 feet in a 300x300 foot terrain chunk. drawing the player's chunk and all adjacent chunks (9 chunks total).  

 

that works out to 90,000, 22,500, or 10,000 draw calls per chunk just for savanna grass plants.

 

clip ranges are set to 1000 feet, as is the frustum far plane distance, during the pass that draws grass and other things that are "neither very near nor very far".  

 

after trivial rejection of chunks behind the camera and a frustum cull, in a scene with 2000-3000 other meshes, i've hit over 100,000 draw calls total.

 

needless to say, that was a bit of a performance hit.  <g>  

Share this post


Link to post
Share on other sites

 D3D9 supports "real" instancing:

 

i think that may be the "dx9 instancing" i was referring to in my other thread. the stuff with buffers looks familiar, and its shader based, which means i would not have tried it before when i first learned of it long ago.

 

but that means i can write some (dx9) shader code to do the instancing, right? and use that alongside the fixed function pipeline? IE my draw savanna grass routine calls a shader instead of DIP ? and everything else is still fixed function, and they work together?  or is it an one or the other situation, shader or fixed function code in one scene, but not both? 

 

can you mix (or switch between) dx9 fixed function and dx9 shaders in a single scene?

Share this post


Link to post
Share on other sites
Posted (edited)

IIRC DX9 instancing is implemented using a stream divisor frequency.  Nothing to do with shaders AFAIR.

 

edit - also that is alot of draw calls.... and you were getting 60fps?  Was that on your new machine with the 1080 or the old machine?

 

edit2- now that I think about it I really don't remember the interplay between the FF pipeline and instancing... so I might be wrong.

Edited by Infinisearch

Share this post


Link to post
Share on other sites

You can mix shaders with non-shader draw calls. They both go through DrawIndexedPrimitive(). It's the setup you do before the draw call that's different (i.e. you call SetVertexShader() and SetPixelShader() to activate shaders, which you can set back to NULL again to go back to non-shader mode).

Share this post


Link to post
Share on other sites
Posted (edited)
edit - also that is alot of draw calls.... and you were getting 60fps?  Was that on your new machine with the 1080 or the old machine?

 

60fps? oh, heavans no!  <g>

 

here's the breakdown in brief: 

terrain chunks are 300 feet across (~ 100m) 

i draw the player's chunk, and the 8 surrounding chunks.

chunks are skipped if off the edge of the world map, or are 100% behind the player, based on player yr. i found it interesting that N-S-E-W chunks only have a 90 degree arc where they are not visible, but diagonal chunks have a 180 degree arc. this is because the player can be anywhere in their chunk, looking in any direction.

a chunk is a list of renderables (Zdrawinfo structs with params for a draw call), with a database styte index that lists them in order by texture. the structs are in an un-ordered arrray. the index is an ordered array of ints (struct indices) sorted on texture.

chunks are drawn one at a time, with each chunk's renderables drawn in order by texture alone. to date this has been sufficiently fast that i don't even bother sorting on mesh or near to far.

for each texture in the chunk, it sets the texture, then draws all the meshes that use it.  it sets the mesh, cull, clamp, material, etc, and then calls DIP.

my wrapper API for dx9 includes state managers for things like texture, mesh, cull, clamp, alphatest, etc.

the far plane is set to 1000 feet for the pass that draws grass. 

trivial range rejection cliprange is also set to 1000 feet. the player can control clip range for about a dozen different things, including grass.

clipping consists of a trivial range rejection based on bbox distance to the camera, and frustum cull (b-spheres vs planes), for each mesh drawn.

 

these are the kind of numbers i'm getting on the i7-6700K + gtx1080

 

one plant every 3 feet, 10,000 plants per chunk, 90,000 plants total before clipping, 60 fps rock solid all day long. but up to a meter between adjacent plants when drawn to scale or even a bit oversized. don't recall the number visible offhand, maybe 3000.

 

one plant every 2 feet, 22,500 plants per chunk, 202,500 plants total before clipping, about 54-56 fps.  again, i don't recall the number visible offhand, perhaps 10k or so. if you really want to know the numbers , i can always kick them back up and tell you what they are. it only takes 15 seconds to make a code change on this new PC. just 20 seconds + edit time from quit game to running game again with recompiled changes. beats the hell out of 3-4 minutes on the old dual core 1.3Ghz PC..

 

one plant every foot, 90,000 plants per chunk, 810,000 pants total before clipping, about 15 fps.    ~98,000 plants visible. i hit 101K+ draw calls total, with about 3000 of them going towards everything except the grass.

 

as the number of plants increases, the max number of meshes per chunk must increase (from 15K to 95K), and the max number of chunks in the chunk cache must decrease (from 90 to 30) to keep from running out of ram.

 

each chunk uses 4 different textures for grass. total textures for a chunk is on the order of one to two dozen. 4 ground, 2 tree trunk, 2 tree leaf, a couple rocks, maybe a berry bush or fruit tree, or water, and the grass plants, that's about it.

Edited by Norman Barrows

Share this post


Link to post
Share on other sites
Posted (edited)
You can mix shaders with non-shader draw calls.

 

 cool. that's the one thing i wan't sure about at first. 

 

looks like this won't turn out to be too much of a big deal to implement.

 

OTOH, i still have yet to get my hands on any usable shader code examples. i assume the ones in the dx9 samples will get me started, and the shaders used by the skinned mesh code work just fine, so i can always just use them as a guide.

 

i plant to keep it as simple as possible: set stream, set shaders, set constants, DIP, set fvf when done. single purpose specialty shaders just for special effects like animated grass. no effects files. pre-compiled at dev time, and loaded once a program start.

 

thanks everyone for all for the help!

Edited by Norman Barrows

Share this post


Link to post
Share on other sites
Posted (edited)
right now i've been doing this with one plant per draw call, so i can rotate them around x and z to make them sway in the wind.

I just realized by plant do you mean a model or a billboard?

 

Also how are you spawning grass?  Are they placed manually or spawned with some sort of coverage map? 

 

edit - also if a model how many triangles?  And if billboard how many billboard per plant?

Edited by Infinisearch

Share this post


Link to post
Share on other sites
Nothing grass-related to add here but I would say to Norman that I put off moving to using Shaders in DX 9 for a long time myself, but when I finally bit the bullet, I never looked back.

It is actually pretty trivial to replicate the basic FFP with a shader. I now don't use the FFP at all, even if I could do what I want with it.

And the sudden rush of possibilities (even with DX 9 and it's limited shader types which I am also still using) is very exhilarating.

Do it, man!

Share this post


Link to post
Share on other sites
I just realized by plant do you mean a model or a billboard?  

 

3d models, anything from 3 quads up to 20 or so. most are twisted or subdivided and bent planes so no faces are coplanar. so "non-coplanar cards" yield pretty good results without resorting to true 3d models with a stem with volume, etc.

 

i tried billboards as impostors very early on (like 2-3 tears ago), but was unimpressed by the results. multiple billbards at differnt angles are required for asymmetric rock outcroppings.  once i went to chunk based rendering on texture order, there was no need for impostoring until now perhaps. as one of my recent tests i tried 6 plants on a 10 foot wide 1 foot tall 2d billboard, at random locations and rotations. looks great at long distance and not from above - definitely a LOD possibilty there, but can only really rotate around x for swaying in the wind. shaders would allow a bit more deformation of the upper verts.

 

 

 

Also how are you spawning grass?  Are they placed manually or spawned with some sort of coverage map? 

 

when a terrain chunk is generated, it generates the grass. it uses a pair of loops that start with a plant every 1, 2 or 3 feet, then uses that x,z location to lookup jitter, rotation, mesh, texture, and scale, based on a generic random pattern map. so for example, get_savanna_grass_mesh(x,z) mods x and z by 100 to map them into the range 0-99, then uses them as the indices into a 100x100 array of random ints with values from 0 thru 100 (the generic random pattern map). the result is then sent thru an if then else statement, if < 26, use mesh #1, else if < 51 use mesh #2, etc.  the same idea for rotation: yr =  result*0.0628 radians. jitter is (result-50) / 25.  IE +/- 2 feet. scale is also based on the random pattern map.  this means one generic random pattern map can be used to determine the jitter, rotation, mesh, texture, and scale of all savanna plants in the game. the same generic pattern map is used for drawing tall grass terrain as well.  things like trees, fruit trees, berry bushes, and rocks each have their own dedicated "plant maps" to determine location, scale, texture, mesh, etc.   plant maps was the original way of doing things (800x800 sparse matrix with 1300 plants). the generic random pattern map is the new way. i even use it as the basis for a heightmap function to generate canyon ground meshes.

 

 

 

also if a model how many triangles?  And if billboard how many billboard per plant?

 

i'vre tested it with models with just 3 twisted quads, and models with up to 20 non-coplanar quads. triangle count (numverts) is really not an issue. a bit more of a hit than tri size (rasterization costs), but still nothing compared to number of draw calls, or figuring out what to draw (iterating over massive numbers of possibly renderable objects).  with billboard tests its always just one quad per plant, or 6 plant images on one quad in my latest test. iteraing can even be more expensive than draw calls actually made, if the number of possibly renderable objects is very high. IE if i try to draw close to a million plants, and only end up drawing 90,000 its almost as slow / even slower than just trying to draw fewer plants but ending up drawing about the same number.

Edited by Norman Barrows

Share this post


Link to post
Share on other sites

And the sudden rush of possibilities (even with DX 9 and it's limited shader types which I am also still using) is very exhilarating. Do it, man!

 

LOL!

 

yeah, i know, i've been putting it off for too long.  but i'm trying to build games, not graphics engines.  and as you say, the power of shaders is intoxicating at the very least - especially for someone like me who's done stuff like hand tuned assembly code blitters and my own software renderer. you can do a lot of cool stuff when you can code down to the metal. <g>.  i may go effects crazy.  good thing caveman isn't a sci-fi game or i might never finish the final graphics! <g>.

Share this post


Link to post
Share on other sites

i'vre tested it with models with just 3 twisted quads, and models with up to 20 non-coplanar quads. triangle count (numverts) is really not an issue. a bit more of a hit than tri size (rasterization costs), but still nothing compared to number of draw calls, or figuring out what to draw (iterating over massive numbers of possibly renderable objects).  with billboard tests its always just one quad per plant, or 6 plant images on one quad in my latest test. iteraing can even be more expensive than draw calls actually made, if the number of possibly renderable objects is very high. IE if i try to draw close to a million plants, and only end up drawing 90,000 its almost as slow / even slower than just trying to draw fewer plants but ending up drawing about the same number.

Actually modern GPU's work on either 32(nvidia) or 64(amd) vertices at a time.  And AMD GPU's for some reason like a minimum call size of 256 vertices.  So if you are drawing with less than that per call or per instance you are underutilizing the GPU.

 

when a terrain chunk is generated, it generates the grass. it uses a pair of loops that start with a plant every 1, 2 or 3 feet, then uses that x,z location to lookup jitter, rotation, mesh, texture, and scale, based on a generic random pattern map. so for example, get_savanna_grass_mesh(x,z) mods x and z by 100 to map them into the range 0-99, then uses them as the indices into a 100x100 array of random ints with values from 0 thru 100 (the generic random pattern map). the result is then sent thru an if then else statement, if < 26, use mesh #1, else if < 51 use mesh #2, etc.  the same idea for rotation: yr =  result*0.0628 radians. jitter is (result-50) / 25.  IE +/- 2 feet. scale is also based on the random pattern map.  this means one generic random pattern map can be used to determine the jitter, rotation, mesh, texture, and scale of all savanna plants in the game. the same generic pattern map is used for drawing tall grass terrain as well.  things like trees, fruit trees, berry bushes, and rocks each have their own dedicated "plant maps" to determine location, scale, texture, mesh, etc.   plant maps was the original way of doing things (800x800 sparse matrix with 1300 plants). the generic random pattern map is the new way. i even use it as the basis for a heightmap function to generate canyon ground meshes.

As you said in your original post if you could merge some of the plants together you'd be better off for the aforementioned reason.

 

You already know you have to decrease your drawcall count... the only thing I'd look into is billboards for further away grass.

Share this post


Link to post
Share on other sites

warning!   <rant mode>  thin skinned types - stay away!

 

 So if you are drawing with less than that per call or per instance you are underutilizing the GPU.

 

 

and that matters because?...

 

what matters is the GPU can do as directed by the game, not whether  the  game makes 100% utilization of the gpu. the gpu is there to serve the game, not the other way around.

 

folks sometimes tend to forget this.

 

who gives a flying F if the stupid card can blast 10 billion static tris of the same texture per sec?      that's not what i need!      i need 1 million draw calls of 50 tris each with about a dozen different textures!  (and that's just for grass!)    i'm so sick of sh*t not really designed for what games do!   from enhanced instruction sets, to hardware cards, to language enhancements, to libraries and API's, to books "supposedly" on game development, to "design patterns".  its all a bunch of horse sh*t.

 

next time someone tries to sell you some sh^t for games, look them straight in the f*ing eye and ask them how "many how many games you made?"   and using this concept / product / whatever you espouse - how many games?   then why the f should i listen to you?

 

<end rant mode>

Edited by Norman Barrows

Share this post


Link to post
Share on other sites
As you said in your original post if you could merge some of the plants together you'd be better off for the aforementioned reason.

 

it would be awesome if i could put all the plants in a static buffer  like i do the ground quads, but they have to be able to sway individually.    using rotation, that means separate meshes for each plant. by just moving the upper verts with a vertex shader, i may be able to place them all in one VB based on texture. but i'd still be generating VB's on a per chunk basis, due to collisions with water, rocks,and other places where plants should not be drawn as well as varying height maps. its not like i could have a single savanna grass vb for use by all terrain chunks in the game  (all 1 billion, 936 million of them!).

 

it would be even more awesome if GPUs were better at drawing large numbers of dynamic instances with different textures (what games need), rather than large numbers of static triangles with the same texture (uh - i can do this... ain't what you need... but...hey! look at all those triangles!  and that framerate!  12 million almost degenerate flat shaded tris at 3000 fps!    impressive huh?    and i'm like: WTF? what use is that?  if it wasn't so pathetic and sad, you'd have to laugh.).

 

 

 

=========== unable to post separately ===================================

 

>>>>   you are underutilizing the GPU.

 

>> and that matters because?...

 

down vote all you want, its still a legitimate question.

Edited by Norman Barrows

Share this post


Link to post
Share on other sites

it would be even more awesome if GPUs were better at drawing large numbers of dynamic instances with different textures (what games need), rather than large numbers of static triangles with the same texture (uh - i can do this... ain't what you need... but...hey! look at all those triangles!  and that framerate!  12 million almost degenerate flat shaded tris at 3000 fps!    impressive huh?    and i'm like: WTF? what use is that?  if it wasn't so pathetic and sad, you'd have to laugh.).


They can.

They do.

The fact you are 15 years behind the tech curve and have in the past actively resisted any attempts to educate you or to bring your knowledge up to a level where you can use modern GPUs to do what they do best leaves you "rant" without substance or merit - the ravings of someone behind the times and out of their depth frankly.

If you want to pretend nothing has changed in hardware or software since 2001, then feel free... just don't bother moaning at us when you can't do something because you stuck in a past which no longer exists; frankly it's a waste of our time and I, for one, shan't be wasting any more of mine on you.

Share this post


Link to post
Share on other sites

=========== unable to post separately ===================================   >>>>   you are underutilizing the GPU.   >> and that matters because?...   down vote all you want, its still a legitimate question.

I didn't downvote you... and the advice comes from GDC presentations by game developers.

 

As far as your rant goes I'd say it matters in that if you want to get maximum performance you need to know what factors affect performance, and then see if you can work around limitations to achieve your goal.  In fact I even agreed with the sentiment "why can't the hardware/API just do what I need it to do?" at one point in time.  But once you look at hardware design choices and the evolution of the API and what drivers do you realize it had to play out the way it did.  Thankfully there are work arounds and new solutions available on newer API's.

 

who gives a flying F if the stupid card can blast 10 billion static tris of the same texture per sec?      that's not what i need!      i need 1 million draw calls of 50 tris each with about a dozen different textures!  (and that's just for grass!)

D3D12 has alot of what you need.  Lots of draw calls and dynamic indexing of textures due to its new binding model. https://msdn.microsoft.com/en-us/library/windows/desktop/mt186614(v=vs.85).aspx

 

There are work around for D3D11 - there is something called manual vertex fetching (requires shaders) combined with instancing and texture atlus's allow you to get alot of variation with 1 or a few draw calls. Look at the last (gpu driven rendering pipeline) presentation on this page.   http://advances.realtimerendering.com/s2015/index.html the other presentation you'd need for background info is Vertex-Shader-Tricks-Bill-Bilodeau.

 

As far as swaying goes if you switch over to shaders I think you can do the animation in a shader.  And as far as FF goes can't you use a dynamic index buffer to do vertex animation on the grass?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this