Вычисление нормалей OpenGL (квадраты)

Моя проблема касается OpenGL и Normals, я понимаю математику, стоящую за ними, и у меня есть некоторый успех.

Функция, которую я прикрепил ниже, принимает чередующийся массив вершин и вычисляет нормали для каждых 4 вершин. Они представляют КВАДРЫ, имеющие одинаковые направления. Насколько я понимаю, эти 4 вершины должны иметь одну и ту же нормаль. Пока они смотрят в одну сторону.

У меня проблема в том, что мои QUADS рендерятся с диагональным градиентом, примерно так: Light Эффект — за исключением того, что тень находится посередине, а свет — по углам.

Я рисую свои QUAD последовательно. TopLeft, TopRight, BottomRight, BottomLeft и вершины, которые я использую для вычисления своих нормалей, это TopRight - TopLeft и BottomRight - TopLeft.

Надеюсь, кто-то увидит, в чем я ошибся, но я занимался этим часами, чтобы не победить.

Для записи я визуализирую куб и чайник рядом с моими объектами, чтобы проверить, работает ли мое освещение, поэтому я почти уверен, что проблем с положением источника света нет.

void CalculateNormals(point8 toCalc[], int toCalcLength)
{
    GLfloat N[3], U[3], V[3];//N will be our final calculated normal, U and V will be the subjects of cross-product
    float length;

for (int i = 0; i < toCalcLength; i+=4) //Starting with every first corner QUAD vertice
{
    U[0] = toCalc[i+1][5] - toCalc[i][5]; U[1] = toCalc[i+1][6] - toCalc[i][6]; U[2] = toCalc[i+1][7] - toCalc[i][7]; //Calculate Ux Uy Uz 
    V[0] = toCalc[i+3][5] - toCalc[i][5]; V[1] = toCalc[i+3][6] - toCalc[i][6]; V[2] = toCalc[i+3][7] - toCalc[i][7]; //Calculate Vx Vy Vz

    N[0] = (U[1]*V[2]) - (U[2] * V[1]);
    N[1] = (U[2]*V[0]) - (U[0] * V[2]);
    N[2] = (U[0]*V[1]) - (U[1] * V[0]);

    //Calculate length for normalising
    length = (float)sqrt((pow(N[0],2)) + (pow(N[1],2)) + (pow(N[2],2)));

    for (int a = 0; a < 3; a++)
    {
        N[a]/=length;
    }

    for (int j = 0; i < 4; i++)
    {
                    //Apply normals to QUAD vertices (3,4,5 index position of normals in interleaved array)
        toCalc[i+j][3] = N[0]; toCalc[i+j][4] = N[1]; toCalc[i+j][5] = N[2];
    }
}
}

person LBHoward    schedule 17.01.2012    source источник
comment
Это С++; Вы рассматривали возможность использования математической библиотеки, такой как GLM?   -  person R. Martinho Fernandes    schedule 17.01.2012
comment
К сожалению, это для задания, и поэтому нам не рекомендуется использовать библиотеки.   -  person LBHoward    schedule 17.01.2012


Ответы (3)


Похоже, что вы берете значения позиции вершины для использования в вычислениях из индексов 5, 6 и 7, а затем записываете нормали в индексах 3, 4 и 5. Обратите внимание, как индекс 5 используется в обоих случаях. Я предполагаю, что один из них не является правильным.

person R. Martinho Fernandes    schedule 17.01.2012
comment
Большое спасибо, у меня было ощущение, что это были небольшие проблемы, которые смотрели мне прямо в лицо. Крайний срок — четверг, поэтому я работал без остановки, чтобы сгладить окончательные проблемы. - person LBHoward; 17.01.2012

Похоже, ваши for-loops кусают вас.

for (int i = 0; i < toCalcLength; i+=4) //Starting with every first corner QUAD vertice
{
  ...
  for (int j = 0; i < 4; i++)
  { //            ^      ^
    // Should you be using 'j' instead of 'i' here?
    // j will never increment
    // This loop won't be called at all after the first time through the outer loop
    ...
  }
}
person JCooper    schedule 17.01.2012
comment
Хорошо подмечено, это решает огромную проблему. Спасибо! - person LBHoward; 17.01.2012

Вы используете индексы 3, 4 и 5 для хранения нормалей:

toCalc[i+j][3] = N[0]; toCalc[i+j][4] = N[1]; toCalc[i+j][5] = N[2];

И вы используете индексы 5, 6 и 7 для получения координат точки:

U[0] = toCalc[i+1][5] - toCalc[i][5]; U[1] = toCalc[i+1][6] - toCalc[i][6]; U[2] = toCalc[i+1][7] - toCalc[i][7];

Эти индексы перекрываются (normal.x имеет тот же индекс, что и position.z), чего не должно происходить.


Рекомендации:

  1. Разместите все в структурах.
  2. Either:
    1. Use math library.
    2. ИЛИ поместите векторную арифметику в отдельные подпрограммы с соответствующими именами.
  3. Используйте именованные переменные вместо индексов.

Тем самым вы уменьшите количество ошибок в своем коде. a.position.x легче читать, чем quad[0][5], и легче исправить опечатку в векторной операции, если код не был скопирован.


Вы можете использовать объединения для доступа к векторным компонентам как по индексу, так и по имени:

struct Vector3{
    union{
        struct{
            float x, y, z;
        };
        float v[3];
    };
};    

Для вычисления нормали в квадрате ABCD

A--B
|  |
C--D

Используйте формулу:

normal = normalize((B.position - A.position) X (C.position - A.position)).

OR

normal = normalize((D.position - A.position) X (C.position - B.position)).

Где «X» означает «перекрестный продукт».

В любом случае будет работать нормально.

person SigTerm    schedule 17.01.2012
comment
Спасибо за вклад, математика была на месте, но, как вы правильно заметили, мои нормали перекрывали мои вершины. - person LBHoward; 17.01.2012