Multi-texture drawing with OpenGL?

165 views Asked by At

I am trying to draw with OpenGL some object with 2 or more different texture. for example 3 sprites from 3 different spritesheets. you can see here what I expected and what I have The problem I have now is that all sprites are from the 1 spritesheet. let have a look, first in the initialisation i load 3 images img0,img1,img2

img0 = loadSurface2D("image0.png");
img1 = loadSurface2D("image1.png");
img2 = loadSurface2D("image2.png");

then in my draw loop a little bit like SDL2:

BKP_Rec dest,src;                                                                                                                    
g2D_renderSurface(img0,NULL,NULL);//will draw all the sprite sheet a (0,0)

src.x = 0;
src.y = 4;
src.w = 64;
src.h = 64;
dest.x = 512;
dest.y = 300;
dest.w = 128;
dest.h = 128;
g2D_renderSurface(img1,&dest,&src);//draw sprite(0,4) 

src.x = 1;
src.y = 2;
dest.x = 562;
dest.y = 300;

g2D_renderSurface(img2,&dest,&src);//draw img 2 sprite(1,2) 

let have a look at loadSurface2D

Surface2D * loadSurface2D(const char * file)
{
    unsigned  int width, height;
    unsigned char * image = image_loadPNG(file,&width,&height);
    //...all test & flip image are done here, removed it for lisibility
    Surface2D * s = NULL;
    ALLOC_L(Surface2D, s, 1);

    s->data = image;

    s->point[0]  = 0; s->point[1] = 0 ; s->point[2] = 0; s->point[3] = 1;
    s->point[4]  = 0; s->point[5] = -nh; s->point[6] = 0; s->point[7] = 1;
    s->point[8]  = nw; s->point[9] = -nh ; s->point[10] = 0; s->point[11] = 1;
    s->point[12] = nw; s->point[13] = 0 ; s->point[14] = 0; s->point[15] = 1;

    s->texture[0]   = 0.0f; s->texture[1]  = 1.0f ;
    s->texture[2]   = 0.0f; s->texture[3]  = 0.0f;
    s->texture[4]   = 1.0f; s->texture[5]  = 0.0f ;
    s->texture[6]   = 1.0f; s->texture[7]  = 1.0f ;

    s->w = (GLfloat) width;
    s->h = (GLfloat) height;

    bkp_numcal_setIdentity4(s->Mtx_translate);
    bkp_numcal_setIdentity4(s->Mtx_scale);
    bkp_numcal_setIdentity4(s->Mtx_rotate);
    bkp_numcal_setIdentity4(s->Mtx_i);
    bkp_numcal_setIdentity4(s->Mtx);

    GLuint point_vbo = 0;
    glGenBuffers(1,&point_vbo);
    glBindBuffer(GL_ARRAY_BUFFER, point_vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(s->point),s->point,GL_STATIC_DRAW);

    GLuint tex_vbo = 1;
    glGenBuffers(1,&tex_vbo);
    glBindBuffer(GL_ARRAY_BUFFER, tex_vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(s->texture),s->texture,GL_STATIC_DRAW);

    GLuint vao = stc_2d->vao_num;
    stc_2d->vao_num += 1;
    glGenVertexArrays(1,&vao);
    glBindVertexArray(vao);

    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, point_vbo);
    glVertexAttribPointer(0,4,GL_FLOAT,GL_FALSE,0,NULL);

    GLuint dimension = 2;
    glEnableVertexAttribArray(1);
    glBindBuffer(GL_ARRAY_BUFFER, tex_vbo);
    glVertexAttribPointer(1,dimension,GL_FLOAT,GL_FALSE,0,NULL);

    s->sampler_loc = glGetUniformLocation(stc_2d->sh_sprite, "basic_texture");
    s->texunit_id = GL_TEXTURE0 + stc_2d->texunit_id;
    stc_2d->texunit_id += 1; // initialized at 0, every function call it increments

    GLuint tex = stc_2d->tex_num;
    ++stc_2d->tex_num;  // same here, another global at 0 when initialized
    glGenTextures(1, &tex);

    glBindTexture(GL_TEXTURE_2D, tex);
    glUseProgram(stc_2d->sprite); // EDITED
    glUniform1i(s->sampler_loc, s->texunit_id);

    glTexImage2D(GL_TEXTURE_2D, 0,
        GL_RGBA, width, height, 0, GL_RGBA,
        GL_UNSIGNED_BYTE, image);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    int st_offset_loc = glGetUniformLocation(stc_2d->sh_sprite, "vt_");
    glUniformMatrix2fv(st_offset_loc, 1,GL_FALSE, s->vt_);
    s->st_offset = st_offset_loc;

    s->vt_[0] = 0.0;
    s->vt_[1] = 0.0;
    s->vt_[2] = 0.0;
    s->vt_[3] = 0.0;

    s->vao = vao;
    s->texid = tex;
    s->tex_vbo = tex_vbo;
    s->point_vbo = point_vbo;
    return s;
}

Basically I just load the png create the texture. there is some values (stc_2D->*) initialized at 0 when starting that behave at counter ex: stc_2D->vba_num telling the current vba number to create.

now the function to render

void g2D_renderSurface(BKP_Surface2D * s,BKP_Rec * dest,BKP_Rec * src)
{
    glUseProgram(stc_2d->sh_sprite);
    glBindVertexArray(s->vao);

    glActiveTexture(s->texunit_id);
    glBindTexture(GL_TEXTURE_2D, s->texid);
    //glUniform1i(s->sampler_loc, s->texunit_id);

Let assume I didn't comment this line

    float xf,yf;

    if(dest != NULL)
    {
        xf = (dest->x / (float) stc_fb_width * 2 - 1) ;
        yf = - ((dest->y / (float) stc_fb_height * 2 - 1));
        bkp_numcal_setFreeScalMatrix4(s->Mtx_scale,dest->w /s->w, dest->h/ s->h, 1.0f);
    }
    else
    {
        xf =  - 1 ;
        yf =   1;
        bkp_numcal_setIdentity4(s->Mtx_scale);
    }

    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    if(src != NULL)
    {
        s->vt_[0] =(float)(src->x * src->w) / s->w;
        s->vt_[1] = (float) src->w / s->w;
        s->vt_[2] =(s->h - (float)((1 + src->y) * src->h) )/ s->h;
        s->vt_[3] = (float) src->h / s->h;
    }
    else
    {
        s->vt_[0] = 0.0;
        s->vt_[1] = (float) s->w / s->w;
        s->vt_[2] = 0.0;
        s->vt_[3] = (float) s->h / s->h;
    }

    glUniform4fv(s->st_offset,1,s->vt_);

    bkp_numcal_translate4(s->Mtx_translate, xf, yf, 0);
    bkp_numcal_multMat4Mat4(s->Mtx,s->Mtx_scale,s->Mtx_translate);
    glUniformMatrix4fv(stc_2d->mtx_location, 1,GL_FALSE, s->Mtx);
    return;
}

Now at the end the shaders.

vertex:

#version 410\n
in vec3 vertex_position;uniform mat4 matrix;
in vec2 vt_loc;
varying vec2 v_uv;
uniform vec4 vt_;
void main() 
{
    v_uv.x = (vt_loc.x * vt_[1]) + vt_[0];
    v_uv.y = (vt_loc.y * vt_[3]) + (vt_[2]);

    gl_Position = matrix * vec4(vertex_position, 1.0);
 }

fragment:

#version 410
out vec4 frag_colour;
uniform sampler2D basic_texture;
varying vec2 v_uv;
void main() 
{
    vec4 texel = texture(basic_texture, v_uv);
    frag_colour = texel; 
}

I think they is probably something I don't understand, as I don't see why it will draw 3 object the size i want, the position I want but with the only same texture. I was thinking I overwrite the same texture but it is the first texture that is shown not the last one loaded.

2

There are 2 answers

3
Rabbid76 On BEST ANSWER

glUniform* specifies the value of a uniform variable for the current program object.
glUseProgram installs a program object as part of current rendering state.

The last value which is set by glUniform* is stored. Remove all settings of uniform values from loadSurface2D. Do all settings of uniform values in g2D_renderSurface right after glUseProgram(stc_2d->sh_sprite);.

Futher note, the value which you have to assign to the texture sampler uniform basic_texture is, not the texture unit enumeration constant (not GL_TEXTURE0, GL_TEXTURE1, ...).
It has to be the number of the texture unit (0, 1, 2. ...).

This is wrong:

s->texunit_id = GL_TEXTURE0 + stc_2d->texunit_id;
glUniform1i(s->sampler_loc, s->texunit_id);

It has to be:

s->texunit_id = stc_2d->texunit_id;

.....

glUseProgram(stc_2d->sh_sprite);

glActiveTexture(s->texunit_id + GL_TEXTURE0); // GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2, ....
glBindTexture(GL_TEXTURE_2D, s->texid);
glUniform1i(s->sampler_loc, s->texunit_id);   // 0, 1, 2, ....
2
BDL On

Uniforms in a shader always store the last state they are set to. Since you change the active texture before drawing, you also have to update the sampler uniform (the line is already there, not sure why its a comment).

glActiveTexture(s->texunit_id);
glBindTexture(GL_TEXTURE_2D, s->texid);
glUniform1i(s->sampler_loc, s->texunit_id); //<-- This is important