2011-11-22 57 views
1

我已经设法将Nehe's Cel-Shading渲染与我的模型加载器进行整合,并使用Toon遮罩和同时勾勒出它们的原始纹理。结果实际上是一个非常漂亮的模型纹理Cel Shading效果,但是它提高了程序的速度,即使屏幕上只有3个模型也是非常慢的...卡通着色器与纹理。这可以优化吗?

由于结果是有点被黑在一起,我在想,也许我正在执行一些额外的步骤或额外的渲染任务,可能是不需要的,并且正在减慢游戏速度? 你可能会发现一些不必要的东西吗?

两个MD2和3DS装载机曾呼吁建立一个InitToon()函数来加载着色器

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) 


} 

这是动画MD2模型图纸:

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);  
} 

而这是在3DS加载器中的drawToon函数

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(); 
} 

最后这是tex.Use()函数t加载BMP纹理,并以某种方式与Toon着色完美融合

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

} 

编辑---------------------------- ------------------------------------------------

谢谢大家的建议。遵循Kos的建议,我重新考虑了该函数以便不使用glBegin/End ....因此,我在将std :: vectors存储在std :: vectors中,然后使用glDrawArrays从vector中读取。 ....这在理论上意味着更快,但它给我一个比以前更低的帧率......这是否正确实施?

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); 
      } 


      normals.push_back(normal[0]); 
      normals.push_back(normal[1]); 
      normals.push_back(normal[2]); 

      MD2TexCoord* texCoord = texCoords + triangle->texCoords[j]; 
      textCoords.push_back(texCoord->texCoordX); 
      textCoords.push_back(texCoord->texCoordY); 

      vertices.push_back(pos[0]); 
      vertices.push_back(pos[1]); 
      vertices.push_back(pos[2]); 
     } 

    } 


    glEnableClientState(GL_NORMAL_ARRAY); 
    glEnableClientState(GL_TEXTURE_COORD_ARRAY); 
    glEnableClientState(GL_VERTEX_ARRAY); 

    glNormalPointer(GL_FLOAT, 0, &normals[0]); 
    glTexCoordPointer(2, GL_FLOAT, 0, &textCoords[0]); 
    glVertexPointer(3, GL_FLOAT, 0, &vertices[0]); 



    glDrawArrays(GL_TRIANGLES, 0, vertices.size()/3); 


    glDisableClientState(GL_VERTEX_ARRAY); // disable vertex arrays 
    glDisableClientState(GL_TEXTURE_COORD_ARRAY); 
    glDisableClientState(GL_NORMAL_ARRAY); 

    vertices.clear(); 
    textCoords.clear(); 
    normals.clear(); 
+2

您应该访问http://codereview.stackexchange.com。这个网站是针对特定的编程问题。 – Constantinius

回答

2

这是非常低效的。只需从你的代码的信息:

MD2Frame* frame1 = frames + frameIndex1; 
MD2Frame* frame2 = frames + frameIndex2; 

取决于MD2Frame如何工作无论是计算2关键帧CPU侧(非常非常慢)这两条线,或偏移2预先计算的关键帧(更可能)。令我担心的是,所有可用的信息都表明这是顶点动画,例如24fps会给你每秒动画数据每顶点数据字节数24 * num顶点*大小。你应该做的是使用骨骼和硬件为你的模型蒙皮。

编辑:注意到它实际上是指针算术,所以它是99%肯定它是预先计算的情况。

这里的低挂果实正在使用一个VBO,它将填充计算的顶点信息。进一步创建2个VBO,每帧一个,将它们绑定到顶点程序,然后在顶点程序中进行混合。或者,如果您打算构建对代码基础有所要求的任何东西,那么就用骨头来看看动画,而这种速度快得多,而且内存效率更高。

编辑:澄清,有顶点融合是有意义的使用情况,所以不知道更多关于你想要做什么我不能说超过:看看每顶点动画实际上是你的最佳解决方案问题。

+0

感谢您的意见。对于这个编程任务,我需要使用关键帧动画,MD2是最简单的格式之一。虽然没有香椿代码,但模型动画速度非常快! – Alex

+0

啊,你的模型有多少个顶点? – Ylisar

+0

小于2000,MD2格式有顶点限制,所以它们的聚合度很低......但并不是所有的模型都是动画的,3DS的都是静态的......动画方面并不是真的问题到目前为止,突出这种渲染效果正在放慢的事情因为我骑自行车穿过所有顶点两次我想象的多次... – Alex

2

看一看这里:

glBegin(GL_TRIANGLES); 
    for(int i = 0; i < numTriangles; i++) { 
     // ... 
      glNormal3f(normal[0], normal[1], normal[2]); 
     // ... 
      glVertex3f(pos[0], pos[1], pos[2]); 
     } 
    } 
glEnd(); 

这部分是非常低效的,因为你必须多次打电话给每个GPU的驱动程序和模型的每个顶点。正确的方法是使用一个绘图调用和顶点数组来发送它,或者 - 甚至更好 - 最好使用顶点缓冲对象将几何图形存储在GPU上。

NeHe教程包含有用的信息,但现在已经过时; glVertexglBegin和家人现在并不真正使用。

+0

这是内容丰富的,谢谢!另一张海报向我推荐了同样的东西!我将考虑用顶点缓冲对象来实现这个对象......有没有什么特别的错误突出了第二个函数(3DS模型)?由于帧速率似乎更低,当我只启用一个,而不是第一个(MD2)... – Alex

+0

我已设法重新执行该功能以删除glBegin/End即时绘图,并使用sdt重新完成它: :矢量容器和glDrawArrays ....但它被证明是更慢....你可以看看我在上面张贴的新函数他问题,看看我是否已经正确实现它?感谢您的亲切帮助! – Alex

+0

在我们继续之前,让我确定是否每个帧都不重新分配矢量?它们是否在渲染循环之外定义,以便可以重用内存缓冲区?也就是说,在注释掉所有'glDrawArrays()'之后,性能如何变化? *(这应该给我们一个指示,如果CPU或GPU是限制)* – Kos