FDNavigate back to the homepage

How to animate grass and alpha clipped grass in react three fiber using GLSL

Rick
March 31st, 2023 · 2 min read

This is going to be a really quick look at a really cool way to animate grass. Which I got inspiration from a great tutorial on how to create stylized grass from Pierrick Picaut.

This tutorial goes in depth on how to accomplish the effect of stylized grass in blender and looks at animating the grass! I wont be going into loads of details on how to export grass from blender as this has been covered in previous tutorials on thefrontdev, but I will be explaining how I create animated grass in a similar way.

The tutorial

Pierrick Picaut does some truly inspirational tutorials and you can learn a lot about shaders and how to accomplish lots of different effects by watching his YouTube videos.

This one really struck me as a really good example of porting to raw GLSL and @react-three/fiber. In particular the part about using noise to animate the alpha clipped grass.

Alpha clipping and alpha maps

The alpha map is just a image which has a object in it where parts of the image have an opacity lower than 1.

opacity = 1 === fully opaque

opacity = 0 === effectively invisible

Wavy grass base alpha map

So how can we leverage this in a fragment shader?

In the fragment shader, you would sample the alpha map and get the r channel like below:

1float alpha = texture2D(grassTexture, uvs).r;
2//If transparent, don't draw
3if (alpha < 0.005) discard;

This bit of code in the main function of a fragment shader will do the following, check if the alpha is below 0.005. So basically is this fragment of the material invisible and if so we use the GLSL key word - discard. This will tell the fragment shader to discard this fragment for every run of the shader where the fragments opacity is below 0.005.

and thats it, its that simple!

I bet you can imagine why this would be useful in situations like these or when using points and you want a custom shape on each point.

Vertex and Fragment shaders

1export const vertexShader = `
2varying vec2 vUv;
3varying vec3 pos;
4void main () {
5 vUv = uv;
6 pos = position;
7 gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
8}
9`;
1import { noise3D } from "./noise";
2
3export const fragmentShader = `
4uniform float time;
5uniform sampler2D grassTexture;
6
7varying vec3 pos;
8varying vec2 vUv;
9
10${noise3D}
11
12void main () {
13 float scaledNoise = .0190;
14 float noise = noise(pos);
15
16 float wavyStrength = sin(time + pos.y)* 0.04;
17 vec2 uvs = vec2(mix(vUv.x, noise * scaledNoise , wavyStrength ), vUv.y);
18
19 float alpha = texture2D(grassTexture, uvs).r;
20 //If transparent, don't draw
21 if (alpha < 0.005) discard;
22
23 vec4 color = texture2D(grassTexture, uvs.xy);
24 gl_FragColor = color;
25}
26`;

The vertex shader is pretty bog standard and the only special thing is we pass 2 varyings to the fragment shader, pos and vUv.

The fragment shader is where it all happens.

First off we get the noise using this gist which has kindly collated it from various places.

Then we use a sine modulator to get the sway back and forth.

During which we use the y positional coordinate which will apply a sin wave in the y axis. We slow this down to 4% (0.04) of its speed as we don’t want a super fast animation, sometimes its all about the subtle animations which would make a scene.

The code below is how we control the speed of the waviness.

1vec2 uvs = vec2(mix(vUv.x, noise * scaledNoise , wavyStrength ), vUv.y);

We mix noise we created earlier with the x coordinate from the uvs and use the oscillating wavyStrength to control how much noise is applied to the x axis of the uvs.

Giving the overall effect of wavy grass in the wind! Obviously this is still quite strong so you could slow it down further or decrease the applied noise by decreasing the scale even further.

The final part is the color, or which you can put any color here you like. If you has loads of grass planes you could use the positional values of each vertex to get a noise value and then apply this to the grass color. And as each plane would have a different position you could have patchy grass.

More articles from theFrontDev

Want to make a cool shader in three ? then come read on!

Really cool tutorial on how to use custom shaders to make abstract visuals and integrate with threejs and USING GLSL. Look at the code example for some indepth detail on how to create something like this!

March 27th, 2023 · 1 min read

How to create a immersive 3D environment for the web

The number 1 walk-through for creating an immersive environment for the web. A in depth look on converting a blender project into a optimized web project. Including amazing fireflies, grass, and HDRI image creation.

January 14th, 2023 · 7 min read
© 2021–2024 theFrontDev
Link to $https://twitter.com/TheFrontDevLink to $https://github.com/Richard-ThompsonLink to $https://www.linkedin.com/in/richard-thompson-248ba3111/