Simple Terrain Shader Tutorial

A couple of weeks ago I wished to try adding terrain tiles to FFX Runner2 by using the sculpt tool in Blender, initially I was intentioned of using some sort of texture splatting (two seamless textures on first UV mixed together by a third one on second UV, Marcos Bitetti made a nice video-tutorial on this topic) but after a while I preferred a more automatic process, at least for mapping slopes and cliffs.

What I wished was a shader adapting to the tilt of the ground, I mean, when the ground is near to be plain it should look as grass, on the contrary, when ground is near to be a cliff it should be rendered as rocks.

Here the test mesh, it was made with the sculpt tool in Blender

For simplicity let’s consider only the diffuse parameter, others may be implemented in the same way. What I need are one texture for the grass, one texture for the rocks and the surface normal vector so I can use the y component value to mix the two texture.

map for grass

map for rocks

Because FragmentShader can’t access surface normal is necessary to use VertexShader and pass the vector between them.

Here the code of the VertexShader, it’s very simple:

VAR1 = vec4(SRC_NORMAL.x,SRC_NORMAL.y,SRC_NORMAL.z,0);

Despite the hermetic description of VAR and VAR2 built-in variables, they seems to be here to pass values from VertexShader to FragmentShader. VAR1 and VAR2 expect to be vect4, this is the reason I filled the last value with a 0.

In FragmentShader, only to view what values are passed, this code give a graphical representation of them:

DIFFUSE = VAR1.xyz;

because I need only the y value this works better:

DIFFUSE = VAR1.yyy;

What you see is the map I will use to mix grass and rock.

The textures are loaded by using

uniform texture grass;
uniform texture rock;

getting the mix amount from the y component of VAR1

float mixval = VAR1.y;

and with a simple linear interpolation mix the two maps

DIFFUSE = mix(tex(rock,UV).rgb, tex(grass,UV).rgb, mixval);

Well, it works, but I wish to have a little more control over the mix, I wish to set prevalence of grass over rocks and the fade transition between them. So I’ll add two more shader parameters by adding

uniform float mix_amount;
uniform float transition;

and then modifying the mixval as follow:

float mixval = clamp( pow( VAR1.y / mix_amount ,transition) , 0, 1);

In this way I can tune the amount of the two textures and how they blend.