Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Мультимедия, OpenGL/DirectX > OpenGL/Вычисление нормалей


Автор: Succorso 5.8.2006, 22:12
Проблема с вычислениями нормалей в OpenGL.
Задача состоит в вычислении нормалей для вершин только один раз и в занисении полученных данных в массив. Затем при каждой новой отрисовке модели мы будем брать данные о нормали для каждой вершины уже из полученного массива, а не вычислять их каждый раз при перересовке модели.

Код реализации данной задачи:
Код

// init == true : идёт инициализация
// init == false : инициализация уже была, и нормали не высчитываем
if(init)
{
    for(int j = 0; j < qualTr; j++)
    {
        u = detour[j * 3] - 1;        // первая вершина
        i = detour[j * 3 + 1] - 1;    // вторая вершина
        o = detour[j * 3 + 2] - 1;  // третья вершина

        // вычисление вектора перпендикулярного грани треугольника
        float r1[ 3 ], r2[ 3 ];    
        for (int k = 0; k < 3; k++)
        {
            r1[ k ] = locvec[i * 3 + k + 3] - locvec[u * 3 + k + 3];
            r2[ k ] = locvec[o * 3 + k + 3] - locvec[u * 3 + k + 3];
        }
        
        // векторное произведение 2-x векторов
        // x, y, z - координаты нормали
        x = r1[ 1 ] * r2[ 2 ] - r1[ 2 ] * r2[ 1 ];
        y = r1[ 2 ] * r2[ 0 ] - r1[ 0 ] * r2[ 2 ];
        z = r1[ 0 ] * r2[ 1 ] - r1[ 1 ] * r2[ 0 ];
        
        // нормализуем нормаль - т.е. приводим к единичному вектору 
        d = sqrt( x * x + y * y + z * z );
        if(d == 0)
            d = 1;                    
        x /= d;
        y /= d; 
        z /= d;

        // заносим вычисленные нормали в массив
        for(int k = 0; k < 3; k++)
            mechanism->setNormal(numpart, numgener, k, 0);

        mechanism->setNormal(numpart, numgener, u * 3 + 3, x);
        mechanism->setNormal(numpart, numgener, u * 3 + 1 + 3, y);
        mechanism->setNormal(numpart, numgener, u * 3 + 2 + 3, z);

        mechanism->setNormal(numpart, numgener, i * 3 + 3, x);
        mechanism->setNormal(numpart, numgener, i * 3 + 1 + 3, y);
        mechanism->setNormal(numpart, numgener, i * 3 + 2 + 3, z);

        mechanism->setNormal(numpart, numgener, o * 3 + 3, x);
        mechanism->setNormal(numpart, numgener, o * 3 + 1 + 3, y);
        mechanism->setNormal(numpart, numgener, o * 3 + 2 + 3, z);
    }
}
// отрисовываем модель
for(int j = 0; j < qualTr; j++)
{        
    u = detour[j * 3] - 1;
    i = detour[j * 3 + 1] - 1;
    o = detour[j * 3 + 2] - 1;

    glBegin( GL_TRIANGLES );
        // достаём нормаль для 1-ой вершины
        glNormal3f(locnorm[u * 3 + 3], locnorm[u * 3 + 3 + 1], locnorm[u * 3 + 3 + 2]);
        
        float v[ 3 ];
        for( int k = 0; k < 3; k++ )
            v[ k ] = locvec[u * 3 + k + 3];
        glVertex3fv( v );

        // достаём нормаль для 2-ой вершины
        glNormal3f(locnorm[i*3 +3], locnorm[i * 3 + 3 + 1], locnorm[i * 3 + 3 + 2]);
        for( int k = 0; k < 3; k++ )
            v[ k ] = locvec[i * 3 + k + 3];
        glVertex3fv( v );

        // достаём нормаль для 3-ей вершины
        glNormal3f(locnorm[o*3 +3], locnorm[o * 3 + 3 + 1], locnorm[o * 3 + 3 + 2]);
        for( int k = 0; k < 3; k++ )
            v[ k ] = locvec[o * 3 + k + 3];
        glVertex3fv( v );
    glEnd();
}

И полученное изображение получается изломанным:
http://imageshack.us
Теперь же если заставить программу во время каждой отрисовки модели вычислять нормаль (комментируем несколько строк) для треугольника, который мы рисуем в текущий момент, то получаем "по красивее" изображение (падение FPS в 15 раз, т.к. нормали вычисляются каждый раз).
Код:
Код

// init == true : идёт инициализация
// init == false : инициализация уже была, и нормали не высчитываем
//if(init)
//{
    for(int j = 0; j < qualTr; j++)
    {
        u = detour[j * 3] - 1;        // первая вершина
        i = detour[j * 3 + 1] - 1;    // вторая вершина
        o = detour[j * 3 + 2] - 1;  // третья вершина

        // вычисление вектора перпендикулярного грани треугольника
        float r1[ 3 ], r2[ 3 ];    
        for (int k = 0; k < 3; k++)
        {
            r1[ k ] = locvec[i * 3 + k + 3] - locvec[u * 3 + k + 3];
            r2[ k ] = locvec[o * 3 + k + 3] - locvec[u * 3 + k + 3];
        }
        
        // векторное произведение 2-x векторов
        // x, y, z - координаты нормали
        x = r1[ 1 ] * r2[ 2 ] - r1[ 2 ] * r2[ 1 ];
        y = r1[ 2 ] * r2[ 0 ] - r1[ 0 ] * r2[ 2 ];
        z = r1[ 0 ] * r2[ 1 ] - r1[ 1 ] * r2[ 0 ];
        
        // нормализуем нормаль - т.е. приводим к единичному вектору 
        d = sqrt( x * x + y * y + z * z );
        if(d == 0)
            d = 1;                    
        x /= d;
        y /= d; 
        z /= d;

        // заносим вычисленные нормали в массив
        for(int k = 0; k < 3; k++)
            mechanism->setNormal(numpart, numgener, k, 0);

        mechanism->setNormal(numpart, numgener, u * 3 + 3, x);
        mechanism->setNormal(numpart, numgener, u * 3 + 1 + 3, y);
        mechanism->setNormal(numpart, numgener, u * 3 + 2 + 3, z);

        mechanism->setNormal(numpart, numgener, i * 3 + 3, x);
        mechanism->setNormal(numpart, numgener, i * 3 + 1 + 3, y);
        mechanism->setNormal(numpart, numgener, i * 3 + 2 + 3, z);

        mechanism->setNormal(numpart, numgener, o * 3 + 3, x);
        mechanism->setNormal(numpart, numgener, o * 3 + 1 + 3, y);
        mechanism->setNormal(numpart, numgener, o * 3 + 2 + 3, z);
    //}
//}
// отрисовываем модель
//for(int j = 0; j < qualTr; j++)
//{        
    u = detour[j * 3] - 1;
    i = detour[j * 3 + 1] - 1;
    o = detour[j * 3 + 2] - 1;

    glBegin( GL_TRIANGLES );
        // достаём нормаль для 1-ой вершины
        glNormal3f(locnorm[u * 3 + 3], locnorm[u * 3 + 3 + 1], locnorm[u * 3 + 3 + 2]);
        
        float v[ 3 ];
        for( int k = 0; k < 3; k++ )
            v[ k ] = locvec[u * 3 + k + 3];
        glVertex3fv( v );

        // достаём нормаль для 2-ой вершины
        glNormal3f(locnorm[i*3 +3], locnorm[i * 3 + 3 + 1], locnorm[i * 3 + 3 + 2]);
        for( int k = 0; k < 3; k++ )
            v[ k ] = locvec[i * 3 + k + 3];
        glVertex3fv( v );

        // достаём нормаль для 3-ей вершины
        glNormal3f(locnorm[o*3 +3], locnorm[o * 3 + 3 + 1], locnorm[o * 3 + 3 + 2]);
        for( int k = 0; k < 3; k++ )
            v[ k ] = locvec[o * 3 + k + 3];
        glVertex3fv( v );
    glEnd();
}

Изображение теперь выглядит так:
http://imageshack.us

Теперь же решил дальше поэкспериментировать и заставить программу во время каждой отрисовки вычислить вначале все нормали для каждой вершины, а уже только потом отрисовать треугольники (падение FPS в 15 раз, т.к. нормали вычисляются каждый раз):
Код

// init == true : идёт инициализация
// init == false : инициализация уже была, и нормали не высчитываем
//if(init)
//{
    for(int j = 0; j < qualTr; j++)
    {
        u = detour[j * 3] - 1;        // первая вершина
        i = detour[j * 3 + 1] - 1;    // вторая вершина
        o = detour[j * 3 + 2] - 1;  // третья вершина

        // вычисление вектора перпендикулярного грани треугольника
        float r1[ 3 ], r2[ 3 ];    
        for (int k = 0; k < 3; k++)
        {
            r1[ k ] = locvec[i * 3 + k + 3] - locvec[u * 3 + k + 3];
            r2[ k ] = locvec[o * 3 + k + 3] - locvec[u * 3 + k + 3];
        }
        
        // векторное произведение 2-x векторов
        // x, y, z - координаты нормали
        x = r1[ 1 ] * r2[ 2 ] - r1[ 2 ] * r2[ 1 ];
        y = r1[ 2 ] * r2[ 0 ] - r1[ 0 ] * r2[ 2 ];
        z = r1[ 0 ] * r2[ 1 ] - r1[ 1 ] * r2[ 0 ];
        
        // нормализуем нормаль - т.е. приводим к единичному вектору 
        d = sqrt( x * x + y * y + z * z );
        if(d == 0)
            d = 1;                    
        x /= d;
        y /= d; 
        z /= d;

        // заносим вычисленные нормали в массив
        for(int k = 0; k < 3; k++)
            mechanism->setNormal(numpart, numgener, k, 0);

        mechanism->setNormal(numpart, numgener, u * 3 + 3, x);
        mechanism->setNormal(numpart, numgener, u * 3 + 1 + 3, y);
        mechanism->setNormal(numpart, numgener, u * 3 + 2 + 3, z);

        mechanism->setNormal(numpart, numgener, i * 3 + 3, x);
        mechanism->setNormal(numpart, numgener, i * 3 + 1 + 3, y);
        mechanism->setNormal(numpart, numgener, i * 3 + 2 + 3, z);

        mechanism->setNormal(numpart, numgener, o * 3 + 3, x);
        mechanism->setNormal(numpart, numgener, o * 3 + 1 + 3, y);
        mechanism->setNormal(numpart, numgener, o * 3 + 2 + 3, z);
    }
//}
// отрисовываем модель
for(int j = 0; j < qualTr; j++)
{        
    u = detour[j * 3] - 1;
    i = detour[j * 3 + 1] - 1;
    o = detour[j * 3 + 2] - 1;

    glBegin( GL_TRIANGLES );
        // достаём нормаль для 1-ой вершины
        glNormal3f(locnorm[u * 3 + 3], locnorm[u * 3 + 3 + 1], locnorm[u * 3 + 3 + 2]);
        
        float v[ 3 ];
        for( int k = 0; k < 3; k++ )
            v[ k ] = locvec[u * 3 + k + 3];
        glVertex3fv( v );

        // достаём нормаль для 2-ой вершины
        glNormal3f(locnorm[i*3 +3], locnorm[i * 3 + 3 + 1], locnorm[i * 3 + 3 + 2]);
        for( int k = 0; k < 3; k++ )
            v[ k ] = locvec[i * 3 + k + 3];
        glVertex3fv( v );

        // достаём нормаль для 3-ей вершины
        glNormal3f(locnorm[o*3 +3], locnorm[o * 3 + 3 + 1], locnorm[o * 3 + 3 + 2]);
        for( int k = 0; k < 3; k++ )
            v[ k ] = locvec[o * 3 + k + 3];
        glVertex3fv( v );
    glEnd();
}

И изображение как в первом варианте с "ломаным" светом.

Чертовщина какая-то: ведь во всех трёх случаях данные о нормалях одинаковые, просто каждый раз по-разному идёт отрисовка модели, но и изображение каждый раз разное.

Помогите кто уже сталкивался с вычислениями нормалей, м.б. уже были подобные трудности. Если нет, то прошу просто помочь разобраться, проект уже третий день стоит из-за этой проблемы.

Автор: Succorso 6.8.2006, 13:15
После обсуждения проблемы на gamedev (http://www.gamedev.ru/code/forum/?id=52427) стало ясно, что ошибка закралась в части хранения данных, а не в алгоритме вычисления нормалей. Топик можно закрывать.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)