1
\$\begingroup\$

This is the code that I use to setup my shader:

char* vertexShaderSource = getResource("vert.shad");
char* fragmentShaderSource = getResource("frag.shad");

vshad = readFile(vertexShaderSource);
fshad = readFile(fragmentShaderSource);

free(vertexShaderSource);
free(fragmentShaderSource);

GLuint getShaderProgram(const char* vshad, const char* fshad)
{
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);

    glShaderSource(vertexShader, 1, &vshad, NULL);
    glCompileShader(vertexShader);

    GLint success;
    GLchar infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);

    if(!success)
    {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        printf("ERROR VERTEX COMPILATION_FAILED %s\n",infoLog);
        SDL_Quit();
    }

    GLuint fragmentShader  = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fshad, NULL);
    glCompileShader(fragmentShader);

    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);

    if(!success)
    {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        printf("ERROR FRAGMENT COMPILATION_FAILED %s\n",infoLog);
        SDL_Quit();
    }

    GLuint shaderProgram = glCreateProgram();

    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if(!success) {
        glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
        printf("ERROR SHADER PROGRAM COMPILATION_FAILED %s\n",infoLog);
        SDL_Quit();
    }
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    return shaderProgram;
}

This code sets up a VAO to render a textured quad:

glGenVertexArrays(1, &vao);
    glGenBuffers(1, &verticesBuffer);
    glGenBuffers(1, &colorBuffer);
    glGenBuffers(1, &indicesBuffer);
    glGenBuffers(1, &texCoordBuffer);

    glBindVertexArray(vao); //bind vertex array buffer

    glBindBuffer(GL_ARRAY_BUFFER, verticesBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(r->vertices), r->vertices, GL_STATIC_DRAW);

    //bind n setup indices
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(r->indices), r->indices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind indices

    //bind n setup colors
    glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(r->colors), r->colors, GL_STATIC_DRAW);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(1);
    glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind colors

    //bind n setup texture coords
    glBindBuffer(GL_ARRAY_BUFFER, texCoordBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(r->texCoords), r->texCoords, GL_STATIC_DRAW);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(2);
    glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind texture coords

    glBindVertexArray(0); //unbind vertex array buffer

And this sets up my texture:

glGenTextures(1, &colorTextureID);
glBindTexture(GL_TEXTURE_2D, colorTextureID);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);


glTexImage2D(GL_TEXTURE_2D, 0, Mode1, colorSurface->w, colorSurface->h, 0, Mode1, GL_UNSIGNED_BYTE, colorSurface->pixels);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
free(colorSurface);

Finally, the rendering code:

void render()
{
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glUseProgram(sp);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, colorTextureID);//ourTexture
    ourTexture = getUniformLocation(sp, "ourTexture");
    glUniform1i(ourTexture, 0);

    glBindVertexArray(verticesBuffer);
    viewMat = getUniformLocation(sp, "viewMat");
    modelMat = getUniformLocation(sp, "modelMat");
    projMat = getUniformLocation(sp, "projMat");

    maxIterLoc = getUniformLocation(sp, "maxIterations");
    centerLoc = getUniformLocation(sp, "center");
    scaleLoc = getUniformLocation(sp, "scale");

    glUniformMatrix4fv(viewMat, 1, GL_FALSE, vm.m);
    glUniformMatrix4fv(projMat, 1, GL_FALSE, opm.m);
    glUniformMatrix4fv(modelMat, 1, GL_FALSE, tm.m);

    glUniform1i(maxIterLoc, maxIterations);
    glUniform1f(scaleLoc, scale);
    glUniform2f(centerLoc, cx, cy);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);



    SDL_GL_SwapWindow(window);
}

This whole setup is just to render a textured quad; basically a non-alpha-blended sprite.

I already put the code to create the shader program in a class of it's own but there's still all the other code of setting up the VAO, texture and the state setup before rendering.

How are these things structured in bigger systems? Imagine if I had to render a textured, normal mapped, shadow mapped 3D model. This code would get significantly longer.

This sample code below is how I was thinking of generalizing the rendering code. This way I can create a few different types of shaders without have so much clutter right in my main class.

    gluint shader_program = -1;
    glint vao, vBuff, iBuff, cBuff, tBuff;
    glint textures;

    shader_program create_sprite_shader()
    {
     glGenVertexArray(...)
     glGenBuffers(...)
     .... do all bind buffer and buffer data
    }

    void use_shader(shader_program s)
    {
     glUseProgram(s->program);
     glActivateTexture(gl_texture0);
     glBindTexture(sp->texture_type, sp->texture);

     get all uniform and attribute locations from shader..
     bind their values
    }

    render(mat44 model, mat44 view, mat44 proj)
    {
      //pass model view and projection matrices to the shader
    }

    unbind_shader()
    {
     glUseProgram(0);
    }

Then, in the main program:

shader_program s_shader;

init_main()
{
  s_shader = create_sprite_shader()
}

while(game_running)
{
  update_game() { .. };
  handle_input() { .. };

  render()
  {
   use_program(s_shader);
   for(0 ... 10)
   {
     s_program->render(sprite[i].modelMatrix, viewMatrix, projMatrix);
   }
   unbind_shader();
  }
}

clean_up() { //clean up resources }

I could have a toon_shader, a metal_shader, etc. Is that the way these parts of the code are built?

I've seem some reference works looking at Intel, Nvidia, etc., but those are usually laid out how my code is currently right now, which doesn't scale well.

\$\endgroup\$
3
\$\begingroup\$

I can give one small piece of advice. Don't do this in your render loop:

viewMat = getUniformLocation(sp, "viewMat");
modelMat = getUniformLocation(sp, "modelMat");
projMat = getUniformLocation(sp, "projMat");

maxIterLoc = getUniformLocation(sp, "maxIterations");
centerLoc = getUniformLocation(sp, "center");
scaleLoc = getUniformLocation(sp, "scale");

You should get the uniform locations only once, usually just after you've compiled your shader and linked it. Then save the locations and just write to them. Getting data from OpenGL tends to be very slow.

Or, as mentioned in the comments, don't even query them at all. In newer versions of OpenGL (3.0 and later, I think), you can specify uniform locations in the shader using layout specifications. Then you don't need to query what they are. It would look something like this:

layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec3 vNormal;
layout (location = 2) in vec3 vTangent;
layout (location = 3) in vec3 vBinormal;
layout (location = 4) in vec2 vTexCoord;
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Even better, don't query for these things. Specify them as a fixed part of the interface between the host application and the shader, and use layout(location=X) to ensure that the attributes are bound to the same location as the host code will use. \$\endgroup\$ Aug 13 '15 at 6:00
  • \$\begingroup\$ Yes! Excellent point. I'll update the answer. \$\endgroup\$ Aug 13 '15 at 13:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.