Студопедия Главная Случайная страница Обратная связь

Разделы: Автомобили Астрономия Биология География Дом и сад Другие языки Другое Информатика История Культура Литература Логика Математика Медицина Металлургия Механика Образование Охрана труда Педагогика Политика Право Психология Религия Риторика Социология Спорт Строительство Технология Туризм Физика Философия Финансы Химия Черчение Экология Экономика Электроника

D-отсечение





В пунктах 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)

Вот рисунок (вид сбоку), на котором видно первые три из этих плоскостей.

y

| ===

| | ===

| ===

| ===|

| === |

=|= |

----*==-|-------O-----------z

=|= |

| === |

| ===|

| ===

| | ===

| ===

|

Отсекаем полигон каждой из этих плоскостей по тому же самому алгоритму Сазерленда-Ходжмана, получаем 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;

}

Точное

Задача текстурирования формулируется таким образом: есть грань – согласно предположениям, треугольная - с наложенной на нее текстурой. То есть каждая точка грани окрашена цветом соответствующей ей точки в текстуре. Текстура накладывается линейным образом. Есть точка экрана с координатами на экране (sx,sy), принадлежащая проекции грани. Требуется найти ее цвет, то есть цвет соответствующей этой точке экрана точки текстуры. А для этого надо найти координаты текстуры для этой точки - точнее, для той точки, проекцией которой на экран является наша (sx,sy). Пусть вершины грани есть точки A(Ax,Ay,Az), B(Bx,By,Bz) и C(Cx,Cy,Cz), а соответствующие им точки текстуры - (Au,Av), (Bu,Bv) и (Cu,Cv). Найдем координаты текстуры для точки, проекцией которой является (sx,sy).

Для точек (x,y,z), проекцией которых является (sx,sy) имеем:

sx = xSize/2+x*dist/(z+dist),

sy = ySize/2-y*dist/(z+dist).

Для упрощения формул будем использовать обозначения

i = sx-XSize/2,

j = YSize/2-sy,

Z = z+dist.

Тогда эти формулы примут вид

i = x*dist/Z,

j = y*dist/Z,

или, что равносильно:

i*Z = x*dist,

j*Z = y*dist.

Рассмотрим точку D, принадлежащую грани. Для нее D = A+a*(B-A)+b*(C-A), так как она лежит в грани. D однозначно задается парой (a,b). Для нее координаты текстуры (из того, что текстура накладывается линейно) будут такие:

Du = Au+a*(Bu-Au)+b*(Cu-Au),

Dv = Av+a*(Bv-Av)+b*(Cv-Av).

Пусть проекция D на экран как раз и имеет координаты (sx,sy), тогда для нее выполнены написанные выше соотношения:

i*DZ = Dx*dist,

j*DZ = Dy*dist.

Подставим сюда координаты D, выраженные через координаты A, B и неизвестные коэффициенты a, b:

 

i*(Az+a*(Bz-Az)+b*(Cz-Az)+dist) = dist*(Ax+a*(Bx-Ax)+b*(Cx-Ax)),

j*(Az+a*(Bz-Az)+b*(Cz-Az)+dist) = dist*(Ay+a*(By-Ay)+b*(Cy-Ay)),

 

т.к. мы договорились, что AZ = Az+dist, и, кстати, поэтому BZ-AZ = Bz-Az, это можно чуть укоротить:

 

i*(AZ+a*(BZ-AZ)+b*(CZ-AZ)) = dist*(Ax+a*(Bx-Ax)+b*(Cx-Ax)),

j*(AZ+a*(BZ-AZ)+b*(CZ-AZ)) = dist*(Ay+a*(By-Ay)+b*(Cy-Ay)).

 

Выделим a и b:

 

a*(i*(BZ-AZ)-dist*(Bx-Ax))+b*(i*(CZ-AZ)-dist*(Cx-Ax)) = dist*Ax-i*AZ,

a*(j*(BZ-AZ)-dist*(By-Ay))+b*(j*(CZ-AZ)-dist*(Cy-Ay)) = dist*Ay-j*AZ.

 

Получили систему двух линейных уравнений, из которой можно найти a и b, а по ним немедленно вычисляются u и v. Введем вектор M = BA (Mx = Bx-Ax и т.д.) и вектор N = CA, обозначим d = dist (все это для краткости записи). Тогда эти два уравнения перепишутся в виде:

 

a*(i*Mz-d*Mx)+b*(i*Nz-d*Nx) = d*Ax-i*AZ,

a*(j*Mz-d*My)+b*(j*Nz-d*Ny) = d*Ay-j*AZ,

 

и решая систему (например, по формулам Крамера) получаем:

 

(dAx-iAZ)(jNz-dNy)-(dAy-jAZ)(iNz-dNx)

a = ------------------------------------- =

(iMz-dMx)(jNz-dNy)-(iNz-dNx)(jMz-dMy)

 

djAxNz-ddAxNy-ijAZNz+idAZNy-diAyNz+ddAyNx+ijNzAZ-djAZNx

= ------------------------------------------------------- =

ijMzNz-idMzNy-djMxNz+ddMxNy-ijMzNz+idNzMy+djNxMz-ddNxMy

 

dj(AxNz-AZNx)+dd(AyNx-AxNy)+id(AZNy-AyNz)

= ----------------------------------------- =

id(MyNz-MzNy)+dj(NxMz-MxNz)+dd(MxNy-NxMy)

 

i(AZNy-AyNz)+j(AxNz-AZNx)+d(AyNx-AxNy)

= --------------------------------------

i(MyNz-MzNy)+j(NxMz-MxNz)+d(MxNy-NxMy)

 

аналогично получаем

 

i(AZMy-AyMz)+j(AxMz-AZMx)+d(AyMx-AxMy)

b = --------------------------------------

i(MyNz-MzNy)+j(NxMz-MxNz)+d(MxNy-NxMy)

 

Знаки умножения здесь везде опущены, иначе читать это все совсем невозможно.

 

Осталось немного, подставить Mx = Bx-Ax и так далее, а потом подставить эти a и b в формулы для Du и Dv. В процессе подстановок что-то сократится, что-то упростится, но формулы для Du и Dv все равно получатся длиной в несколько строк.

 

Но из этих формул видно кое-что интересное. А именно, то, что

 

u = (C1*sx+C2*sy+C3) / (C4*sx+C5*sy+C6),

v = (C7*sx+C8*sy+C9) / (C4*sx+C5*sy+C6),

 

причем C1,..., C9 - просто какие-то коэффициенты, зависящие от грани. То есть, можно посчитать эти коэффициенты один раз на грань, а потом считать u, v по написанным пару строк выше формулам. Но все равно получается как минимум одно деление на точку. Это слишком медленно. Поэтому все методы текстурирования на самом деле используют приближенные вычисления, хотя и именно по этим формулам.

 

Стоит отметить тот факт, что на самом деле здесь грань не обязательно должна быть треугольной - можно взять любые три вершины многоугольной грани. То же самое справедливо и для остальных описанных методов текстурирования.


 







Дата добавления: 2015-10-02; просмотров: 438. Нарушение авторских прав; Мы поможем в написании вашей работы!




Кардиналистский и ординалистский подходы Кардиналистский (количественный подход) к анализу полезности основан на представлении о возможности измерения различных благ в условных единицах полезности...


Обзор компонентов Multisim Компоненты – это основа любой схемы, это все элементы, из которых она состоит. Multisim оперирует с двумя категориями...


Композиция из абстрактных геометрических фигур Данная композиция состоит из линий, штриховки, абстрактных геометрических форм...


Важнейшие способы обработки и анализа рядов динамики Не во всех случаях эмпирические данные рядов динамики позволяют определить тенденцию изменения явления во времени...

Методы анализа финансово-хозяйственной деятельности предприятия   Содержанием анализа финансово-хозяйственной деятельности предприятия является глубокое и всестороннее изучение экономической информации о функционировании анализируемого субъекта хозяйствования с целью принятия оптимальных управленческих...

Образование соседних чисел Фрагмент: Программная задача: показать образование числа 4 и числа 3 друг из друга...

Шрифт зодчего Шрифт зодчего состоит из прописных (заглавных), строчных букв и цифр...

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

Значення творчості Г.Сковороди для розвитку української культури Важливий внесок в історію всієї духовної культури українського народу та її барокової літературно-філософської традиції зробив, зокрема, Григорій Савич Сковорода (1722—1794 pp...

Постинъекционные осложнения, оказать необходимую помощь пациенту I.ОСЛОЖНЕНИЕ: Инфильтрат (уплотнение). II.ПРИЗНАКИ ОСЛОЖНЕНИЯ: Уплотнение...

Studopedia.info - Студопедия - 2014-2024 год . (0.011 сек.) русская версия | украинская версия