Changing terrain using height map and vertex shader

I need to use a heightmap (.png) to modify a terrain (2D plane) but I just can’t make it work.
I am trying to make a new texture using the .png, then in vertex shader ( I need to work with vertex shader ) I have to modify the vertexes depending on the texture.

channels = 1 // I think this is for grey

Creating the texture :

    heightPixels = stbi_load((textureLoc + "heightmap.png").c_str(), &width, &height, &channels, STBI_grey);
    		GLuint textureID = 6;
    		glGenTextures(1, &textureID);
    		glBindTexture(GL_TEXTURE_2D, textureID);

    		//Send heightPixels to gpu
    		glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, (void*)heightPixels);

    		Texture2D* texture = new Texture2D();
    		texture->Init(textureID, width, height, channels);
    		mapTextures["heightMap"] = texture;

Vertex Shader:

    #version 330

    layout(location = 0) in vec3 v_position;
    layout(location = 1) in vec3 v_normal;
    layout(location = 2) in vec2 v_texture_coord;
    layout(location = 3) in vec3 v_color;

    // Uniform properties
    uniform mat4 Model;
    uniform mat4 View;
    uniform mat4 Projection;
    uniform sampler2D texture_1;
    uniform int heightMapSize;

    uniform float time;

    out vec2 texcoord;

    void main()
    	// TODO : pass v_texture_coord as output to Fragment Shader
    	texcoord = v_texture_coord;
    	float u = v_texture_coord.x;
    	float v = v_texture_coord.y;

    	float height = texture2D(texture_1, vec2(u,v)).r;

        //normalization for lighting
    	vec2 texelSize = vec2(1.0/heightMapSize,1.0/heightMapSize);
    	float heightRight = texture2D(texture_1, vec2(u + texelSize.x,v)).r;
    	float heightUp = texture2D(texture_1, vec2(u,v + texelSize.y)).r;

    	float Hx = height - heightRight;
    	float Hz = height - heightUp;
    	vec3 normal = normalize(vec3(Hz,1,Hz));

    	//Calculating new position
    	vec3 new_pos = v_position + vec3(0,0,height * 10);

    	gl_Position = Projection * View * Model * vec4(new_pos, 1.0);

Fragment Shader:

    #version 330
    uniform sampler2D texture_1;
    uniform sampler2D texture_2;
    uniform int mixer;

    in vec2 texcoord;

    layout(location = 0) out vec4 out_color;

    void main()
    	// TODO : calculate the out_color using the texture2D() function
    	vec4 color1 = texture2D(texture_1, texcoord);
    	vec4 color2 = texture2D(texture_2, texcoord);
    	vec4 color;
    	if(mixer == 1)
    		color = mix(color1, color2, 0.5f);
    		color = color1;

    	if(color.a < 0.5f)

    	out_color = color;

The problem is that the texture is invisible (nothing appears), if I use other textures (random ones) they appear but the plane is white and there is no deformity.

A couple of points here. First, if the width isn’t guaranteed to be a multiple of 4, you need to call glPixelStorei(GL_UNPACK_ALIGNMENT, 1). Second, unless you’re subsequently calling glGenerateMipmap on the texture (or explicitly clamping the range of mipmap levels), you need to set the minification filter to GL_NEAREST or GL_LINEAR with e.g.


The default filter is GL_NEAREST_MIPMAP_LINEAR, which won’t work if the texture doesn’t have all mipmap levels defined.

Thanks for your answer, using glGenerateMipmap or glTextureParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); works, the plane is now visible, but it is still a plane, no deformity, I am guessing the problem is in vertex shader now.

Another thing I notice is that you’re calculating the normal assuming that vertical is Y but calculating the offset assuming that vertical is Z.

That isn’t the problem, I know about it, I was testing and forgot to edit it.
The problem is u and v are the same everytime, if I do something like

float u = v_position.x/heightMapSize;
float v = v_position.y/heightMapSize;

then it works, the land will deform, the problem with this is tht it depends on the position in xOy coordinates, why textureCoords doesn’t work? And it also depends on how big the terrain is, is there a way to make it work everytime doesn’t matter the size or the position?

Are you passing valid data for attribute 2?

But if the geometry is (initially) a regular grid, you don’t need both positions and texture coordinates, as either one can be generated from the other.

What do you mean? The texture_1 is good, I even try to deform it at some (x,y) point by modifying the heightPixels array and it worked. v_texture_coord shouldn’t be the coordinate in (u,v) for the current vertex? how would I use only v_texture_coord and not v_position?

If you’re getting the expected result using v_position.xy but not when using v_texture_coord, that suggests that this attribute:
layout(location = 2) in vec2 v_texture_coord;
isn’t getting fed the appropriate values.

Are you populating the buffer with the correct data? Is the correct buffer bound at the time of the glVertexAttribPointer call? Are the parameters to the glVertexAttribPointer call correct? Is the attribute enabled?

You aren’t showing any of that code, so it’s impossible to say what might be wrong.

It should.

I note that you’re using that data for both the height map and the colour. That’s fine if you’re mapping the terrain to the same portion of both textures. If you’re doing something else (e.g. tiling the colour texture) then you need multiple sets of texture coordinates (or generate both sets from the position).

texcoord = (v_position.xy - origin) / size;
where origin and size are uniforms.

(and also use texcoord in the texture2D call rather than reconstructing it as vec2(u,v)).

This is the first time I hear about this, I only do what I wrote above, import the .png, create the texture as shown above,and sending it to GPU. What should I do? Searched on the internet but don’t understand how that works.

The only thing that I do that is missing is this piece of code from rendering the mesh :

if (texture2)
		//TODO : activate texture location 1
		//TODO : Bind the texture2 ID
		//TODO : Send texture uniform value
		glBindTexture(GL_TEXTURE_2D, texture2->GetTextureID());
		glUniform1i(glGetUniformLocation(shader->GetProgramID(), "texture_2"), 1);

	// Draw the object
	glDrawElements(mesh->GetDrawMode(), static_cast<int>(mesh->indices.size()), GL_UNSIGNED_SHORT, 0);

You must have initialized that VAO somewhere otherwise you wouldn’t be getting anything.

Go find the point where you’re calling glVertexAttribPointer (or glVertexAttribFormat or glVertexArrayAttribFormat). Whatever you’re doing to send the data for v_position, you need to do something similar for the other attributes (well, at least for v_texture_coord; you don’t appear to be using v_normal or v_color).

If you’re just copying and pasting bits of code that you found without actually understanding it, you need to read a book or tutorial. You aren’t going to figure out OpenGL programming by guesswork.

This may be off topic but … why do you use a texture for position.z instead of writing this value to .z before sending positions to gpu? You’ve got attribute normals that you can calculate with those positions - all once - they are after all somewhat constant.