РАЗДЕЛЫ ГЛАВНАЯ СТАТЬИ СКАЧАТЬ О DARK BASIC ПОЛЕЗНЫЕ ССЫЛКИ CODE DEMOS Посетители ПРИМЕЧАНИЕ! |
шаг 1, ребро 0-1: вершина 0 не лежит в нужной области, вершина 1 лежит. Ищем точку пересечения, находим точку A, добавляем ее в список вершин результата. Теперь этот список состоит из одной вершины A. шаг 2, ребро 1-2: обе вершины лежат в области, добавляем вершину 1. Результат теперь являет собой список A, 1. шаг 3, ребро 2-0: 2 лежит в области, 0 не лежит. Добавляем вершину 2 и точку пересечения B. После последнего шага, таким образом, получили корректный результат отсечения - полигон с вершинами A, 1, 2, B.
В случае, когда надо сделать отсечение в экран, последовательно применяем алгоритм, отсекая полигон прямыми sx=0, sx=XSIZE, sy=0, sy=YSIZE. Из-за такого простого вида уравнений прямых соответственно упрощается код для выяснения принадлежности вершины нужной области и поиска точки пересечния. Вот, например, кусок кода для отсечения полигона прямой sx=0 (оставляющий область sx > 0). В пунктах 3.6.1 и 3.6.2 делался упор на 2D-отсечение, т.е. отсечение экраном уже спроецированного полигона. Еще один метод - это 3D-отсечение, когда все полигоны отсекаются областью зрения камеры. В этом случае после проецирования полигон заведомо попадает в экран и дальнейшее отсечение уже не требуется. Кстати, z-отсечение при 3D-отсечение делается почти автоматически, очень хорошо вписываясь в общую схему, при использовании же 2D-отсечения придется делать еще и его. Рассмотрим стандартную камеру. Ее область зрения задается "пирамидой", ограниченной пятью плоскостями со следующими уравнениями (откуда взялось smallvalue и что это такое, написано в п.3.5): z = -dist + smallvalue y = (z + dist) * ySize / (2 * dist) y = -(z + dist) * ySize / (2 * dist) x = (z + dist) * xSize / (2 * dist) x = -(z + dist) * xSize / (2 * dist) Вот рисунок (вид сбоку), на котором видно первые три из этих плоскостей. Отсекаем полигон каждой из этих плоскостей по тому же самому алгоритму Сазерленда-Ходжмана, получаем 3D-отсечение. Теперь выясним, как это самое отсечение сделать относительно универсально (а не только для стандартной камеры), быстро и просто. Зададим наши пять плоскостей не в форме какого-то уравнения, а в форме plane = [o, n], где o - какая-то точка, принадлежащая плоскости; n - нормаль, смотрящая в то полупространство, которое мы хотим оставить. Например, для стандартной камеры в этом случае плоскости запишутся так: n = (0, 0, 1), o = (0, 0, -dist + smallvalue) n = (0, -dist, ySize/2), o = (0, 0, -dist) n = (0, dist, ySize/2), o = (0, 0, -dist) n = (-dist, 0, xSize/2), o = (0, 0, -dist) n = ( dist, 0, xSize/2), o = (0, 0, -dist) При такой форме задания плоскости критерий принадлежности произвольной точки p нужному нам полупространству выглядит очень просто: (p - o) * n >= 0. Не менее просто выглядит и процедура поиска пересечения отрезка от точки p1 до точки p2 с плоскостью. Для любой точки p внутри отрезка имеем p = p1 + k * (p2 - p1), 0 <= k <= 1, но так как p лежит в плоскости, p * n = 0; отсюда имеем (p1 * n) + (k * (p2 - p1) * n) = 0, k = -((p2 - p1) * n) / (p1 * n) = (p1 * n - p2 * n) / (p1 * n) = 1 - (p2 * n) / (p1 * n). и моментально находим точку пересечения. Все 3D-отсечение, таким образом, сводится к последовательному применению одной универсальной процедуры отсечения плоскостью. Кроме того, видно, что можно посчитать матрицу перевода стандартной камеры в произвольную, применить ее к выписанным точкам o и нормалям n для плоскостей, задающих "стандартную" область зрения (к нормалям, естественно, надо применить только "поворотную" часть матрицы) и получить, таким образом, уравнения плоскостей уже для *любой* камеры. Тогда 3D-отсечение можно сделать вообще до всяческих преобразований сцены, минимизировав, таким образом, количество поворотов и проецирований вершин - не попавшие в область зрения вершины поворачивать и проецировать, очевидно, не надо. Проецирования невидимых вершин, впрочем, можно избежать и другим образом: сделав поворот сцены, а потом 3D-отсечение "стандартной" областью зрения камеры. Рассмотрим это более подробно. Пусть у нас есть какая-то камера; пусть есть матрица, которая переводит стандартную камеру в эту камеру. Она как бы состоит из двух частей: матрицы T (обозначения здесь использутся те же самые, что в п.2.5) и матрицы параллельного переноса, совмещающей Ss и s (обозначим ее буквой M). Причем сначала применяется матрица M, потом матрица T. Так вот, для перевода какой-то плоскости-ограничителя области зрения стандартной камеры, заданной в форме plane = [o,n], надо всего лишь сделать пару матричных умножений (поскольку M - матрица переноса, и ее применение на деле сводится к трем сложениям, матричных умножений будет ровно два): new_o = T * M * std_o new_n = T * std_n Что лучше и быстрее, как обычно, не ясно. При отсечении до преобразований тест на попадание точки в область зрения стоит от 3 до 15 умножений (относительно дешевые операции типа сложений не считаем), плюс 11 умножений и 2 деления на поворот и проецирование после отсечения, зато поворачиваются и проецируются только видимые точки. При отсечении после преобразований тест стоит 8 умножений (так как в координатах нормалей шесть нулей и одна единица), зато для всех точек придется сделать 9 умножений для поворота; проецироваться же по-прежнему будут только видимые точки. Так что наиболее подходящий метод выбирайте сами. В завершение осталось только привести процедуру для отсечения полигона произвольной плоскостью: // вычитание векторов float vecsub(vertex *result, vertex a, vertex b) { result->x = a.x - b.x; result->y = a.y - b.y; result->z = a.z - b.z; } // скалярное умножение векторов float vecmul(vertex a, vertex b) { return a.x * b.x + a.y * b.y + a.z * c.z; } // dst - массив для сохранения вершин результата // src - массив вершин исходного полигона // num - число вершин исходного полигона // n - нормаль к плоскости // o - точка, лежащая в плоскости // функция возвращает число вершин результата int clipPlane(vertex *dst, vertex *src, int num, vertex n, vertex o) { int i, r; vertex p1, p2, tmp; float t1, t2; float k; r = 0; for (i = 0; i < num; i++) { p1 = src[i]; p2 = src[(i + 1) % num]; vecsub(&tmp, p1, o); t1 = vecmul(tmp, n); vecsub(&tmp, p2, o); t2 = vecmul(tmp, n); if (t1 >= 0) { // если начало лежит в области dst[r++] = p1; // добавляем начало } // если ребро пересекает границу // добавляем точку пересечения if (((t1 > 0) && (t2 < 0)) || ((t2 >= 0) && (t1 < 0))) { k = 1 - vecmul(p1, n) / vecmul(p2, n); dst[r].x = p1.x + k * (p2.x - p1.x); dst[r].y = p1.y + k * (p2.y - p1.y); dst[r].z = p1.z + k * (p2.z - p1.z); dst[r].u = p1.u + k * (p2.u - p1.u); dst[r].v = p1.v + k * (p2.v - p1.v); r++; } } return r; } (Автором данной статьи является Андрей Аксенов. Адрес в FIDO: 2:5036/5.47) |
Copyright © 2006 Miha, PGCreate Administrator.Все права защищены.
E-mail автора сайта (webmaster) |