Шаг 40 - Кривые Безье

Все предыдущие шаги мы строили "стандартные" математические фигуры, треугольники, кубики, шарики. А что делать если у нас есть несколько точек и мы хотим построить кривую или поверхность по этим точкам. Сглаживающие поверхности и кривые называют сплайнами. Есть много способов задания сплайна, но сейчас мы рассмотрим только кривые Безье.

Кривую Безье можно построить и не пользуясь 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. Опять видите полную кривую.

Вообще-то полигональные модели это не тривиальная область математики, поэтому нам, простым программерам, дано постичь эту штуку через руки, так что рекомендую поэкспериментировать с разными коэффициентами и просто посмотреть что получается.


Загрузить проект | Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Kirill V. Ratkin.