maja
maja

Reputation: 18064

OpenGL combine Textures

I'm programming a 2D-Game in OpenGL and I have to output a level which consists of 20x15 fields.

So I'm currently outputting a texture for each field which is quite slow (300 textures/frame).

But due to the reason that the level never changes, I wondered if it's possible to combine the textures to a big, single texture before the game-loop starts.

Then I would have to output only one texture with 4 Texture Coordinates (0/0)(0/1)(1/1)(1/0) and 4 glVertex2f() which specifies the position in the Window.

This is my current Code for each of the 300 fields:

glColor3f(1,1,1);
glBindTexture(GL_TEXTURE_2D,textur);
glBegin(GL_QUADS);
    glTexCoord2f(textArea.a.x,textArea.b.y);glVertex2f(display.a.x,display.a.y);
    glTexCoord2f(textArea.a.x,textArea.a.y);glVertex2f(display.a.x,display.b.y);
    glTexCoord2f(textArea.b.x,textArea.a.y);glVertex2f(display.b.x,display.b.y);
    glTexCoord2f(textArea.b.x,textArea.b.y);glVertex2f(display.b.x,display.a.y);
glEnd();

Note that I have the images for all possible field-types in one .tga-File. So I'm choosing the right one with glTexCoord2f().

The image-File with all Tiles is loaded into

GLuint textur;

So I bind the same texture for every field.

My target is to decrease CPU-time. Display-Lists didn't work because there is so many data to load in the Graphics Card, that, in the end, display-Lists were even slower.

I also wasn't able to use VBOs because I don't use extensions like GLUT.

So my idea was to generate a single texture which should be quite easy and effective.

I hope you can give me feedback how I can combine textures and if this method would be the easiest one to increase performance

EDIT: that are the OpenGl-Functions I use in my program:

When I start the program, I initialize the window:

glfwInit();                     
if( !glfwOpenWindow(windowSize.x,windowSize.y, 0, 0, 0, 0, 0, 0, GLFW_WINDOW ) ) 
{   glfwTerminate();           
    return;
}

And that's all what the game-loop does with OpenG:

int main()
{
    //INIT HERE (see code above)

    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);                                
    glAlphaFunc(GL_GREATER,0.1f);
    glEnable(GL_ALPHA_TEST);

    long loopStart;//measure loopcycle-time
    do{
        height = height > 0 ? height : 1;
        glViewport( 0, 0, width, height );              //set Origin
        glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );      //background-color
        glClear(GL_COLOR_BUFFER_BIT);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();                      
        glOrtho(0,windowSize.x,0,windowSize.y,0,128);   //2D-Mode
        glMatrixMode(GL_MODELVIEW);
        loopStart=clock();

        //(...) OUTPUT HERE (code see above)

        glfwSwapBuffers();                              //erzeugte Grafikdaten ausgeben

        printf("%4dms -> ",clock()-loopStart);
    }while(...);

    glDisable(GL_ALPHA_TEST);
    glDisable(GL_TEXTURE_2D);
    glfwTerminate();
}

Upvotes: 1

Views: 3114

Answers (2)

maja
maja

Reputation: 18064

I identified a huge time-killer now. The textures I was using were too large, and the resolution was very unefficient.

The main-texture which included the level sprites had a resolution of 2200x2200 Pixels. So the GPU increased the size to 4096x4096 and calculated it with a huge amount of data.

The image contains 10x10 different Level-Tiles which are outputed on the screen with a resolution of 50x50 pixels each.
So I saved the Tiles-File with a lower resolution (1020 x 1020 Pixels -> each tile=102x102px) and now I have a loop-cycle time of <=15ms.
This isn't perfect, but in comparison with my previous 30-60ms it was a huge progress.

Upvotes: 1

Soulfreezer
Soulfreezer

Reputation: 21

I see you're using GLFW. You can add GLEW and GLM and then you should use OpenGL 3.x or higher.

Here is a FULL example, how you can easily draw 2000 Textured Quads (With Alphablending) or more with FPS of 200 or more on a lost budget Laptop. It has only one little Texture, but it will work also with an 4096x4096 Texture Atlas. You get one HUGE Performance-Hit, if the Subtexture Size in the big texture EXACTLY matches the size of your Quad you draw! You should use 50x50 Pixels also in the Big-Texture! The following Deme-Code here also UPDATES ALL 2000 Quads each frame and send them to the GPU. If you will not have to update them each frame and put the Scroll-Coordinates to the Shader..you will gain performance again. If you need no blending...use Alpha-Tests..you will gain again more speed.

#define GLEW_STATIC
#include "glew.h"
#include "glfw.h"
#include "glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtx/transform.hpp"
#include <sstream>
#include <fstream>
#include <vector>

#define BUFFER_OFFSET(i) ((char *)NULL + (i))

std::ofstream logger("Log\\Ausgabe.txt", (std::ios::out | std::ios::app));

class Vertex
{
public:
    float x;
    float y;
    float z;
    float tx;
    float ty;
};

class Quad
{
public:
    float x;
    float y;
    float width;
    float height;
};

int getHighResTimeInMilliSeconds(bool bFirstRun);
GLuint buildShader();
void addQuadToLocalVerticeArray(Vertex * ptrVertexArrayLocal, Quad *quad, int *iQuadCounter);

int main()
{
    logger << "Start" << std::endl;

    if(!glfwInit())
        exit(EXIT_FAILURE);

    glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR,3);
    glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR,3);
    glfwOpenWindowHint(GLFW_OPENGL_FORWARD_COMPAT, 1);
    glfwOpenWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);

    if( !glfwOpenWindow(1366, 768,8,8,8,8,32,32,GLFW_FULLSCREEN) )
    {
        glfwTerminate();
        exit( EXIT_FAILURE );
    }

    if (glewInit() != GLEW_OK)
        exit( EXIT_FAILURE );

    //Init
    GLuint VertexArrayID;
    GLuint vertexbuffer;
    GLuint MatrixID;
    GLuint TextureID;
    GLuint Texture;

    GLuint programID = buildShader();

    //Texture in Video-Speicher erstellen
    GLFWimage img;
    int iResult = glfwReadImage("Graphics\\gfx.tga", &img, GLFW_NO_RESCALE_BIT);

    glEnable(GL_TEXTURE_2D);
    glGenTextures(1, &Texture);
    glBindTexture(GL_TEXTURE_2D, Texture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,32,32, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.Data);
    glfwFreeImage(&img);

    Vertex * ptrVertexArrayLocal = new Vertex[12000];

    glGenVertexArrays(1, &VertexArrayID);
    glBindVertexArray(VertexArrayID);
    glGenBuffers(1, &vertexbuffer);
    glBindBuffer(GL_ARRAY_BUFFER, VertexArrayID);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * 12000, NULL, GL_DYNAMIC_DRAW);

    glm::mat4 Projection = glm::ortho(0.0f, (float)1366,0.0f, (float)768, 0.0f, 100.0f);
    glm::mat4 Model      = glm::mat4(1.0f); 
    glm::mat4 MVP        = Projection * Model;

    glViewport( 0, 0, 1366, 768 ); 

    MatrixID = glGetUniformLocation(programID, "MVP");
    glEnable(GL_CULL_FACE);
    glEnable (GL_BLEND); 
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    TextureID  = glGetUniformLocation(programID, "myTextureSampler");

    glUseProgram(programID);

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

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, Texture);
    glUniform1i(TextureID, 0);

    int iQuadVerticeCounter=0;

    int iNumOfQuads = 2000;
    Quad * ptrQuads = new Quad[iNumOfQuads];

    //LOCAL VERTICES CHANGES EACH LOOP
    for (int i=0; i<iNumOfQuads; i++)
    {
        ptrQuads[i].width  = 32;
        ptrQuads[i].height = 32;
        ptrQuads[i].x      = (float)(rand() % (1334));
        ptrQuads[i].y      = (float)(rand() % (736));
    }

    int iCurrentTime=0;
    int iFPS=0;
    int iFrames=0;
    int iFrameCounterTimeStart=0;
    int running = GL_TRUE;

    bool bFirstRun=true;

    while( running )
    {
        iCurrentTime = getHighResTimeInMilliSeconds(bFirstRun);
        bFirstRun=false;

        //UPDATE ALL QUADS EACH FRAME!
        for (int i=0; i<iNumOfQuads; i++)
        {
            ptrQuads[i].width  = 32;
            ptrQuads[i].height = 32;
            ptrQuads[i].x      = ptrQuads[i].x;
            ptrQuads[i].y      = ptrQuads[i].y;
            addQuadToLocalVerticeArray(ptrVertexArrayLocal, &ptrQuads[i], &iQuadVerticeCounter);
        }

        //DO THE RENDERING
        glClear( GL_COLOR_BUFFER_BIT );

        glBindBuffer(GL_ARRAY_BUFFER, VertexArrayID);
        glBufferSubData(GL_ARRAY_BUFFER, 0,sizeof(Vertex) * iQuadVerticeCounter, ptrVertexArrayLocal);

        glEnableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
        glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,sizeof(Vertex),BUFFER_OFFSET(0));

        glEnableVertexAttribArray(1);
        glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
        glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,sizeof(Vertex),BUFFER_OFFSET(3*sizeof(GL_FLOAT)));

        glDrawArrays(GL_TRIANGLES, 0, iQuadVerticeCounter);

        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);

        iQuadVerticeCounter=0;

        glfwSwapBuffers();

        //END OF DOING THE RENDERING

        running = !glfwGetKey( GLFW_KEY_ESC ) &&glfwGetWindowParam( GLFW_OPENED );

        iFrames++;

        if (iCurrentTime >= iFrameCounterTimeStart + 1000.0f)
        {
            iFPS = (int)((iCurrentTime - iFrameCounterTimeStart) / 1000.0f * iFrames);
            iFrameCounterTimeStart = iCurrentTime;
            iFrames = 0;
            logger << "FPS: " << iFPS << std::endl;
        }
    }

    glfwTerminate();
    exit( EXIT_SUCCESS );
}

int getHighResTimeInMilliSeconds(bool bFirstRun)
{
    if (bFirstRun)
        glfwSetTime(0);

    return (int)((float)glfwGetTime()*1000.0f);
}

GLuint buildShader()
{
    //Hint: Shader in the TXT-File looks like this
    /*std::stringstream ssVertexShader;
    ssVertexShader << "#version 330 core"<< std::endl 
                   <<   "layout(location = 0) in vec3 vertexPosition_modelspace;"<< std::endl
                   <<   "layout(location = 1) in vec2 vertexUV;"<< std::endl
                   <<   "out vec2 UV;"<< std::endl
                   <<   "uniform mat4 MVP;"<< std::endl
                   <<   "void main(){"<< std::endl
                   <<   "vec4 v = vec4(vertexPosition_modelspace,1);"<< std::endl
                   <<   "gl_Position = MVP * v;"<< std::endl
                   <<   "UV = vertexUV;"<< std::endl
                   <<   "}"<< std::endl;*/


    std::string strVertexShaderCode;
    std::ifstream VertexShaderStream("Shader\\VertexShader.txt", std::ios::in);
    if(VertexShaderStream.is_open())
    {
        std::string Line = "";
        while(getline(VertexShaderStream, Line))
            strVertexShaderCode += "\n" + Line;

        VertexShaderStream.close();
    }

    //Hint: Shader in the TXT-File looks like this
    /*std::stringstream ssFragmentShader;
    ssFragmentShader << "#version 330 core\n"
                      "in vec2 UV;\n"
                      "out vec4 color;\n"
                      "uniform sampler2D myTextureSampler;\n"
                      "void main(){\n"
                      "color = texture( myTextureSampler, UV ).rgba;\n"
                      "}\n";*/


    std::string strFragmentShaderCode;
    std::ifstream FragmentShaderStream("Shader\\FragmentShader.txt", std::ios::in);
    if(FragmentShaderStream.is_open())
    {
        std::string Line = "";
        while(getline(FragmentShaderStream, Line))
            strFragmentShaderCode += "\n" + Line;

        FragmentShaderStream.close();
    }

    GLuint gluiVertexShaderId = glCreateShader(GL_VERTEX_SHADER);
    char const * VertexSourcePointer = strVertexShaderCode.c_str();
    glShaderSource(gluiVertexShaderId, 1, &VertexSourcePointer , NULL);
    glCompileShader(gluiVertexShaderId);

    GLint Result = GL_FALSE;
    int InfoLogLength;
    glGetShaderiv(gluiVertexShaderId, GL_COMPILE_STATUS, &Result);
    glGetShaderiv(gluiVertexShaderId, GL_INFO_LOG_LENGTH, &InfoLogLength);
    std::vector<char> VertexShaderErrorMessage(InfoLogLength);
    glGetShaderInfoLog(gluiVertexShaderId, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
    std::string strInfoLog = std::string(&VertexShaderErrorMessage[0]);

    GLuint gluiFragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
    char const * FragmentSourcePointer = strFragmentShaderCode.c_str();
    glShaderSource(gluiFragmentShaderId, 1, &FragmentSourcePointer , NULL);
    glCompileShader(gluiFragmentShaderId);

    Result = GL_FALSE;
    glGetShaderiv(gluiFragmentShaderId, GL_COMPILE_STATUS, &Result);
    glGetShaderiv(gluiFragmentShaderId, GL_INFO_LOG_LENGTH, &InfoLogLength);
    std::vector<char> FragmentShaderErrorMessage(InfoLogLength);
    glGetShaderInfoLog(gluiFragmentShaderId, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
    strInfoLog = std::string(&FragmentShaderErrorMessage[0]);

    GLuint gluiProgramId = glCreateProgram();
    glAttachShader(gluiProgramId, gluiVertexShaderId);
    glAttachShader(gluiProgramId, gluiFragmentShaderId);
    glLinkProgram(gluiProgramId);

    Result = GL_FALSE;
    glGetProgramiv(gluiProgramId, GL_LINK_STATUS, &Result);
    glGetProgramiv(gluiProgramId, GL_INFO_LOG_LENGTH, &InfoLogLength);
    std::vector<char> ProgramErrorMessage( std::max(InfoLogLength, int(1)) );
    glGetProgramInfoLog(gluiProgramId, InfoLogLength, NULL, &ProgramErrorMessage[0]);
    strInfoLog = std::string(&ProgramErrorMessage[0]);

    glDeleteShader(gluiVertexShaderId);
    glDeleteShader(gluiFragmentShaderId);
    return gluiProgramId;
}

void addQuadToLocalVerticeArray(Vertex * ptrVertexArrayLocal, Quad *quad, int *ptrQuadVerticeCounter)
{
    //Links oben
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].x  = quad->x;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].y  = quad->y;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].z  = 0.0f;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].tx = 0.0f;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].ty = 1.0f;

    ++(*ptrQuadVerticeCounter);

    //Links unten
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].x  = quad->x;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].y  = quad->y - quad->height;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].z  = 0.0f;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].tx = 0.0f;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].ty = 0.0f;

    ++(*ptrQuadVerticeCounter);

    //Rechts unten
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].x  = quad->x + quad->width;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].y  = quad->y - quad->height;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].z  = 0.0f;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].tx = 1.0f;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].ty = 0.0f;

    ++(*ptrQuadVerticeCounter);

    //Rechts unten
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].x  = quad->x + quad->width;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].y  = quad->y - quad->height;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].z  = 0.0f;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].tx = 1.0f;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].ty = 0.0f;

    ++(*ptrQuadVerticeCounter);

    //Rechts oben
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].x  = quad->x + quad->width;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].y  = quad->y;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].z  = 0.0f;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].tx = 1.0f;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].ty = 1.0f;

    ++(*ptrQuadVerticeCounter);

    //Links oben
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].x  = quad->x;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].y  = quad->y;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].z  = 0.0f;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].tx = 0.0f;
    ptrVertexArrayLocal[*ptrQuadVerticeCounter].ty = 1.0f;

    ++(*ptrQuadVerticeCounter);
}

Upvotes: 2

Related Questions