Все предыдущие шаги мы строили "стандартные" математические фигуры, треугольники, кубики, шарики. А что делать если у нас есть несколько точек и мы хотим построить кривую или поверхность по этим точкам. Сглаживающие поверхности и кривые называют сплайнами. Есть много способов задания сплайна, но сейчас мы рассмотрим только кривые Безье.
Кривую Безье можно построить и не пользуясь OpenGL, посмотрите MSDN на функцию PolyBezier, но этот подход нам не очень интересен ;).
Давайте посмотрим как такой сплайн построить с помощью OpenGL. Как обычно в OpenGL сначала мы устанавливаем и подготавливаем некие значения, а потом включаем режим их отображения. Для вычисления промежуточных точек кривой используется одномерный вычислитель (one-dimensional evaluator) (возможно, что перевод не совсем правильный, но я не математик и понятие не имею как корректно перевести это словосочетание на русски):
glMap1f(GL_MAP1_VERTEX_3, 0.0f, 1.0f, 3, 4, (const float*)m_Placeholders); glEnable(GL_MAP1_VERTEX_3);
Вот он этот вычислитель. Первый параметр - символическая константа, в нашем случае GL_MAP1_VERTEX_3, говорит о том, что контрольная точка будет задаваться тройкой вещественных чисел. Второй и третий аргументы определяют начальный и конечный интервал рассчитываемой кривой. Надо сказать, что это некие относительные единицы измерения и логику их работы можно понять поэкспериментировав с ними. Четвертый параметр задает "шаг", т.е. сколько чисел содержится в считываемой порции данных, в нашем случае мы читаем по 3 числа, т.к. одна контрольная точка описывается 3 координатами. Важно здесь только то, что эти числа должны располагаться в памяти последовательно. Пятый параметр задает количество опорных точек, ну и шестой - массив с точками.
Массив объявлен как член класса и заполняется в конструкторе:
CExampleView::CExampleView() { // TODO: add construction code here m_Placeholders[0][0] = -4.0f; m_Placeholders[0][1] = -4.0f; m_Placeholders[0][2] = 0.0f; m_Placeholders[1][0] = -2.0f; m_Placeholders[1][1] = 4.0f; m_Placeholders[1][2] = 0.0f; m_Placeholders[2][0] = 2.0f; m_Placeholders[2][1] = -4.0f; m_Placeholders[2][2] = 0.0f; m_Placeholders[3][0] = 4.0f; m_Placeholders[3][1] = 4.0f; m_Placeholders[3][2] = 0.0f; }
Теперь следом за glMap1f мы включаем этот одномерный вычислитель.
На этом мы закончили этап подготовки к рисованию кровой. Перейдем к её отображению:
void CExampleView::OnDraw(CDC* pDC) { CExampleDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3f(0.0f, 0.5f, 0.5f); glBegin(GL_LINE_STRIP); for(int i=0; i<30; i++) glEvalCoord1f((float)i/30.0f); glEnd(); SwapBuffers(pDC->m_hDC); }
При помощи специальной функции glEvalCoord1f мы как бы вычисляем координату Y задавая X (в данном случае правильнее будет их называть V и U) и рисуем точку. Поскольку мы производим эти вычисления внутри командных скобок glBegin(GL_LINE_STRIP) и glEnd(), то все точки у нас будут соединяться в прямые, отрисованные в режиме STRIP. Если вы забыли, что такое режим STRIP, то напоминаю, что это когда конечная точка одной прямой является первой для другой.
Теперь относительно второго и третьего аргумента команды glMap1f. Попробуйте вместо 1.0 поставить 2.0, вы увидите половину кривой. А теперь в цикле измените предел по i с 30 на 60. Опять видите полную кривую.
Вообще-то полигональные модели это не тривиальная область математики, поэтому нам, простым программерам, дано постичь эту штуку через руки, так что рекомендую поэкспериментировать с разными коэффициентами и просто посмотреть что получается.