Good formula for swaying grass?

Started by
23 comments, last by Norman Barrows 7 years, 2 months ago

Anyone know a good formula for swaying grass?

In his dx9 shader book, luna uses

float sine = amplitude*sin(amplitude*gTime);
posW.xyz += sine*right;
where amplitude is 0.5 through 1.0 for upper verts, and 0.0 for lower verts.
so upper verts sway back and forth left to right (with respect to lookat). and higher amplitudes sway more, and faster.
But wind is more of a force that varies across a field of grass, and pushes against the plant, which is a dampened spring system, that bends as the force varies. when the force is removed, it oscillates back to its neutral position.
as a first stab at a formula i came up with:
pos.xyz += sin(time) * Vwind * randval
where Vwind is a direction vector, and randval is a random per instance "scale factor" in the range 0.0 through 1.0.
the motion from using sin() is too uniform. Not gusty winds. No amber waves of grain.
it seems Vwind needs to be on a per vertex basis, which means editing the instance VB each frame. something i DEFINITELY don't want to do.
while looking at samples online trying to find the code to animate a vertex based on UV, i saw references to things like adding a bit of noise etc to mix it up a bit.
adding in sin(pos.x) or sin(pos.z) depending on wind direction should get waves across a field once you get the period right.
but what about gusts? and gusts that move? adding in sin(pos.x % some_value) and sin(pos.z % some_value) would create gustier areas, but they would never move.they would always be in the same place in the field of grass.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

Advertisement

You should check out some of the CryTek documents. They implemented a pretty nifty method for vegetation animation. To be honest I've only implemented Sine wave animation for my projects, but the results of this look fantastic.

http://http.developer.nvidia.com/GPUGems3/gpugems3_ch16.html

https://mtnphil.wordpress.com/2011/10/18/wind-animations-for-vegetation/

You should check out some of the CryTek documents.

from the GPU gems 3 link:

"A wind vector is computed per-instance"

So that's no good. i'll have 360,000 or 540,000 instances per frame. I really want to stay away from updating instance data every frame. Or maybe that's an arbitrary decision on my part? I never thought i'd be able to draw 1 million grass plants at 60fps, but i'm doing it. Maybe i can update half a million instances per frame too?

I'm not familiar with the capabilities of cry engine 3. can it draw half a million visible animated grass plants at 60 fps? and still have time left for the rest of the game? even if you have to update the instance data every frame?

The link makes no mention of performance numbers - simply that it can do it, but it doesn't say how many at once at what FPS on what hardware.

In the second link, while its based on the first example, it does not have per instance animation data - so that's cool, but sway is simply a function of time and some sway scaling and stiffness constants. And Vwind is a constant, not a variable based on location - not so cool. almost looks like all plants would sway in unison... since there's no exe file, its a bit of work to even know exactly what the effect looks like. So that code doesn't even address the issue of Vwind should not be the same for all plants across a field.

Looks like what i need is a way to use the data in a vertex to come up with a Vwind value for that vertex. But simply using position means the same areas would always have the same amount of gustiness - right?

since pos, norm, and UV for a vertex in the source mesh never change, modulating them with any variable passed in would always result in the same areas behaving the same way - right?

The search continues....

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

"A wind vector is computed per-instance" So that's no good. i'll have 360,000 or 540,000 instances per frame. I really want to stay away from updating instance data every frame.

Well, *something* needs to change per-instance every frame if you want each plant to animate separately.

Have a world "wind texture" that can be sampled from the vertex shader to determine the wind strength and direction at a particular position ("plant world position" will have to be part of your instance data).

Well, *something* needs to change per-instance every frame if you want each plant to animate separately.

heh, heh, heh - yeah - i know...

Have a world "wind texture" that can be sampled from the vertex shader to determine the wind strength and direction at a particular position ("plant world position" will have to be part of your instance data).

Ooh! Give that man a cigar! We just might have a winner!

Ok, so i can do a texture lookup to get a Vwind. But then i'm generating a texture every frame or few frames... if i use a 300x300 texture for a 300x300 chunk that has one plant per d3d unit, i'm still generating 90,000*4 bytes of data per chunk per frame.

maybe i should just try brute force first, and set Vwind for each instance each frame.

What about multiple streams?

mesh in stream 0

instance data in stream 1

Vwind in stream 2

Can you do multiple streams and hardware instancing at the same time?

So lets assume for a moment i can figure out how to get Vwind on a per vertex basis (somehow)....

What's a good formula / algo for generating wind patterns?

You know me, always thinking ahead to the next thing i haven't figured out yet.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

I really want to stay away from updating instance data every frame. Or maybe that's an arbitrary decision on my part?


Weird, it's almost like there was a reason I recommended that you do this all on the GPU in the other thread. :)

You shouldn't ever need to "update instances." You pass in a wind gradient (or parameters to a function that calculates the gradient) to your shader which performs all the deformation on the fly, on the GPU, as the grass is being rendered, with full LoD support baked in to the animation (because grass further away will have simpler animations, since you can't see it move much anyway).

This then also lets you do localized deformation, e.g. making your grass bend away from creatures moving through it, with only nominal per-instance or per-creature overhead (you define a force gradient for the creature and provide its parameters to the GPU too, which is then interpolated in with the global wind gradient).

Sean Middleditch – Game Systems Engineer – Join my team!

If you only care about swaying and not wind patterns in a global sense, you could probably determine swayage using the value noise that shader-toy is fond of.

https://www.shadertoy.com/view/lsf3WH

You can use it to generate a cheap approximation of perlin-noise on the GPU. It's bound to look better than a trig wave, and I believe (don't quote me on this) that it's cheaper than sampling from a gradient texture too.

You could probably layer a couple samples together to get erratic results that look like gusts. You can do all of the classic noise-combination tricks like sampling them at different scales, translating the sample positions at different speeds, multiplying normalized samples together, etc. There's going to be some magical franken-combination that looks good I think.

Edit: I messed around with it a little out of curiosity. I think this could look pretty good. Paste this into shader toy:


// Created by inigo quilez - iq/2013
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.

// Value Noise (http://en.wikipedia.org/wiki/Value_noise), not to be confused with Perlin's
// Noise, is probably the simplest way to generate noise (a random smooth signal with 
// mostly all its energy in the low frequencies) suitable for procedural texturing/shading,
// modeling and animation.
//
// It produces lowe quality noise than Gradient Noise (https://www.shadertoy.com/view/XdXGW8)
// but it is slightly faster to compute. When used in a fractal construction, the blockyness
// of Value Noise gets qcuikly hidden, making it a very popular alternative to Gradient Noise.
//
// The princpiple is to create a virtual grid/latice all over the plane, and assign one
// random value to every vertex in the grid. When querying/requesting a noise value at
// an arbitrary point in the plane, the grid cell in which the query is performed is
// determined (line 30), the four vertices of the grid are determined and their random
// value fetched (lines 35 to 38) and then bilinearly interpolated (lines 35 to 38 again)
// with a smooth interpolant (line 31 and 33).


// Value    Noise 2D: https://www.shadertoy.com/view/lsf3WH
// Value    Noise 3D: https://www.shadertoy.com/view/4sfGzS
// Gradient Noise 2D: https://www.shadertoy.com/view/XdXGW8
// Gradient Noise 3D: https://www.shadertoy.com/view/Xsl3Dl
// Simplex  Noise 2D: https://www.shadertoy.com/view/Msf3WH


float hash(vec2 p)  // replace this by something better
{
    p  = 50.0*fract( p*0.3183099 + vec2(0.71,0.113));
    return -1.0+2.0*fract( p.x*p.y*(p.x+p.y) );
}

float noise( in vec2 p )
{
    vec2 i = floor( p );
    vec2 f = fract( p );
	
	vec2 u = f*f*(3.0-2.0*f);

    return mix( mix( hash( i + vec2(0.0,0.0) ), 
                     hash( i + vec2(1.0,0.0) ), u.x),
                mix( hash( i + vec2(0.0,1.0) ), 
                     hash( i + vec2(1.0,1.0) ), u.x), u.y);
}

float fbm( in vec2 p)
{
    mat2 m = mat2( 1.6,  1.2, -1.2,  1.6 );
    float f = 0.0;
    f += 0.5000*noise( p ); p = m*p;
    f += 0.2500*noise( p ); p = m*p;
    f += 0.1250*noise( p ); p = m*p;
    f += 0.0625*noise( p );
    return f;
}

float norm( in float f )
{
    return 0.5 + 0.5 * f;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 p = fragCoord.xy / iResolution.xy;

    vec2 uv = p * vec2(iResolution.x/iResolution.y,1.0);

    float time = iGlobalTime;

    uv *= 8.0;

    vec2 uv1 = uv * 0.5 + vec2(time * 0.3, time * 0.7) * 0.15;
    vec2 uv2 = uv + vec2(time, -time) * 0.23;
    float f1 = norm(fbm(uv1));     
    float f2 = norm(fbm(uv2));

    float f = f1 * f2;
	
    fragColor = vec4( f, f, f, 1.0 );
}

You would just evaluate something like that at each grass position.

Ok, so i can do a texture lookup to get a Vwind. But then i'm generating a texture every frame or few frames...

You don't need to update the texture. Just apply a sine wave (or a noise function) over time to the output of the texture.

Wind doesn't really change direction on the time scales most games deal in. It tends to gust and die down with the same general directional pattern.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

You shouldn't ever need to "update instances." You pass in a wind gradient (or parameters to a function that calculates the gradient) to your shader which performs all the deformation on the fly, on the GPU, as the grass is being rendered,

check. got all that up and running.

with full LoD support baked in to the animation (because grass further away will have simpler animations, since you can't see it move much anyway).

No LOD yet. i'm at a million instances, and still at 62 fps. i should put an elasped_ticks() timer on it - see what the overhead really is. between ticks and some clock speed value, i think you can calc ms.

This then also lets you do localized deformation, e.g. making your grass bend away from creatures moving through it, with only nominal per-instance or per-creature overhead (you define a force gradient for the creature and provide its parameters to the GPU too, which is then interpolated in with the global wind gradient).

i was thinking of passing in one gradient per instance. IE one Vwind per instance. mesh in stream 0, worldmats in stream 1, Vwinds in stream 2. mesh and worldmats are constants. Vwinds changes each frame or whatever. but i doubt dx9 can do that. which means i lock the instance buffer and write new Vwind vectors to it each frame.

To determine Vwind values, i was thinking a particle system of wind intensity emitters. say a dozen or so in visual range. they wander about and shrink and grow in strength, winking out, or rising up, respawing if they leave the 3x3 rterrain chunk area around the player. the strength would determine influence based on range to the plant, and the sum of the influences (at some falloff rate) would be combined with Vwind_global, to get each Vwind_instance.

At least that's the first brute force thing that comes to mind. I have yet to test sending a new Vwind per instance each frame, so i'm unsure of the performance hit of just that, if any.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

If you only care about swaying and not wind patterns in a global sense

you know me - I want them both <g>.

I've seen mention of noise to mix things up a bit. That code looks like it could work well as a nice noise plugin. Thanks!

And i know hat you mean about super-imposing waves to get cool results. The height map function for the game is based on super-imposed sin and cos vales in x and z. Its used to determine the y value of everything in the game, even the verts generated for a terrain chunk ground mesh.

I'll probably end up with some thing like per instance Vwind based on global wind, with noise, and some sort of gusts / waves of grain effect / system.

-----------------------------------------

OK folks, now some related questions:

double sided lighting: i DIP, set winding CW, set boolean to the shader to reverse normals, then DIP again, then set winding back to CCW - right? (CCW is the default winding dir in my in-house dx9 based gamedev lib)

normal transform. in the Nvidia code samples, they were transforming the normals along with the vertex. necessary for correct lighting - right?

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

This topic is closed to new replies.

Advertisement