Realistic Grass

I have a rather simple question. Whether the answer will be simple or not, I don’t know.

Is there any way to make 3D grass that animates (blows around in the wind, shifts around when you walk over it, generally be affected by other forces) without having to write some hairy javascript? And how could I optimize it so that it doesn’t slaughter a quad G5 (not that I own one…) ?

If there isn’t any way to do it without it being terribly GPU expensive, I guess I could just do a Halo-like bump map for the ground and forget the crazy grass…

But that would be pretty cool…

I’d try something with particles first. No swaying blades but if you’re just looking for more depth/dynamics it should be pretty cheap and easy. You could end up spending a while tweaking it but…maybe.

Speaking of hairy…

What you are describing is essentially the same problem as modeling and animating hair, which is an extremely difficult (and not completely solved) problem. Your best bet is to be as creative as you can and cook up some tricks to try and approximate the look you seek.

when the Vertext Coloring shaders are available (kicks OTEE again) you will be able to make grass that can sway in the wind and such. Jeff

Actually, you don’t need vertex colors for swaying grass… You “just” need to write some shaders.

Good lord Nicholas, show us the way!!! Give us some example code please! You said before this was “bleeding edge” Unity stuff (not available in the current build). If we can do this now then please give us an example. I am so looking forward to this type of interaction within an environment.

Try this shader… It does windy grass… Just make a bunch of planes that are scattered randomly and intersects each other… Then put a grass texture on it.

Shader "Nature/Windy Grass" {
	Properties {
		_Color ("Main Color", Color) = (.5, .5, .5, 0)
		_Color2 ("Fade Color", Color) = (1, .9, .8, 0)
		_MainTex ("Base (RGB) Alpha (A)", 2D) = "white" {}
		_Cutoff ("Base Alpha cutoff", Range (0,1)) = .5
	}
	SubShader {
		Tags { "Queue" = "Transparent" }
		BindChannels {
			Bind "Color", color
			Bind "Vertex", vertex
			Bind "TexCoord", texcoord
		}

		Pass {

CGPROGRAM
// vertex Vertex

#include "waves.cginc"		// Get in the helper wave functions
#include "UnityCG.cginc"		// Get standard Unity constants

struct Appdata {
	float4 vertex;
	float3 normal; 
	float4 texcoord;
	float4 color;
};

struct v2f {
	float4 pos : POSITION;
	float4 color : COLOR0;
	float4 uv : TEXCOORD0;
	float fog : FOGC; 
}; 

uniform float4 _Color, _Color2;

v2f Vertex(Appdata v) {
	v2f o;

	const float4 _waveXSize = float4(0.012, 0.02, -0.06, 0.048) * 2;
	const float4 _waveZSize = float4 (0.006, .02, -0.02, 0.1) * 2;
	const float4 waveSpeed = float4 (0.3, .3, .08, .07) * 4;

	float4 _waveXmove = _waveXSize * waveSpeed * 25;
	float4 _waveZmove = _waveZSize * waveSpeed * 25;
	
	// We model the wind as basic waves...

	// Calculate the wind input to leaves from their vertex positions....
	// for now, we transform them into world-space x/z positions... 
	// Later on, we should actually be able to do the whole calc's in post-projective space
	float3 worldPos = mul ((float3x4)_Object2World, v.vertex);
	
	// This is the input to the sinusiodal warp
	float4 waves;
	waves = worldPos.x * _waveXSize;
	waves += worldPos.z * _waveZSize;

	// Add in time to model them over time
	waves += _Time.x * waveSpeed;

	float4 s, c;
	waves = frac (waves);
	FastSinCos (waves, s,c);

	float waveAmount = v.texcoord.y;
	s *= waveAmount;

	// Faste winds move the grass more than slow winds
	s *= normalize (waveSpeed);

	s = s * s;
	float fade = dot (s, 1.3);
	s = s * s;
	float3 waveMove = float3 (0,0,0);
	waveMove.x = dot (s, _waveXmove);
	waveMove.z = dot (s, _waveZmove);
	
	v.vertex.xz -= mul ((float3x3)_World2Object, waveMove).xz;

	o.pos = mul(glstate.matrix.mvp, v.vertex);
	o.fog = o.pos.w;
	o.uv = v.texcoord;
	o.color = lerp (_Color, _Color2, fade.xxxx);

	return o;
}
ENDCG
			AlphaTest Greater [_Cutoff]
			Cull Off
			SetTexture [_MainTex] { combine texture * primary double, texture}
			
		}
	}	
	SubShader {	
		Tags { "Queue" = "Transparent" }
		Pass {
			Blend SrcAlpha oneMinusSrcAlpha
			AlphaTest Greater [_Cutoff]
			Cull Off
			color [_Color]
			ZTest Less
			SetTexture [_MainTex] { combine texture * primary DOUBLE, texture}
		}

	}
}

muchos gracias :slight_smile:

shader language doesnt make any sense at all :shock:

ps: its “muchos grassias”! :lol:

1 Like

I don’t have pro, but I like being wowed. Anyone want to post a small web version to show it off? :smile:

This method works real well in Torque with the right textures. It’s not as intensive as it sounds. And since Unity > Torque + 9999, this makes me happy. I didn’t know shaders could add geometry like that. My mind is blown.

Thanks for the shader.
Can you use this shader with standard assets? Or do you need Pro?

I ask because I get an error:
" Can’t open include file “waves.cginc” (line 21) "

Line 21:
" #include “waves.cginc” // Get in the helper wave functions "

Is it trying to fetch a pro assets right there?

I have pro as well, and does it too.

There is nothing PRO about this effect - I simply forgot to include a file…

Paste this into waves.cginc and place the file next to the shader

// Calculate a 4 fast sine-cosine pairs
// val: 	the 4 input values - each must be in the range (0 to 1)
// s:		The sine of each of the 4 values
// c:		The cosine of each of the 4 values
void FastSinCos (float4 val, out float4 s, out float4 c) {
	val = (val * 1.02 - .5) * 6.28318530;	// scale to range: -pi to pi  make it cyclic
	// powers for taylor series
	float4 r5 = val * val;					// wavevec ^ 2
	float4 r1 = r5 * val;						// wavevec ^ 3
	float4 r6 = r1 * val;						// wavevec ^ 4;
	float4 r2 = r6 * val;						// wavevec ^ 5;
	float4 r7 = r2 * val;						// wavevec ^ 6;
	float4 r3 = r7 * val;						// wavevec ^ 7;
	float4 r8 = r3 * val;						// wavevec ^ 8;

	//Vectors for taylor's series expansion of sin and cos

	float4 sin7 = {1, -0.16161616, 0.0083333, -0.00019841};

	float4 cos8  = {-0.5, 0.041666666, -0.0013888889, 0.000024801587};

	// sin
	s = r1 * sin7.y + val + r2 * sin7.z + r3 * sin7.w;

	// cos
	c = 1 + r5 * cos8.x + r6 * cos8.y + r7 * cos8.z + r8 * cos8.w;
}

float4 DoCalcWave (float4 waveoffsets, inout float3 vertex, inout float3 normal, 
	float4 waveHeights, float4 waveDirX, float4 waveDirY) {
	float4 s, c;
	FastSinCos (waveoffsets, s, c);
	// wave height
	float height = dot (s, waveHeights);
	// offset vertex by normal
	vertex.xyz += normal * height;
	// offset normal by cos (wave)
	float4 coswave = c * waveHeights;
	normal.xz += float2 (dot (coswave, waveDirX), dot (coswave, waveDirY)) * uvParams.z;
	normal = normalize (normal);
	return s;
}

waves.cg? I’m afraid you’ve lost me.

Sorry, and thank you for your patience.

Sorry, should have been more clear :wink: Bit stressed prior to GDC…

Open your favourite text editor, paste in the text above and save it in a file called ‘waves.cginc’. The file must go next to the shader.

You then should open the shader script and re-save it, so unity reimports it. This time, it can find the waves.cg file and the shader should be ready to use…

So can someone post a SS of the final product?

A screenshot wouldn’t really show anything. The effect is a sort of stretchy undulating of the texture you use it with. The grass texture I’m using right now is sort of a flattish crabgrass look – something with more of a height to it (like a low angle isometric view) would probably work better.

(BTW, waves.cg should be waves.cginc. So, you will need something like grass_shader.shader, waves.cginc, grass.mat, and grass.png. Then apply the material to a plane, for example.)

Here’s a serene scene to demonstrate. Currently, my grass texture is not too nice looking, but you’ll get the idea. Also, it’s on .mac so it’s not an instant load. :wink:
http://homepage.mac.com/aaronsullivan/unity/grasstest.html

That pro water or Indy water?