Multiple textures inside a triangle strip

I have been trying to include multiple textures within my triangle strip, however, it seems to me that all calls to glBindTexture must be outside the glBegin(GL_TRIANGLE_STRIP) and glEnd().

Perhaps I could use a texture atlas, but I’d need help getting the new texture co-ordinates.

Here is my code and an image of the “desired” effect.


                if (j % 2 == 0) // Second triangle
                    GL11.glTexCoord3f(0, 1, 0);
                else
                    GL11.glTexCoord3f(0, 0, 0);
                GL11.glVertex3f(j - xOffset, data[i][j] - yOffset, i - zOffset);

                texture2.bind(); // This doesn't work, but out side the GL11.glBegin(GL11.GL_TRIANGLE_STRIP), it does :\

                // We need to render the next strip to "link up" the two strips
                if (j % 2 == 0)
                    GL11.glTexCoord3f(1, 0, 1);
                else
                    GL11.glTexCoord3f(1, 1, 1);
                GL11.glVertex3f(j - xOffset, data[i + 1][j] - yOffset, (i + 1) - zOffset);

and the image:

Indeed, this doesn’t work that way. You can only bind a texture before any draw call.
The simplest solution to this is using a texture atlas and give appropriate texture coords for each vertex so that separate image appear on each primitive.
The more complicated one would be to use VBOs and use multiple sampler2D uniforms within the shader code - each vertex would then have to have an attribute indicating the texture unit it should use.

Would you mind helping me with the shaders? I’m quite new to writing with it, do you recommend any good tutorials?

I think, that perhaps this resources would help you:
http://learnopengl.com/#!Getting-started/Hello-Triangle
https://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_03 - especially this one

Otherwise you can search yourself, use keywords like: GLSL, VBO (Vertex Buffer Object), uniform, attribute, varying, programmable pipeline.

Keep in mind also that not every tutorial will work with every OpenGL version, so probably you will have to figure out what features to use depending on your target OpenGL version.

I’ve been quite busy recently, but unfortunately, my code isn’t working, here is my code and my shader:


        Texture texture1 = new Texture(Terrain.class.getResourceAsStream("/resources/textures/grass.jpg"));
        GL13.glActiveTexture(0);
        texture1.bind();
        Texture texture2 = new Texture(Terrain.class.getResourceAsStream("/resources/textures/stone.jpg"));
        GL13.glActiveTexture(1);
        texture2.bind();
        Init.glFill();
        useShader();
        for (int i = 0; i < data.length - 1; i++) {
            GL11.glBegin(GL11.GL_TRIANGLE_STRIP);
            for (int j = 0; j < data[i].length; j++) {
                if (j % 2 == 0) // Second triangle
                    GL11.glTexCoord3f(0, 1, 0);
                else
                    GL11.glTexCoord3f(0, 0, 0);
                GL11.glVertex3f(j - xOffset, data[i][j] - yOffset, i - zOffset);

                // We need to render the next strip to "link up" the two strips.
                // If this is commented out, you would just have a load of strips
                // that aren't connected.
                if (j % 2 == 0)
                    GL11.glTexCoord3f(1, 0, 1);
                else
                    GL11.glTexCoord3f(1, 1, 1);
                GL11.glVertex3f(j - xOffset, data[i + 1][j] - yOffset, (i + 1) - zOffset);
            }
            GL11.glEnd();
        }
        stopShader();
        Init.glLine();
        texture1.unbind();
        GL11.glEndList();

and


private void useShader() {
        int shaderID = ShaderLoader.loadShaderPair("/resources/shaders/terrain.vs", "/resources/shaders/terrain.fs");
        GL20.glUseProgram(shaderID);

        GL20.glUniform1f(GL20.glGetUniformLocation(shaderID, "grass"), 0);
        GL20.glUniform1f(GL20.glGetUniformLocation(shaderID, "stone"), 1);
    }

    private void stopShader() {
        GL20.glUseProgram(0);
    }

terrain.vs:


#version 120

varying float height;

void main() {
	height = gl_Vertex.y;
	gl_Position = ftransform();
}

terrain.fs:


#version 120

uniform sampler2D grass;
uniform sampler2D stone;

varying float height;

void main() {
	if (height >= 150) {
		gl_FragColor = texture2D(stone, vec2(gl_TexCoord[0].s, gl_TexCoord[0].t));
	} else {
		gl_FragColor = texture2D(grass, vec2(gl_TexCoord[0].s, gl_TexCoord[0].t));
	}
}

What am I doing wrong?

You can’t mix your intermediate mode calls with your pipeline calls

@dentmaged: What do you mean “doesn’t work”. No geometry on screen, not textured? I can tell you right now that you shouldn’t be loading the shader every frame.

Are there any other solutions which I could use?

You just need to convert everything to the pipeline way of doing things

Some comments

move


int shaderID = ShaderLoader.loadShaderPair("/resources/shaders/terrain.vs", "/resources/shaders/terrain.fs");
Texture texture1 = new Texture(Terrain.class.getResourceAsStream("/resources/textures/grass.jpg"));
Texture texture2 = new Texture(Terrain.class.getResourceAsStream("/resources/textures/stone.jpg"));

to your initialisation method

move


for (int i = 0; i < data.length - 1; i++) {
            GL11.glBegin(GL11.GL_TRIANGLE_STRIP);
            for (int j = 0; j < data[i].length; j++) {
                if (j % 2 == 0) // Second triangle
                    GL11.glTexCoord3f(0, 1, 0);
                else
                    GL11.glTexCoord3f(0, 0, 0);
                GL11.glVertex3f(j - xOffset, data[i][j] - yOffset, i - zOffset);

                // We need to render the next strip to "link up" the two strips.
                // If this is commented out, you would just have a load of strips
                // that aren't connected.
                if (j % 2 == 0)
                    GL11.glTexCoord3f(1, 0, 1);
                else
                    GL11.glTexCoord3f(1, 1, 1);
                GL11.glVertex3f(j - xOffset, data[i + 1][j] - yOffset, (i + 1) - zOffset);
            }
            GL11.glEnd();
        }

in to 2 byte buffers, one containing the vertex data and one containing the texture coordinate data. Then create 2 VBO’s and pass these to your vertex shader

There is an excellent tutorial post from @riven describing different ways to do this

You then need to integrate these VBO’s in to your shaders so you can pass in the vertex/texture data to be drawn. You can do this in different ways depending on the shader version you want to use. I use the below way


layout(location = 0) in vec3 vertexData;
layout(location = 1) in vec2 textureData;

You can tell opengl to use the vertexData you pass (assuming no other manipulation of the data)


gl_Position = vertexData;

You then need to pass the texture data from the vertex shader to the texture shader and use that in the texture2D method you have. If you use a variable called theTextureData to pass this then you can extract the correct fragment by doing


texture(texture_number, theTextureData);

This should be enough to give you a very basic understanding of what to do, you can easily google to find the more detailed information