Просто об электротехнике, электронике, математике, физике
Просто об электротехнике, электронике, математике, физике
Категория: Программа "Живая математика"

Отсечение невидимых граней при построении объемных объектов в “Живой математике”


Здравствуйте, уважаемые мои читатели! Хочу поделиться с вами тем, чему научилась сама, а именно – как добиться, чтобы при построении объемных моделей в “Живой математике” (или “Живой геометрии”, как ее еще называют), грани, которые экранируются объектом, не прорисовывались бы. То есть, если модель повернута  гранью к нам, то видно было бы элементы, принадлежащие этой грани (отрезки и точки), но при повороте объекта и экранировании самим объектом этой грани (то есть грань – позади) – эти отрезки и точки исчезали бы.

Во-первых, необходимо, очевидно, научиться строить сами объемные объекты, если вы этого еще не умеете, то вам сюда (призмы и пирамиды), если вам нужен именно параллелепипед – то сюда.

Алгоритм, о котором я хочу рассказать, называется алгоритмом Робертса. Хочу сразу заметить, что в машинной графике я – чайник, не моя это область. Просто удалось разобрать простейшую задачу, которая изначально не была для меня простейшей, и для ее решения потребовалась неделя. Поэтому решила сберечь неделю времени для вас. Специалистам – на форумы 3D – программистов, здесь – объяснение для обычных людей, простым языком, для таких же “чайников”, как я.

Алгоритм создания невидимости основан на следующем: представим себе объект и наблюдателя, который на него смотрит из какой-то точки пространства, находящейся, разумеется, вне этого объекта. Нужно оговорить, что объект должен быть “выпуклым”, то есть плоскости, которым принадлежат грани, не должны разрезать объект. В этом случае алгоритм работает хорошо, в противном случае необходимо применять дополнительные условия или другие способы отсечения. Основан алгоритм на следующем факте: если между нормалью к грани объекта и линией наблюдения угол острый, то грань видна, а если тупой – то нет. На рисунках представлена зеленым линия наблюдения, красным – нормали, которые составляют острый угол с линией наблюдения, а фиолетовые нормали на соседнем рисунке составляют тупой угол с линией наблюдателя, поэтому эти грани – не видно. У острых углов, как известно, косинус положительный, а у тупых – отрицательный. Поэтому основывать и расчет, и построение будем именно на этом факте.

Итак, делать будем следующее: рассчитаем вектора нормалей ко всем граням. Нормалей к плоскости всегда две, нам нужна та, что глядит “наружу”, из объекта, а не “внутрь”. Зададимся линией наблюдения, и определим знак косинуса угла между нормалями и линией наблюдения. На основании этих сведений сформируем коэффициенты видимости-невидимости, и создадим гомотетии вершин объекта с указанными коэффициентами. Эти точки существуют только при определенном положении объекта, и, если их соединить отрезками, то отрезки будут исчезать при исчезновении соответствующих точек, и появляться обратно при повороте объекта и появлении данных гомотетий.

Ну а теперь – к делу. Расписываю все подробно, так, как мне хотелось бы, чтобы объяснили мне, когда я только начала вникать в эту проблему.

1. Определяем количество граней объекта. Для правильной пирамиды их 4, для параллелепипеда – 6.

2. Рассчитываем нормали к граням (по количеству граней, понятно). Это будут нормали, определенные в  вершине многоугольника, а найдем мы их путем расчета векторного произведения прилежащих ребер. Здесь важно не ошибиться с направлением нормали, как ранее сказано, она должна обязательно смотреть “наружу”, из объекта. Как же этого достичь? Тут поможет правило буравчика, известное в физике и электротехнике (для тех, кто его помнит), или следующие способы:

Вариант правила буравчика (винта) для векторного произведения через часовую стрелку: Если нарисовать векторы так, чтобы их начала совпадали и вращать первый вектор-сомножитель кратчайшим образом ко второму вектору-сомножителю и смотреть с той стороны, чтобы это вращение было для нас по часовой стрелке, вектор-произведение будет направлен от нас (завинчиваться вглубь часов) – но в этом случае мы с вами должны быть как бы “внутри” объекта.

Правило правой руки для векторного произведения:

Если нарисовать векторы так, чтобы их начала совпадали и вращать первый вектор-сомножитель кратчайшим образом ко второму вектору-сомножителю, а четыре пальца правой руки при этом показывали бы направление вращения (как бы охватывая вращающийся цилиндр), то оттопыренный большой палец покажет направление вектора-произведения (см. картинку):

Иначе говоря, нужно охватить наш объект ладонью так, чтобы грань, к которой строится нормаль, была бы сверху. Тогда один из векторов будет параллелен указательному пальцу, а второй – большому. Например:

Тогда векторное произведение векторов будет записано так:

normal=a_x*i-b_y*j+c_z*k

Это и есть искомая нормаль. В этой записи:

a_x=x_N*y_M-x_M*y_N

b_y=-(x_K*y_M-x_M*y_K)

c_z=x_K*y_N-x_N*y_K

Здесь i, j, k – единичные вектора выбранного базиса.

То есть, нам понадобятся координаты трех вершин этой грани по осям x и y

x_N, y_N, x_M, y_M, x_K, y_K, а значит, нужно их определить.

Если мы выполняем расчет для параллелепипеда, то придется рассчитать последние суммы (ну или разности) для каждой грани  – то есть 6 раз. Проделав это, будем иметь 6 нормалей.

3. Теперь подошло время для выбора места для нас с вами, как наблюдателей – то есть точки наблюдения. Вот тут у меня возникли трудности. В одних источниках рекомендуется точка, располагающаяся на оси z, причем то на положительной полуоси, то – на отрицательной. А другие рекомендуют точку с координатами (1, 1, 1). Эта точка мне кажется более правильной, если мы хотим видеть три грани параллелепипеда одновременно. Итак, выбираем точку (1, 1, 1). Вектор, изображающий это направление – тоже (1, 1, 1).

4. Определим косинус угла между нормалью и выбранным направлением на наблюдателя, и даже не сам косинус, а только его знак. Все помнят формулу скалярного произведения, в которую входит этот самый косинус, тогда, если выразить его из этой формулы, получим:

cos (alpha)={normal*(1)}/{delim{|}{normal}{|}*delim{|}{1}{|}} – здесь в числителе не просто произведение, а скалярное произведение векторов.

Или:

cos (alpha)={normal*(1)}/{({a_x}^2+{b_y}^2+{c_z}^2)*(1^2+1^2+1^2)}

Как видно, в знаменателе – заведомо положительное число, поэтому считать его вообще не нужно, на знак оно не повлияет.

Тогда всего-то и надо, что определить знак скалярного произведения вектора нормали на единичный вектор направления на наблюдателя – или знак такого числа:

a_x+b_y+c_z. Если знак положительный – грань видна, если отрицательный – грань не видна. Введем величину: orient=sgn(a_x+b_y+c_z), тогда эта величина равна 1, когда грань видна, а если не видна, то для такой грани  orient=sgn(a_x+b_y+c_z)=-1. В этих формулах: sgn – операция определения знака. Возвращает 1, если знак положителен, и (-1) – если знак отрицателен.

5. Ну вот, мы уже приближаемся к сути – то есть к вычислению коэффициентов видимости – невидимости ребер. Первый вопрос – а сколько их должно быть, этих коэффициентов? Ответ такой: их число равно удвоенному числу ребер. Тогда для параллелепипеда это 12*2=24, а для тетраэдра –  6*2=12, для правильной призмы с пятиугольником в основании – 30, с шестиугольником в основании – 36 и т.д. Половина из них – коэффициенты видимости, вторая половина – коэффициенты невидимости. Коэффициент видимости может быть равен 1(тогда элемент видно), или неопределен (тогда не видно), и аналогично коэффициент невидимости – может быть равен 1 или не определен. Зачем, спросите вы, нам тогда нужны и коэффициенты видимости, и невидимости? Пусть просто исчезает невидимое, и все! Но нам-то нужно, чтобы невидимое прорисовывалось бы, но только тоненько, пунктиром… А чтобы существовал отрезок, даже тоненький – нужно, чтобы существовали его предки – точки. Иными словами, если грань лицевая (видимая в данный момент), то все вершины существуют, и отрезки (ребра) между ними – тоже, и нарисованы они жирно. Но стоит нам повернуть объект так, чтобы эта грань стала нелицевой, задней – и точки-вершины должны исчезнуть, но вместо них появляются другие точки (в тех же местах!), и отрезок, их соединяющий – но уже пунктирный.

Не определен может быть, например, корень четной степени из отрицательного числа. Отсюда формулы для коэффициентов видимости:

visible=sqrt{sgn(orient_1+orient_2+1)}

и  невидимости:

invisible=sqrt{-sgn(orient_1+orient_2+1)}

Понятно, что, если две смежные грани невидимы, то и ребро между ними невидимо тоже. Тогда получим корень из (-1), и коэффициент видимости не определен, а коэффициент невидимости равен 1 – то, что нам и нужно. Если хотя бы одна из двух смежных граней видна – то коэффициент видимости равен 1, и ребро видно, при этом коэффициент невидимости неопределен.

6. Теперь, собственно, надо приступать к рисованию ребер. Ребро – отрезок, соединяющий точки – вершины. У нас, если объект уже построен, то вершины он уже имеет. Но нам-то понадобятся другие точки для наших то видимых, то невидимых ребер. Давайте обходить объект в каком-то одном направлении, например, против часовой стрелки. Начинаем с точки А. Отмечаем точку D как центр гомотетии, и в вершине А создаем новую точку с коэффициентом гомотетии invisible – A’. Соединяем D и А’ тонким отрезком, или пунктиром. Потом создаем вторую гомотетию точки А, но уже с коэффициентом visible – А”, и эту точку соединим с точкой D толстым, лицевым, отрезком. Только смотрите внимательно, какие точки вы соединяете. Если несколько точек находятся в одном месте, надо пощелкать мышью, чтобы выбрать нужную точку. Так, обходя верхнее основание объекта, проводим тонкий и толстый отрезки для всех ребер. Аналогично делаем для нижнего основания, а затем и для боковых ребер. И ура – все получилось!

Предлагаю посмотреть построение в видеоуроке:



Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *