Формирование массива вершин и индексов
Самой сложной задачей является правильное вычисление координат всех вершин треугольников и формирование массива индексов Tria, с помощью которого команда glDrawElements обходит массив Vert при задании треугольников. Функция Sphere реализует алгоритм последовательного обхода сначала всех сферических треугольников вокруг полюсов сферы, а затем обхода сферических четырехугольников, образованных пересечением параллелей и меридианов. В процессе обхода формируется массив вершин Vert. После этого обходы повторяются для того, чтобы заполнить массив индексов Tria. Северный и южный полюса обрабатываются индивидуально. Для осуществления обхода предварительно создаются константы: ¨ da — шаг изменения сферического угла a (широта), ¨ db — шаг изменения сферического угла b (долгота), ¨ af и bf — конечные значения углов. Для упрощения восприятия алгоритма следует учитывать следующие особенности, связанные с порядком обхода вершин: ¨ После обработки северного и южного полюсов, мы движемся вдоль первой широты (a=da) от востока к западу по невидимой части полусферы и возвращаемся назад по видимой ее части. Затем происходит переход на следующую широту (a += da) и цикл повторяется. ¨ Координаты вершин (x, z) представляют собой проекции точек на экваториальную плоскость, а координата y постоянна для каждой широты. ¨ При обработке одной секции кольца для двух треугольников формируется по три индекса. void Sphere(VERT *v, TRIA* t) { v[0].v = Point3D (0, gRad, 0); // Формирование массива вершин. Северный полюс v[0].n = Point3D (0, 1, 0); v[0].c = gClr2; UINT last = gnVert - 1; // Индекс последней вершины (на южном полюсе) v[last].v = Point3D (0, -gRad, 0); // Южный полюс v[last].n = Point3D (0, -1, 0); v[last].c = gnVert & 1? gClr2: gClr1; double da = PI / (gnRings + 2.), // Подготовка констант db = 2. * PI / gnSects, af = PI - da/2., bf = 2. * PI - db/2.; UINT n = 1; // Индекс вершины, следующей за северным полюсом
for (double a = da; a < af; a += da) // Цикл по широтам { double y = gRad * cos(a), // Координата y постоянна для всего кольца xz = gRad * sin(a); // Вспомогательная точка
for (double b = 0.; b < bf; n++, b += db) // Цикл по секциям (долгота) { // Координаты проекции в экваториальной плоскости double x = xz * sin(b), z = xz * cos(b); v[n].v = Point3D (x, y, z); // Вершина, нормаль и цвет v[n].n = Point3D (x / gRad, y / gRad, z / gRad); v[n].c = n & 1? gClr1: gClr2; } } //====== Формирование массива индексов. Треугольники вблизи полюсов for (n = 0; n < gnSects; n++) { t[n].i1 = 0; // Индекс общей вершины (северный полюс) t[n].i2 = n + 1; // Индекс текущей вершины t[n].i3 = n == gnSects - 1? 1: n + 2; // Замыкание
t[gnTria-gnSects+n].i1 = gnVert - 1; // Южный полюс t[gnTria-gnSects+n].i2 = gnVert - 2 - n; t[gnTria-gnSects+n].i3 = gnVert - 2 - ((1 + n) % gnSects); } int k = 1; // Треугольники разбиения колец. Вершина, следующая за полюсом n = gnSects; // Начинаем с кольца после треугольников вокруг полюса. gnSects – Количество секций for (UINT i = 0; i < gnRings; i++, k += gnSects) { for (UINT j = 0; j < gnSects; j++, n += 2) { t[n].i1 = k + j; // Индекс общей вершины t[n].i2 = k + gnSects + j; // Индекс текущей вершины t[n].i3 = k + gnSects + ((j + 1) % gnSects); // Замыкание
t[n + 1].i1 = t[n].i1; // То же для второго треугольника t[n + 1].i2 = t[n].i3; t[n + 1].i3 = k + ((j + 1) % gnSects); } } } Для завершения работы осталось дополнить программу стандартным набором процедур (OnSize, main), алгоритм функционирования которых вы уже изучили. Будьте внимательны при реализации недостающего кода. В обработчике OnSize вам надо определить viewport и параметры перспективного проецирования. Задание. Исследуйте функционирование программы, вводя различные значения глобальных регулировок. Попробуйте задать нечетное число секций. Объясните результат. В качестве упражнения введите возможность интерактивного управления степенью дискретизации сферы и исследуйте эффективность работы конвейера при ее увеличении. Добавьте реакции на события клавиатуры и позвольте пользователю изменять (как увеличивать, так и уменьшать) количество параллелей и меридианов. Изменения должны быть малыми (по единице) при малых значениях и большими (например, по 8) при значениях более 8. Выделите в отдельную функцию вычисление параметров, зависящих от gnRings и gnSects. Выводите количество треугольников в консольное окно. Доведите их количество до миллиона.
|