# Basic terrain shader without using external texture

I have this:

(Right now I have the height map in a x*x size 2D array and a 1D vector too.)

What I am trying to achieve is something like this: Without using any textures, only plain colors. So basically smooth transitions and some shadow (using shaders). My vertex shader looks like this:

    #version 330
layout (location = 0) in vec3 Position;
layout (location = 1) in vec3 Normal;
layout (location = 2) in vec3 Color;

out vec3 fragmentNormal;
out vec4 ex_color,pos;
out vec3 N;
out vec3 v;

void main () {
pos= vec4(Position,1);
ex_color = vec4(Color,1);
fragmentNormal = Normal;

v = vec3(gl_ModelViewMatrix * pos);
N = normalize(gl_NormalMatrix * Normal);

gl_Position = gl_ModelViewProjectionMatrix * vec4(Position,1);


I have normals for all the vertices. Color is set simply in the c++ code based on height.

    in vec3 N;
in vec3 v;
in vec4 ex_color;
void main(void)
{
vec3 L = normalize(gl_LightSource[0].position.xyz - v);
vec4 Idiff = gl_FrontLightProduct[0].diffuse * max(dot(N,L), 0.0);
Idiff = clamp(Idiff, 0.0, 1.0);
gl_FragColor = Idiff*ex_color;
}


So I guess my problem is what formula should I use to mix the colors. I think I don't need to set the colors in the c++ code but in the shaders.

Update: Here is the wireframe of the terrain.

Update2: Based on @Babis' answer the result is:

So the gradient is not "projected" onto the surface as I would like to do. What could cause this? Maybe my qustion wasn't clear.

• there's a difference between screen Y (what you appear to use for that last pic) and world Y (what you want to use) – ratchet freak Nov 3 '14 at 11:22

It's a simple interpolation. Let's assume that your heights span -1 to 1, 0 is water surface and you have 3 colours: dark green, light green and white. A way to do what you want is to have the following color_from_height function in the pixel or fragment shader (depending on how efficient you want to be). In my shader below I visualize the gradient, but the function color_from_height should get the world space height.

// This is a shadertoy example (www.shadertoy.com) which will visualize the 4-colours palette

vec3 color_from_height( const float height )
{
vec3 terrain_colours[4];
terrain_colours[0] = vec3(0.0,0.0,0.6);
terrain_colours[1] = vec3(0.1, 0.3, 0.1);
terrain_colours[2] =  vec3(0.4, 0.8, 0.4);
terrain_colours[3] = vec3(1.0,1.0,1.0);
//vec3 terrain_colours[1] = vec3[1]{ vec3(0,0,0.6)};
if(height < 0.0)
return terrain_colours[0];
else
{
float hscaled = height*2.0 - 1e-05; // hscaled should range in [0,2)
int hi = int(hscaled); // hi should range in [0,1]
float hfrac = hscaled-float(hi); // hfrac should range in [0,1]
if( hi == 0)
return mix( terrain_colours[1],terrain_colours[2],hfrac); // blends between the two colours
else
return mix( terrain_colours[2],terrain_colours[3],hfrac); // blends between the two colours
}
return vec3(0.0,0.0,0.0);
}

void main(void)
{
vec2 uv = gl_FragCoord.xy / iResolution.xy;
vec3 col = color_from_height(uv.y*2.0-1.0);
gl_FragColor = vec4(col,1.0);
}

• you misunderstood my answer, the height that you need to provide is the "world space height". the screen space height that I used was to visualize the gradient :) – Babis Nov 3 '14 at 11:06
• Thanks I got it :) Now I need to rescale my terrain to [0,1] or modify your solution to my range :) What would you prefer? – Zoltantan Nov 3 '14 at 17:01
• None of the two, really :) you can provide in the shader the maximum height and "normalize" the provided height: norm_height = height/max_height; color_from_height( norm_height ); That way you barely do any changes. – Babis Nov 3 '14 at 17:08