Simple Terrain Shader Tutorial

img
Dec
19

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.

grass

map for grass

rock

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);

simple_terrain_tut2

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;

simple_terrain_tut3

because I need only the y value this works better:

DIFFUSE = VAR1.yyy;

simple_terrain_tut4

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);

simple_terrain_tut5
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.

simple_terrain_tut6

Here below the code.

Have fun!

 

VertexShader

// simple terrain shader
 VAR1 = vec4(SRC_NORMAL.x,SRC_NORMAL.y,SRC_NORMAL.z,0);

FragmentShader

// simple terrain shader
 uniform texture grass;
 uniform texture rock;
 uniform float mix_amount;
 uniform float transition;
 float mixval = clamp( pow( VAR1.y / mix_amount ,transition) , 0, 1);
 DIFFUSE = mix(tex(rock,UV).rgb, tex(grass,UV).rgb, mixval);
  • img
    Carlixyz Reply
    Aug 24, 2016 @ 15:14 pm

    Wow, Thank You very much, I’m just beginning to grasp about godot and is just Awesome what (some) people can do, Please keep uploading more tuts, they never aren’t enough !

Skip to toolbar