与纹理的香椿着色器。这可以优化吗?

时间:2011-11-22 13:50:41

标签: c++ opengl shader

我设法将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()函数,它加载一个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

}

EDIT ---------------------------------------------- ------------------------------

谢谢大家的建议。按照Kos的建议,我重新考虑了函数,以便不使用glBegin / End ....所以我在计算位置后将顶点存储在std :: vectors中,然后使用glDrawArrays从向量中读取。 ....这在理论上意味着更快,但它给我的帧速率比之前低得多......这是否正确实现了?

    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 个答案:

答案 0 :(得分:2)

这非常低效。只需使用代码中提供的信息:

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

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

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

这里的低悬的果实是使用VBO,你用计算的顶点信息填充。更进一步创建2个VBO,每个框架一个,将它们绑定到顶点程序,然后在顶点程序中进行混合。或者,如果您要构建对此代码库有点要求的任何东西,那么请使用骨骼来研究动画,这样可以更快,更节省内存。

编辑:澄清,有顶点混合有用的用例,所以不知道你要做什么更多我不能多说:看看每个顶点动画是否真的是你问题的最佳解决方案

答案 1 :(得分: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();

这部分非常低效,因为你有几次调用模型的每个顶点的 / em>的GPU驱动程序。正确的方法是使用一个绘制调用和顶点数组发送它,或者 - 甚至更好 - 最好使用顶点缓冲对象在GPU上存储几何体。

NeHe教程包含有用的信息,但现在已经过时了; glVertexglBegin以及家人现在还没有真正使用过。