Как создать сферу
Для того чтобы из существующей заготовки — икосаэдра из двадцати граней — создать сферу, круглую, блестящую и без изъянов, нужно осуществить предельный переход, как в мат. анализе, бесконечно увеличивая число треугольников при бесконечном уменьшении их размеров. В дискретном мире нет места предельным переходам, поэтому вместо бесконечного деления надо ограничиться каким-то конечным числом и начать делить каждый из двадцати треугольников икосаэдра на все более мелкие правильные треугольники. Вычисление нормали при этом упрощается, так как при приближении к сфере нормаль в каждой вершине треугольника приближается к нормали поверхности сферы. А последняя — равна нормированному вектору радиуса текущей точки. Алгоритм деления проиллюстрируем рисунком. Треугольник с вершинами V1, V2 и V3 разбивается на четыре треугольника: (V1,V12,V31), (V2,V23,V12), (V3,V32,V23) и (V12,V23,V31). После этого промежуточные точки деления надо посадить на поверхность сферы, то есть изменить их координаты так, чтобы концы векторов (V12, V23 и V31) дотянулись до поверхности сферы. Для этого достаточно нормировать векторы с помощью уже существующей процедуры Scale. Она впоследствии будет использована как для масштабирования нормали, так и для нормировки координат вершин новых треугольников. Но сейчас мы будем вычислять нормаль приближенно. Введем еще две вспомогательные функции. //=== Команды OpenGL для изображения одного треугольника void SetTria(double *v1, double *v2, double *v3) { glBegin(GL_TRIANGLES); glNormal3dv (v1); glVertex3dv (v1); // Нормаль и вершина задаются одним вектором glNormal3dv (v2); glVertex3dv (v2); glNormal3dv (v3); glVertex3dv (v3); glEnd(); } //====== Генерация внутренних треугольников void Split (double *v1, double *v2, double *v3) { double v12[3], v23[3], v31[3]; // Промежуточные вершины
for (int i = 0; i < 3; i++) { v12[i] = v1[i]+v2[i]; // Можно не делить пополам, так как будем нормировать v23[i] = v2[i]+v3[i]; v31[i] = v3[i]+v1[i]; } Scale(v12); Scale(v23); Scale(v31); // Нормируем три новые вершины SetTria(v1, v12, v31); SetTria(v2, v23, v12); // и рисуем четыре треугольника SetTria(v3, v31, v23); SetTria(v12,v23, v31); } Вставьте эти глобальные функции в файл и измените функцию DrawScene, в которой отсутствует вызов функции GetNorm для точного вычисления нормали, но есть вызов функции Split для каждой из 20 граней икосаэдра. В результате мы получаем фигуру из 80 треугольных граней, которая значительно ближе к сфере, чем икосаэдр. void DrawScene() { //=== Внимание. Здесь код подготовки массивов и начало создания списка //... glBegin (GL_TRIANGLES);
for (int i = 0; i < 20; i++) Split (v[id[i][0]], v[id[i][1]], v[id[i][2]]);
glEnd(); glEndList(); } Интересные и неожиданные результаты могут быть получены вследствие ошибок. Если ошибки происходят в условиях симметричного отражения, то возникают ситуации, сходные со случайными изменениями узоров в калейдоскопе. Замените на обратные знаки компонентов вектора в функции Scale. Это действие в предыдущих версиях программы было эквивалентно изменению знака нормали. Найдите строку, похожую на ту, что приведена ниже, и замените знаки так, как показано, на минусы. v[0] /= -d; v[1] /= -d; v[2] /= -d; Запустите и проверьте. Вы должны получить изображение, которое значительно интереснее скучной сферы.
|