Toon shader with Texture. Can this be optimized?

Alex
  • Toon shader with Texture. Can this be optimized? Alex

    I am quite new to OpenGL, I have managed after long trial and error to integrate Nehe's Cel-Shading rendering with my Model loaders, and have them drawn using the Toon shade and outline AND their original texture at the same time. The result is actually a very nice Cel Shading effect of the model texture, but it is havling the speed of the program, it's quite very slow even with just 3 models on screen...

    Since the result was kind of hacked together, I am thinking that maybe I am performing some extra steps or extra rendering tasks that maybe are not needed, and are slowing down the game? Something unnecessary that maybe you guys could spot?

    Both MD2 and 3DS loader have an InitToon() function called upon creation to load the shader

    initToon(){
    
        int i;                                                        // Looping Variable ( NEW )
        char Line[255];                                                // Storage For 255 Characters ( NEW )
        float shaderData[32][3];                                    // Storate For The 96 Shader Values ( NEW )
        FILE *In = fopen ("Shader.txt", "r");                        // Open The Shader File ( NEW )
    
        if (In)                                                        // Check To See If The File Opened ( NEW )
        {
            for (i = 0; i < 32; i++)                                // Loop Though The 32 Greyscale Values ( NEW )
            {
                if (feof (In))                                        // Check For The End Of The File ( NEW )
                    break;
    
                fgets (Line, 255, In);                                // Get The Current Line ( NEW )
    
                shaderData[i][0] = shaderData[i][1] = shaderData[i][2] = float(atof (Line)); // Copy Over The Value ( NEW )
            }
    
            fclose (In);                                            // Close The File ( NEW )
        }
    
        else
            return false;                                            // It Went Horribly Horribly Wrong ( NEW )
    
        glGenTextures (1, &shaderTexture[0]);                        // Get A Free Texture ID ( NEW )
    
        glBindTexture (GL_TEXTURE_1D, shaderTexture[0]);            // Bind This Texture. From Now On It Will Be 1D ( NEW )
    
        // For Crying Out Loud Don't Let OpenGL Use Bi/Trilinear Filtering! ( NEW )
        glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);    
        glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    
        glTexImage1D (GL_TEXTURE_1D, 0, GL_RGB, 32, 0, GL_RGB , GL_FLOAT, shaderData);    // Upload ( NEW )
    
    
    }
    

    This is the drawing for the animated MD2 model:

    void MD2Model::drawToon() {
        float        outlineWidth    = 3.0f;                                // Width Of The Lines ( NEW )
        float        outlineColor[3]    = { 0.0f, 0.0f, 0.0f };                // Color Of The Lines ( NEW )
    
    
    // ORIGINAL PART OF THE FUNCTION
    
    
        //Figure out the two frames between which we are interpolating
        int frameIndex1 = (int)(time * (endFrame - startFrame + 1)) + startFrame;
        if (frameIndex1 > endFrame) {
            frameIndex1 = startFrame;
        }
    
        int frameIndex2;
        if (frameIndex1 < endFrame) {
            frameIndex2 = frameIndex1 + 1;
        }
        else {
            frameIndex2 = startFrame;
        }
    
        MD2Frame* frame1 = frames + frameIndex1;
        MD2Frame* frame2 = frames + frameIndex2;
    
        //Figure out the fraction that we are between the two frames
        float frac =
            (time - (float)(frameIndex1 - startFrame) /
             (float)(endFrame - startFrame + 1)) * (endFrame - startFrame + 1);
    
    
    // I ADDED THESE FROM NEHE'S TUTORIAL FOR FIRST PASS (TOON SHADE)
    
        glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);                // Use The Good Calculations ( NEW )
        glEnable (GL_LINE_SMOOTH);
        // Cel-Shading Code //
        glEnable (GL_TEXTURE_1D);                                    // Enable 1D Texturing ( NEW )
        glBindTexture (GL_TEXTURE_1D, shaderTexture[0]);            // Bind Our Texture ( NEW )
    
        glColor3f (1.0f, 1.0f, 1.0f);                                // Set The Color Of The Model ( NEW )
    
    // ORIGINAL DRAWING CODE
    
        //Draw the model as an interpolation between the two frames
        glBegin(GL_TRIANGLES);
        for(int i = 0; i < numTriangles; i++) {
            MD2Triangle* triangle = triangles + i;
            for(int j = 0; j < 3; j++) {
                MD2Vertex* v1 = frame1->vertices + triangle->vertices[j];
                MD2Vertex* v2 = frame2->vertices + triangle->vertices[j];
                Vec3f pos = v1->pos * (1 - frac) + v2->pos * frac;
                Vec3f normal = v1->normal * (1 - frac) + v2->normal * frac;
                if (normal[0] == 0 && normal[1] == 0 && normal[2] == 0) {
                    normal = Vec3f(0, 0, 1);
                }
                glNormal3f(normal[0], normal[1], normal[2]);
    
                MD2TexCoord* texCoord = texCoords + triangle->texCoords[j];
                glTexCoord2f(texCoord->texCoordX, texCoord->texCoordY);
                glVertex3f(pos[0], pos[1], pos[2]);
            }
        }
        glEnd();
    
    // ADDED THESE FROM NEHE'S FOR SECOND PASS (OUTLINE)
    
        glDisable (GL_TEXTURE_1D);                                    // Disable 1D Textures ( NEW )
    
    
        glEnable (GL_BLEND);                                    // Enable Blending ( NEW )
            glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);        // Set The Blend Mode ( NEW )
    
            glPolygonMode (GL_BACK, GL_LINE);                        // Draw Backfacing Polygons As Wireframes ( NEW )
            glLineWidth (outlineWidth);                                // Set The Line Width ( NEW )
    
            glCullFace (GL_FRONT);                                    // Don't Draw Any Front-Facing Polygons ( NEW )
    
            glDepthFunc (GL_LEQUAL);                                // Change The Depth Mode ( NEW )
    
            glColor3fv (&outlineColor[0]);                            // Set The Outline Color ( NEW )
    
    
    // HERE I AM PARSING THE VERTICES AGAIN (NOT IN THE ORIGINAL FUNCTION) FOR THE OUTLINE AS PER NEHE'S TUT
    
            glBegin (GL_TRIANGLES);                                    // Tell OpenGL What We Want To Draw
            for(int i = 0; i < numTriangles; i++) {
            MD2Triangle* triangle = triangles + i;
            for(int j = 0; j < 3; j++) {
                MD2Vertex* v1 = frame1->vertices + triangle->vertices[j];
                MD2Vertex* v2 = frame2->vertices + triangle->vertices[j];
                Vec3f pos = v1->pos * (1 - frac) + v2->pos * frac;
                Vec3f normal = v1->normal * (1 - frac) + v2->normal * frac;
                if (normal[0] == 0 && normal[1] == 0 && normal[2] == 0) {
                    normal = Vec3f(0, 0, 1);
                }
                glNormal3f(normal[0], normal[1], normal[2]);
    
                MD2TexCoord* texCoord = texCoords + triangle->texCoords[j];
                glTexCoord2f(texCoord->texCoordX, texCoord->texCoordY);
                glVertex3f(pos[0], pos[1], pos[2]);
            }
        }
            glEnd ();                                                // Tell OpenGL We've Finished
    
            glDepthFunc (GL_LESS);                                    // Reset The Depth-Testing Mode ( NEW )
    
            glCullFace (GL_BACK);                                    // Reset The Face To Be Culled ( NEW )
    
            glPolygonMode (GL_BACK, GL_FILL);                        // Reset Back-Facing Polygon Drawing Mode ( NEW )
    
            glDisable (GL_BLEND);    
    }
    

    Whereas this is the drawToon function in the 3DS loader

    void Model_3DS::drawToon()
    {
    
        float        outlineWidth    = 3.0f;                                // Width Of The Lines ( NEW )
        float        outlineColor[3]    = { 0.0f, 0.0f, 0.0f };                // Color Of The Lines ( NEW )
    
    //ORIGINAL CODE
    
        if (visible)
        {
        glPushMatrix();
    
            // Move the model
            glTranslatef(pos.x, pos.y, pos.z);
    
            // Rotate the model
            glRotatef(rot.x, 1.0f, 0.0f, 0.0f);
            glRotatef(rot.y, 0.0f, 1.0f, 0.0f);
            glRotatef(rot.z, 0.0f, 0.0f, 1.0f);
    
            glScalef(scale, scale, scale);
    
            // Loop through the objects
            for (int i = 0; i < numObjects; i++)
            {
                // Enable texture coordiantes, normals, and vertices arrays
                if (Objects[i].textured)
                    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
                if (lit)
                    glEnableClientState(GL_NORMAL_ARRAY);
                glEnableClientState(GL_VERTEX_ARRAY);
    
                // Point them to the objects arrays
                if (Objects[i].textured)
                    glTexCoordPointer(2, GL_FLOAT, 0, Objects[i].TexCoords);
                if (lit)
                    glNormalPointer(GL_FLOAT, 0, Objects[i].Normals);
                glVertexPointer(3, GL_FLOAT, 0, Objects[i].Vertexes);
    
                // Loop through the faces as sorted by material and draw them
                for (int j = 0; j < Objects[i].numMatFaces; j ++)
                {
                    // Use the material's texture
                    Materials[Objects[i].MatFaces[j].MatIndex].tex.Use();
    
    
    // AFTER THE TEXTURE IS APPLIED I INSERT THE TOON FUNCTIONS HERE (FIRST PASS)
    
    
    
                        glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);                // Use The Good Calculations ( NEW )
                        glEnable (GL_LINE_SMOOTH);
                        // Cel-Shading Code //
                        glEnable (GL_TEXTURE_1D);                                    // Enable 1D Texturing ( NEW )
                        glBindTexture (GL_TEXTURE_1D, shaderTexture[0]);            // Bind Our Texture ( NEW )
    
                            glColor3f (1.0f, 1.0f, 1.0f);                                // Set The Color Of The Model ( NEW )
    
    
    
                    glPushMatrix();
    
                        // Move the model
                        glTranslatef(Objects[i].pos.x, Objects[i].pos.y, Objects[i].pos.z);
    
                        // Rotate the model
    
                        glRotatef(Objects[i].rot.z, 0.0f, 0.0f, 1.0f);
                        glRotatef(Objects[i].rot.y, 0.0f, 1.0f, 0.0f);
                        glRotatef(Objects[i].rot.x, 1.0f, 0.0f, 0.0f);
    
                        // Draw the faces using an index to the vertex array
                        glDrawElements(GL_TRIANGLES, Objects[i].MatFaces[j].numSubFaces, GL_UNSIGNED_SHORT, Objects[i].MatFaces[j].subFaces);
    
                    glPopMatrix();
                }
    
    
    
                    glDisable (GL_TEXTURE_1D);                                    // Disable 1D Textures ( NEW )
    
    
    // THIS IS AN ADDED SECOND PASS AT THE VERTICES FOR THE OUTLINE
    
    
        glEnable (GL_BLEND);                                    // Enable Blending ( NEW )
            glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);        // Set The Blend Mode ( NEW )
    
            glPolygonMode (GL_BACK, GL_LINE);                        // Draw Backfacing Polygons As Wireframes ( NEW )
            glLineWidth (outlineWidth);                                // Set The Line Width ( NEW )
    
            glCullFace (GL_FRONT);                                    // Don't Draw Any Front-Facing Polygons ( NEW )
    
            glDepthFunc (GL_LEQUAL);                                // Change The Depth Mode ( NEW )
    
            glColor3fv (&outlineColor[0]);                            // Set The Outline Color ( NEW )
    
            for (int j = 0; j < Objects[i].numMatFaces; j ++)
                {
            glPushMatrix();
    
                        // Move the model
                        glTranslatef(Objects[i].pos.x, Objects[i].pos.y, Objects[i].pos.z);
    
                        // Rotate the model
                                        glRotatef(Objects[i].rot.z, 0.0f, 0.0f, 1.0f);
                        glRotatef(Objects[i].rot.y, 0.0f, 1.0f, 0.0f);
                        glRotatef(Objects[i].rot.x, 1.0f, 0.0f, 0.0f);
    
                        // Draw the faces using an index to the vertex array
                        glDrawElements(GL_TRIANGLES, Objects[i].MatFaces[j].numSubFaces, GL_UNSIGNED_SHORT, Objects[i].MatFaces[j].subFaces);
    
                    glPopMatrix();
    
    
            }
    
                    glDepthFunc (GL_LESS);                                    // Reset The Depth-Testing Mode ( NEW )
    
            glCullFace (GL_BACK);                                    // Reset The Face To Be Culled ( NEW )
    
            glPolygonMode (GL_BACK, GL_FILL);                        // Reset Back-Facing Polygon Drawing Mode ( NEW )
    
            glDisable (GL_BLEND);
    
    glPopMatrix();
    }
    

    Finally this is the tex.Use() function that loads a BMP texture and somehow gets blended perfectly with the Toon shading

    void GLTexture::Use()
    {
        glEnable(GL_TEXTURE_2D);                                // Enable texture mapping
        glBindTexture(GL_TEXTURE_2D, texture[0]);                // Bind the texture as the current one
    
    }
    

  • You are using the old, deprecated immediate mode for drawing your polygons (glBegin/glVertex/glEnd calls). This causes a huge performance impact because you fill up the draw call budget pretty quickly, and multiple synchronous flush commands must be sent to the GPU to draw the triangles.

    Just use vertex buffer objects for drawing your models and you will achieve a good overall performance.

Tags
c++ opengl shaders animation post-processing
Related questions and answers
  • ]); } glEnd(); } // Use the original orientation. glDisable(GL_CULL_FACE); glPopAttrib(); glPopMatrix(); } void MD2::Interpolate(vec3_t* Vertices) { for(int i = 0; i... char index_normal; }; typedef short md2_textcoord_t[2]; struct md2_frame_t { float scale[3]; vec3_t translate; char name[16]; md2_vertex_t vertices[1]; // First vertex of this frame...I'm trying to load MD2 models (stolen from Cube) and my loader seems to be loading the models fine, but I can't say the same for the drawing of the model. Here's my code: typedef float vec3_t[3

  • at the same time: glBegin(GL_TRIANGLES); for(int i = 0; i &lt; numTriangles; i++) { MD2Triangle* triangle = triangles + i; for(int j = 0; j &lt; 3; j++) { MD2Vertex* v1...-&gt;texCoordY); glVertex3f(pos[0], pos[1], pos[2]); } } glEnd(); What I'd like to do is to calculate all positions before hand, store them in a Vertex array and then draw...]); glVertexPointer(3, GL_FLOAT, 0, &amp;vertices[0]); glDrawArrays(GL_TRIANGLES, 0, vertices.size()/3); glDisableClientState(GL_VERTEX_ARRAY); // disable vertex arrays glDisableClientState(GL_TEXTURE

  • (GL_LIGHT0); glEnable(GL_LIGHT1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3f(0.0f, 0.0f, 0.0f); int vertexIndex = 0, normalIndex; glRotatef(90, 0, 1, 0); glPushMatrix(); for(int... is identified, so that's all good. I'm writing the game in C++ with OpenGl/GLFW The drawing function is: int win_width; int win_height; glfwGetWindowSize(&amp;win_width, &amp;win_height); float win...++) { glBegin(GL_POLYGON); for(int c = 0; c &lt; (int)groups[a].faces[b].vertices.size(); c++) { vertexIndex = groups[a].faces[b].vertices[c]; glVertex3d

  • , temporarySurface-&gt;h, 0, GL_RGBA, GL_UNSIGNED_BYTE, temporarySurface-&gt;pixels); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); ...and the drawing code itself. glBindTexture(GL_TEXTURE_2D,thisIsMyTexture); glBegin( GL_QUADS ); glTexCoord2f(0.0f, 0.0f); glVertex3f(0.0f, 0.0f, 1.0f); glTexCoord2f(1.0f...); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glLoadIdentity(); ...here is my image loading function... SDL_Surface *loadImage (const char *filename, bool alpha = true) { SDL_Surface

  • ); glEnable(GL_BLEND); // enable read-only depth buffer glDepthMask(GL_FALSE); // set the blend function to what we use for transparency glBlendFunc(GL_SRC_ALPHA, GL_ONE); // set back to normal depth..., 1.0f); glVertex3f(terreno[x+1][z+1][0], terreno[x+1][z+1][1], terreno[x+1][z+1][2]); } glEnd(); } } I can move the camera with the functions: void keyboard (unsigned char key, int...i wanna walk to terrain... first i created the terrain void desenha_terreno(float px, float pz){ for (int z = 0; z &lt; iwidth-1; z++) { glBegin(GL_TRIANGLE_STRIP); for (int x = 0; x &lt

  • ); glDrawArrays(GL_TRIANGLES, 0, 6); glBindTexture(GL_TEXTURE_2D, 0); } Tried everything I can think of or find in a FBO tutorial or have read about. I don't get any errors and it returns as complete... is the init code for the FBO and it's texture: glGenTextures(1,&amp;fboimg); glBindTexture(GL_TEXTURE_2D,fboimg); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf...(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glBindTexture(GL_TEXTURE_2D, 0); glGenFramebuffers(1,&amp;fboHandle); glBindFramebuffer(GL_FRAMEBUFFER,fboHandle

  • _TRIANGLES, m_nNumIndices, GL_UNSIGNED_INT, ((char*)NULL + 0) ); glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); glDisableClientState( GL_NORMAL_ARRAY... in the shader code) for( int n = 0; n &lt; vShaderArgs.size(); n ++) glBindAttribLocation( m_nProgramId, n, vShaderArgs[n].sFieldName.c_str() ); // Create and bind to a vertex array object, which... onscreen, in the shape of the terrain rectangle, but there are no regular lines etc. Here's the code I use for rendering: void ShaderProgram::Draw() { using namespace AntiMatter; if( ! m

  • (zbd)); zbd.Width = SCREEN_WIDTH; // set the width of the depth buffer zbd.Height = SCREEN_HEIGHT; // set the height of the depth buffer zbd.ArraySize = 1; // we are only creating one texture... memcpy(pVoid, OurVertices, sizeof(OurVertices)); // copy the vertices to the buffer pBuffer-&gt;Unmap(); // create the index buffer out of DWORDs DWORD OurIndices[] = { 0, 1, 2, // side 1...(&amp;dsvd, sizeof(dsvd)); dsvd.Format = DXGI_FORMAT_D32_FLOAT; // one 32-bit float per pixel dsvd.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2D; // depth buffer is a 2D texture device-&gt

  • wrong in my frame buffer set up code, or elsewhere. But I can't see what. The FBO is set up through the following function: unsigned int fbo_id; unsigned int depth_buffer; int m_FBOWidth, m_FBOHeight; unsigned int m_TextureID; void initFBO() { m_FBOWidth = screen_width; m_FBOHeight = screen_height; glGenRenderbuffers(1, &amp;depth_buffer); glBindRenderbuffer(GL_RENDERBUFFER...); glBindFramebuffer(GL_FRAMEBUFFER, 0); } Here is my drawing box code, which just takes a transformation matrix and calls the appropriate functions. The current values of P is a projection matrix

Data information