Toon Shader с текстурой.Можно ли это оптимизировать? - PullRequest
1 голос
/ 22 ноября 2011

Мне удалось интегрировать рендеринг 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);    
}

Принимая во внимание, что это функция drawToon в загрузчике 3DS

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 ---------------------------------------------- ------------------------------

Спасибо всем за совет. Следуя совету Коса, я пересмотрел функцию, чтобы не использовать 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 ]

2 голосов
/ 22 ноября 2011

Посмотрите здесь:

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

Эта часть ужасно неэффективна, потому что у вас есть несколько обращений к драйверу графического процессора для каждой вершины вашей модели .Правильный подход состоит в том, чтобы отправить все это одним вызовом отрисовки и массивами вершин, или, что еще лучше, предпочтительно сохранить геометрию в графическом процессоре, используя объект буфера вершин.

Учебные руководства NeHe содержат полезную информацию, ноустарел сейчас;glVertex и glBegin и семья на самом деле не используются в настоящее время.

2 голосов
/ 22 ноября 2011

Это ужасно неэффективно.Имея только информацию, доступную из вашего кода:

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

Эти 2 строки в зависимости от того, как работает MD2Frame, либо вычисляют 2 ключевых кадра ЦП (очень очень медленно), либо смещаются в 2 предварительно вычисленных ключевых кадра (более вероятно)).Меня беспокоит то, что вся доступная информация предполагает, что это вершина анимирована, что, например, при 24 кадрах в секунду даст вам 24 * числа вершин * размер байтов данных на каждую вершину данных в секунду анимации.То, что вы должны делать, это анимировать, используя кость и аппаратную оболочку вашей модели.

РЕДАКТИРОВАТЬ: заметил, что это на самом деле арифметика указателя, так что он на 99% уверен, что это предварительно вычисленный случай.

Низко висящий фрукт здесь использует VBO, который вы заполняете вычисленной информацией о вершине.Сделав еще один шаг, создайте 2 VBO, по одному для каждого кадра, свяжите их обоих с вершинной программой, а затем выполните смешивание в вершинной программе.Или, если вы собираетесь создавать что-то, что в некоторой степени требует этой основы кода, вместо этого посмотрите на анимацию с использованием костей, что чертовски намного быстрее и более эффективно использует память.

РЕДАКТИРОВАТЬ: УточнениеСуществуют случаи, когда смешивание вершин имеет смысл, поэтому, не зная больше о том, что вы пытаетесь сделать, я не могу сказать больше, чем: посмотрите, является ли анимация по вершинам лучшим решением для вашей проблемы.

...