Texture doesn't appear properly

Hello,

This problem may not be related directly to shaders, but I don’t even know exactly where the problem is. Since I moved from fixed pipeline to shaders, the texture appears wrong. Here is a screenshot about it:


(The model has 2 planes which appears right, only the texture is wrong)

Some information about my code: I wrote an exporter for Blender which creates a binary model file. I wrote a loader for it. The model file can handle multiple objects (meshes) in one model. Each object has different texture. The first version uses VBO without shaders and works pretty well. After that I studied how to use shaders I changed my code based on this tutorial: http://www.opengl-tutorial.org/beginners-tutorials/tutorial-5-a-textured-cube/

I think the main problem is that I don’t know the concept of drawing a complex model with multiple object and texture. Most of the articles and tutorials shows how to draw one object.

Here is my drawing function. I call this in the main loop after the keyboard handling and camera update.


void CModel::draw(glm::mat4 &MVP)
{
    glActiveTexture(GL_TEXTURE0);

    for(unsigned int i=0; i<m_numObjects; i++)
    {
        glUseProgram(m_programID);

        unsigned int matrixUniform  = glGetUniformLocation(m_programID, "MVP");
        unsigned int textureUniform = glGetUniformLocation(m_programID, "myTextureSampler");

        glUniformMatrix4fv(matrixUniform, 1, GL_FALSE, &MVP[0][0]);

        glBindTexture(GL_TEXTURE_2D, m_textureIDs[i]);

        glUniform1i(textureUniform, 0);

        glBindBuffer(GL_ARRAY_BUFFER, m_bufferIDs[i]);
        // vertices
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(
                0,                  // must match the layout in the shader.
                3,                  // size
                GL_FLOAT,           // type
                GL_FALSE,           // normalized?
                sizeof(float)*8,    // stride
                (void*)0            // array buffer offset
        );

        // UVs
        glEnableVertexAttribArray(1);
        glVertexAttribPointer(
                1,
                2,
                GL_FLOAT,
                GL_FALSE,
                sizeof(float)*8,
                (void*)6
        );

        glDrawArrays(GL_TRIANGLES, 0, m_numElements[i]);

        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);

        glUseProgram(0);
    }

    //glDisable(GL_TEXTURE_2D);
}

The data array which is sent to the buffer has this format: [vertex.x, vertex.y, vertex.z normal.x, normal.y, normal.z, textureCoord.u, textureCoord.v, …]

The vertex and fragment shaders worked in the first version. Here are the shaders, but I don’t think it should be changed in any way.


#version 330 core

// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec2 vertexUV;

// Output data ; will be interpolated for each fragment.
out vec2 UV;

// Values that stay constant for the whole mesh.
uniform mat4 MVP;

void main(){

	// Output position of the vertex, in clip space : MVP * position
	gl_Position =  MVP * vec4(vertexPosition_modelspace,1);

	// UV of the vertex. No special space for this one.
	UV = vertexUV;
}


#version 330 core

// Interpolated values from the vertex shaders
in vec2 UV;

// Ouput data
out vec3 color;

// Values that stay constant for the whole mesh.
uniform sampler2D myTextureSampler;

void main(){

	// Output color = color of the texture at the specified UV
	color = texture2D( myTextureSampler, UV ).rgb;
}

As you are using 2 textures, it’s better to use 2 texture units, i.e. GL_TEXTURE0, and GL_TEXTURE1.

Do you say that instead of using glActiveTexture(GL_TEXTURE0) before the loop I should call it inside the loop before glBindTexture and in each loop set different unit?
I tried it but it doesn’t work or I just misunderstood you.

pleeeeeeeaaaaaaaaaaaaase i neeed help in my hw pleas :frowning: help we take simple code with our dr but he gave us a complex one to put comment on it and describe work each function

please need help

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <GL/glut.h>

GLfloat light_diffuse[] =
{1.0, 0.0, 0.0, 1.0};
GLfloat light_position[] =
{1.0, 1.0, 1.0, 0.0};
GLUquadricObj *qobj;

int win1, win2, submenu1, submenu2;

int list = 1;

float thetime = 0.0;

void
display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (glutGetWindow() == win1) {
glCallList(list); /* render sphere display list /
} else {
glCallList(1); /
render sphere display list */
}
glutSwapBuffers();
}

Void //
display_win1(void)
{
glPushMatrix();
glTranslatef(0.0, 0.0, -1 - 2 * sin(thetime));
display();
glPopMatrix();
}

idle(void)
{
GLfloat light_position[] =
{1.0, 1.0, 1.0, 0.0};

glutSetWindow(win1);
thetime += 0.05;
light_position[1] = 1 + sin(thetime);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
display_win1();
}

/* ARGSUSED */
void
delayed_stop(int value)
{
glutIdleFunc(NULL);
}

void
it(int value)
{
glutDestroyWindow(glutGetWindow());
printf("menu selection: win=%d, menu=%d
", glutGetWindow(), glutGetMenu());
switch (value) {
case 1:
if (list == 1) {
list = 2;
} else {
list = 1;
}
break;
case 2:
exit(0);
break;
case 3:
glutAddMenuEntry(“new entry”, value + 9);
break;
case 4:
glutChangeToMenuEntry(1, “toggle it for drawing”, 1);
glutChangeToMenuEntry(3, “motion done”, 3);
glutIdleFunc(idle);
break;
case 5:
glutIdleFunc(NULL);
break;
case 6:
glutTimerFunc(2000, delayed_stop, 0);
break;
default:
printf("value = %d
", value);
}
}

void
init(void)
{
gluQuadricDrawStyle(qobj, GLU_FILL);
glNewList(1, GL_COMPILE); /* create sphere display list /
gluSphere(qobj, /
radius / 1.0, / slices / 20, / stacks

                                                   */ 20);

glEndList();
gluQuadricDrawStyle(qobj, GLU_LINE);
glNewList(2, GL_COMPILE); /* create sphere display list /
gluSphere(qobj, /
radius / 1.0, / slices / 20, / stacks

                                                   */ 20);

glEndList();
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
gluPerspective( /* field of view in degree / 40.0,
/
aspect ratio / 1.0,
/
Z near / 1.0, / Z far / 10.0);
glMatrixMode(GL_MODELVIEW);
gluLookAt(0.0, 0.0, 5.0, /
eye is at (0,0,5) /
0.0, 0.0, 0.0, /
center is at (0,0,0) /
0.0, 1.0, 0.); /
up is in positive Y direction */
glTranslatef(0.0, 0.0, -1.0);
}

void
menustate(int inuse)
{
printf("menu is %s
", inuse ? “INUSE” : “not in use”);
if (!inuse) {
}
}

void
keyboard(unsigned char key, int x, int y)
{
if (isprint(key)) {
printf("key: `%c’ %d,%d
", key, x, y);
} else {
printf("key: 0x%x %d,%d
", key, x, y);
}
}

void
special(int key, int x, int y)
{
char *name;

switch (key) {
case GLUT_KEY_F1:
name = “F1”;
break;
case GLUT_KEY_F2:
name = “F2”;
break;
case GLUT_KEY_F3:
name = “F3”;
break;
case GLUT_KEY_F4:
name = “F4”;
break;
case GLUT_KEY_F5:
name = “F5”;
break;
case GLUT_KEY_F6:
name = “F6”;
break;
case GLUT_KEY_F7:
name = “F7”;
break;
case GLUT_KEY_F8:
name = “F8”;
break;
case GLUT_KEY_F9:
name = “F9”;
break;
case GLUT_KEY_F10:
name = “F11”;
break;
case GLUT_KEY_F11:
name = “F12”;
break;
case GLUT_KEY_LEFT:
name = “Left”;
break;
case GLUT_KEY_UP:
name = “Up”;
break;
case GLUT_KEY_RIGHT:
name = “Right”;
break;
case GLUT_KEY_DOWN:
name = “Down”;
break;
case GLUT_KEY_PAGE_UP:
name = “Page up”;
break;
case GLUT_KEY_PAGE_DOWN:
name = “Page down”;
break;
case GLUT_KEY_HOME:
name = “Home”;
break;
case GLUT_KEY_END:
name = “End”;
break;
case GLUT_KEY_INSERT:
name = “Insert”;
break;
default:
name = “UNKONW”;
break;
}
printf("special: %s %d,%d
", name, x, y);
}

void
mouse(int button, int state, int x, int y)
{
printf("button: %d %s %d,%d
", button, state == GLUT_UP ? “UP” : “down”, x, y);
}

void
motion(int x, int y)
{
printf("motion: %d,%d
", x, y);
}

void
visible(int status)
{
printf("visible: %s
", status == GLUT_VISIBLE ? “YES” : “no”);
}

void
enter_leave(int state)
{
printf("enter/leave %d = %s
",
glutGetWindow(),
state == GLUT_LEFT ? “left” : “entered”);
}

int
main(int argc, char *argv)
{
qobj = gluNewQuadric();
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
win1 = glutCreateWindow(“sphere”);
glutEntryFunc(enter_leave);
init();
glutDisplayFunc(display_win1);
glutCreateMenu(it);
glutAddMenuEntry(“toggle draw mode”, 1);
glutAddMenuEntry(“exit”, 2);
glutAddMenuEntry(“new menu entry”, 3);
glutAddMenuEntry(“motion”, 4);
glutAttachMenu(GLUT_LEFT_BUTTON);
glutCreateMenu(it);
glutAddMenuEntry(“yes”, 1);
glutAddMenuEntry(“no”, 2);
glutAttachMenu(GLUT_RIGHT_BUTTON);
win2 = glutCreateWindow(“second window”);
glutEntryFunc(enter_leave);
glutKeyboardFunc(keyboard);
glutSpecialFunc(special);
glutMouseFunc(mouse);
#if 0
glutMotionFunc(motion);
#endif
glutVisibilityFunc(visible);
init();
light_diffuse[1] = 1;
light_diffuse[2] = 1;
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glutDisplayFunc(display);
submenu1 = glutCreateMenu(it);
glutAddMenuEntry(“submenu a”, 666);
glutAddMenuEntry(“submenu b”, 777);
submenu2 = glutCreateMenu(it);
glutAddMenuEntry(“submenu 1”, 25);
glutAddMenuEntry(“submenu 2”, 26);
glutAddSubMenu(“submenuXXX”, submenu1);
glutCreateMenu(it);
glutAddSubMenu(“submenu”, submenu2);
glutAddMenuEntry(“stop motion”, 5);
glutAddMenuEntry(“delayed stop motion”, 6);
glutAddSubMenu(“submenu”, submenu2);
glutAttachMenu(GLUT_LEFT_BUTTON);
glutMenuStateFunc(menustate);
glutMainLoop();
return 0; /
ANSI C requires main to return int. */
}

need comment and what each function do

You should open a new thread and describe exactly what is you problem, copy some sample from the code. We can’t find out what is your home work, and it would be great to not spoil my thread.:slight_smile:

i apologize ,i dont know how to open new one thats y i asked u both to help me frome here realy sorry

I did a test, which shows that you could just use one texture unit, such as GL_TEXTURE0.

and it seems quite a few things are not quite right in your source code.

[QUOTE=Power2012;1245329]I did a test, which shows that you could just use one texture unit, such as GL_TEXTURE0.

and it seems quite a few things are not quite right in your source code.[/QUOTE]

What is wrong exactly? Here is the whole class with the init method to get a clearer picture of my code:


#include "global.h"
#include "CModel.h"
#include "CModelLoader.h"
#include "CShaderLoader.h"

CModel::CModel(){}

CModel::~CModel()
{
    // TODO free memory
}

/** @brief Load mbm file
  *
  * @fileName: mdm model file name we want to load and draw
  */
void CModel::init(char *fileName)
{
    CModelLoader   loader;
    CShaderLoader  shaders;

    loader.loadData(fileName);

    loader.createDataArray( m_dataArray,
                            m_dataArraySizes,
                            m_numObjects,
                            m_numElements,
                            m_textureIDs );

    glGenVertexArrays(1, &m_vertexArrayID);
    glBindVertexArray(m_vertexArrayID);

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    m_programID      = shaders.loadShaders("vertexshader.vert", "fragmentshader.frag");

    //m_matrixUniform  = glGetUniformLocation(m_programID, "MVP");
    //m_textureUniform = glGetUniformLocation(m_programID, "myTextureSampler");

    m_bufferIDs      = new unsigned int[m_numObjects];

    glGenBuffers(m_numObjects, m_bufferIDs);
    for(unsigned int i=0; i<m_numObjects; i++)
    {
        glBindBuffer(GL_ARRAY_BUFFER, m_bufferIDs[i]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(float)*m_dataArraySizes[i], m_dataArray[i], GL_STATIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }

}

void CModel::draw(glm::mat4 &MVP)
{
    glActiveTexture(GL_TEXTURE0);
 
    for(unsigned int i=0; i<m_numObjects; i++)
    {
        glUseProgram(m_programID);
 
        unsigned int matrixUniform  = glGetUniformLocation(m_programID, "MVP");
        unsigned int textureUniform = glGetUniformLocation(m_programID, "myTextureSampler");
 
        glUniformMatrix4fv(matrixUniform, 1, GL_FALSE, &MVP[0][0]);
 
        glBindTexture(GL_TEXTURE_2D, m_textureIDs[i]);
 
        glUniform1i(textureUniform, 0);
 
        glBindBuffer(GL_ARRAY_BUFFER, m_bufferIDs[i]);
        // vertices
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(
                0,                  // must match the layout in the shader.
                3,                  // size
                GL_FLOAT,           // type
                GL_FALSE,           // normalized?
                sizeof(float)*8,    // stride
                (void*)0            // array buffer offset
        );
 
        // UVs
        glEnableVertexAttribArray(1);
        glVertexAttribPointer(
                1,
                2,
                GL_FLOAT,
                GL_FALSE,
                sizeof(float)*8,
                (void*)6
        );
 
        glDrawArrays(GL_TRIANGLES, 0, m_numElements[i]);
 
        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);
 
        glUseProgram(0);
    }
 
    //glDisable(GL_TEXTURE_2D);
}

    glUseProgram(0);
    //glDisable(GL_TEXTURE_2D);
}


could you try to draw only the first object please, to do so just modify the for loop to for(unsigned int i=0; i<1; i++), see what if you get the texture right or not.

I tried, draw only the first, then I draw only the second. The texture was the same noisy image.

you have 2 objects, 2 VBOs, and 1 VAO. I’d suggest to create 2 VAOs rather than just 1

also could you remove these from the draw() function, and put them into init() function:

glUniform1i(textureUniform, 0);

    glBindBuffer(GL_ARRAY_BUFFER, m_bufferIDs[i]);
    // vertices
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(
            0,                  // must match the layout in the shader.
            3,                  // size
            GL_FLOAT,           // type
            GL_FALSE,           // normalized?
            sizeof(float)*8,    // stride
            (void*)0            // array buffer offset
    );

    // UVs
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(
            1,
            2,
            GL_FLOAT,
            GL_FALSE,
            sizeof(float)*8,
            (void*)6
    );

    glDrawArrays(GL_TRIANGLES, 0, m_numElements[i]);

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);

and use 2 VAOs in init(), register these 2 VBOs to the 2 VAOs respectively.

and in the draw() function, call glBindVertexArray() with proper VAO handle, before the glDrawArray command.

this is part of my program, hopefully it helps


        ///////////////////////////////
	// texture 1
	const char *texName ="marble.jpg";
	QImage timg = QGLWidget::convertToGLFormat(QImage(texName, "JPG"));
	glActiveTexture(GL_TEXTURE0);
	//GLuint tid;
	glGenTextures(1, &tid);
	glBindTexture(GL_TEXTURE_2D, tid);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, timg.width(), timg.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, timg.bits());
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	int loc = program.index("Tex1", UNIFORM);
	glUniform1i(loc, 0);
	///////////////////////////////
	// texture 2
	const char *texName2 = "pattern.jpg";
	QImage timg2 = QGLWidget::convertToGLFormat(QImage(texName2, "JPG"));
	glActiveTexture(GL_TEXTURE0);
	//GLuint tid2;
	glGenTextures(1, &tid2);
	glBindTexture(GL_TEXTURE_2D, tid2);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, timg2.width(), timg2.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, timg2.bits());
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	int loc2 = program.index("Tex2", UNIFORM);
	glUniform1i(loc2, 0);
	///////////////////////////////
	// generate vertices data
	tor = new torus(300, 300, 0.3, 1.0);	
	tor->setMaterials(program, vec3(0.3, 0.3, 0.3), vec3(0.9, 0.9, 0.9), vec3(0.8, 0.8, 0.8), 250.0);
	///////////////////////////////
	// create 3 VBOs
	vertexBufferObj VBOs[3];
	tor->setVBO(VBOs[0], VERTEX);
	tor->setVBO(VBOs[1], NORMAL);
	tor->setVBO(VBOs[2], TEXTURE);
	///////////////////////////////
	// create one vertex array object -- VAO, for 3 VBOs
	vertexArrayObj VAO;
	tor->registerVAO(VAO.handle());
	VAO.enable();
	VAO.enableGenericAttrib(program.index("VertexPosition", GENERIC));
	VAO.enableGenericAttrib(program.index("VertexNormal",   GENERIC));	
	VAO.enableGenericAttrib(program.index("VertexTexCoord", GENERIC));
	tor->setVAO(program, VBOs[0], VERTEX);
	tor->setVAO(program, VBOs[1], NORMAL);
	tor->setVAO(program, VBOs[2], TEXTURE);
	///////////////////////////////
	// generate a box
	bx = new box(8.0, 6.0, 16.0);
	bx->setMaterials(program, vec3(0.3, 0.3, 0.3), vec3(0.9, 0.9, 0.9), vec3(0.8, 0.8, 0.8), 250.0);
	vertexBufferObj boxVBOs[3];
	bx->setVBO(boxVBOs[0], VERTEX);
	bx->setVBO(boxVBOs[1], NORMAL);
	bx->setVBO(boxVBOs[2], TEXTURE);
	// vao for box
	vertexArrayObj boxVAO;
	bx->registerVAO(boxVAO.handle());
	boxVAO.enable();
	boxVAO.enableGenericAttrib(program.index("VertexPosition", GENERIC));
	boxVAO.enableGenericAttrib(program.index("VertexNormal",   GENERIC));	
	boxVAO.enableGenericAttrib(program.index("VertexTexCoord", GENERIC));
	bx->setVAO(program, boxVBOs[0], VERTEX);
	bx->setVAO(program, boxVBOs[1], NORMAL);
	bx->setVAO(program, boxVBOs[2], TEXTURE);

and this is what I get
[ATTACH=CONFIG]319[/ATTACH]

Thanks for the help. I’ve never used VAO so it is a bit confusing. I know what it is used for, but I don’t really understand what should I put in the init and put in the draw.
For example the glBindTexture, where does it go? And the uniforms:
glUniformMatrix4fv(matrixUniform, 1, GL_FALSE, &MVP[0][0]);
glUniform1i(textureUniform, 0);
Are these also going to the init()?

here is a tutorial about VAO which might help

http://programming4.us/multimedia/8300.aspx

if you are using 1 texture unit (such as GL_TEXTURE0), then before you draw an object you need to bind the appropriate texture, so glBindTexture will go in the draw();

you don’t need to call glUniform1i(textureUniform, 0) multiple times, so it’s better in init();

if MVP is changing every time, for example, the object is rotating then you need to update MVP, in this case glUniformMatrix4fv(matrixUniform, 1, GL_FALSE, &MVP[0][0]) will be in the draw(), otherwise put it in init()

I changed my code, I’m suing VAO and I put the glUniform1i in the init. The result is still the same. Maybe the problem is somewhere else? I’m suing SDL for handling the window and load images.
I’m going to sleep. Thanks for your help, I’ll be back tomorrow and continue to fixing this issue.
Here is the code as I changed based on your tips:


#include "global.h"
#include "CModel.h"
#include "CModelLoader.h"
#include "CShaderLoader.h"

CModel::CModel(){}

CModel::~CModel()
{
    // TODO free memory
}

/** @brief Load mbm file
  *
  * @fileName: mdm model file name we want to load and draw
  */
void CModel::init(char *fileName)
{
    CModelLoader   loader;
    CShaderLoader  shaders;

    loader.loadData(fileName);

    loader.createDataArray( m_dataArray,
                            m_dataArraySizes,
                            m_numObjects,
                            m_numElements,
                            m_textureIDs );

    m_programID      = shaders.loadShaders("vertexshader.vert", "fragmentshader.frag");

    m_textureUniform = glGetUniformLocation(m_programID, "myTextureSampler");

    m_bufferIDs      = new unsigned int[m_numObjects];
    m_VAOIDs         = new unsigned int[m_numObjects];

    glGenBuffers(m_numObjects,      m_bufferIDs);
    glGenVertexArrays(m_numObjects, m_VAOIDs);

    for(unsigned int i=0; i<m_numObjects; i++)
    {
        glBindBuffer(GL_ARRAY_BUFFER, m_bufferIDs[i]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(float)*m_dataArraySizes[i], m_dataArray[i], GL_STATIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

    }

    for(unsigned int i=0; i<m_numObjects; i++)
    {
        glBindVertexArray(m_VAOIDs[i]);
        glBindBuffer(GL_ARRAY_BUFFER, m_bufferIDs[i]);

        glBindBuffer(GL_ARRAY_BUFFER, m_bufferIDs[i]);

        glUniform1i(m_textureUniform, 0);

        // vertices
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(
                0,                  // must match the layout in the shader.
                3,                  // size
                GL_FLOAT,           // type
                GL_FALSE,           // normalized?
                sizeof(float)*8,    // stride
                (void*)0            // array buffer offset
        );

        // UVs
        glEnableVertexAttribArray(1);
        glVertexAttribPointer(
                1,
                2,
                GL_FLOAT,
                GL_FALSE,
                sizeof(float)*8,
                (void*)6
        );

        glBindVertexArray(0);
    }

}

/** @brief Drawing the mdm model
  *
  */
void CModel::draw(glm::mat4 &MVP)
{
    glActiveTexture(GL_TEXTURE0);

    for(unsigned int i=0; i<m_numObjects; i++)
    {
        glUseProgram(m_programID);

        unsigned int matrixUniform  = glGetUniformLocation(m_programID, "MVP");

        glBindVertexArray(m_VAOIDs[i]);

        glUniformMatrix4fv(matrixUniform, 1, GL_FALSE, &MVP[0][0]);

        glBindTexture(GL_TEXTURE_2D, m_textureIDs[i]);

        glDrawArrays(GL_TRIANGLES, 0, m_numElements[i]);

        //glDisableVertexAttribArray(0);
        //glDisableVertexAttribArray(1);

        //glBindBuffer(GL_ARRAY_BUFFER, 0);

        glBindVertexArray(0);

        glUseProgram(0);
    }

}