Связь и интернет Архив Программирование
   
Сделать стартовойСделать закладку            
   ПОИСК  
   
Главная / Алгоритмы / Графика / demodesign 3D programming FAQ /
8  Perl
8  PHP
8  JavaScript
8  HTML
8  DHTML
8  XML
8  CSS
8  C / C++
8  Pascal и Delphi
8  Турбо Ассемблер
8  MySQL
8  CASE-технологии
8  Алгоритмы
8  Python
8  Обратная связь
8  Гостевая книга
Новости о мире


Текстурирование - Программирование от RIN.RU
Текстурирование



Перспективно-корректное


Этот метод основан на приближении u, v кусочно-линейными функциями. Кратко говоря, при рисовании каждая строка разбивается на куски (обычно несколько кусков длиной 8/16/32 пикселов и один оставшийся произвольной длины), в начале и конце каждого куска считаются точные значения u, v, а на куске они интерполируется линейно.


Точные значения u и v, в принципе, можно считать по формулам из 4.1, но обычно используют более простой путь. Он основан на том факте, что значени 1/Z, u/Z и v/Z зависят от sx, sy ЛИНЕЙНО. Доказательство этого факта пока опущено. Таким образом, достаточно для каждой вершины посчитать 1/Z, u/Z, v/Z и линейно их интерполировать - точно так же, как интерполируются u и v в 4.2. Причем, так как эти значения зависят от sx, sy строго линейно, то интерполяция дает не сильно приближенные результаты, а абсолютно точные!


Сами же точные значения u, v считаются как


u = (u/Z) / (1/Z),
v = (v/Z) / (1/Z).


Дальше все становится совсем просто. При рисовании треугольника, на ребрах интерполируем не u и v, как в этом пункте, а 1/Z, u/Z, v/Z. Кроме того, заранее считаем d(u/Z)/dsx, d(v/Z)/dsx, d(1/Z)/dsx (то есть, изменений этих самых u/Z, v/Z, 1/Z соотвествующих шагу по dsx на 1) так, как считали du/dsx - это будет нужно для быстрого вычисления точных значений u, v. Каждую линию рисуем кусками по 8/16/32 пикселов (на самом деле, кусками любой длины; просто если длина - степень двойки, то при вычислении du/dx и dv/dx для текущего куска можно деление на длину куска заменить сдвигом вправо) и, если надо, рисуем оставшийся хвостик. Для расчета точных значений u, v в конце каждого куска пользуемся посчитанными (ага!) значениями d(u/Z)/dsx, d(v/Z)/dsx, d(1/Z)/dsx; раз значения u/Z, v/Z, 1/Z в начале куска известны, меняются они линейно и длина куска известна (либо 16 пикселов, либо длина остатка), то в конце куска они считаются все это до боли просто:



// расчет u/Z, v/Z, 1/Z в конце куска
uZ_b = uZ_a + length * duZ_dsx;
vZ_b = vZ_a + length * dvZ_dsx;
Z1_b = Z1_a + length * dZ1_dsx;




Все вместе выглядеть это будет примерно так:



// ...
current_sx = x_start;
length = x_end - x_start;


// расчет u/Z, v/Z, 1/Z, u, v в начале самого первого куска
uZ_a = uZ_start;
vZ_a = vZ_start;
Z1_a = Z1_start; // это 1/Z
u_a = uZ_a / Z1_a;
v_a = vZ_a / Z1_a;


// рисуем куски по 16 пикселов
while (length >= 16) {
// расчет u/Z, v/Z, 1/Z, u, v в конце куска
uZ_b = uZ_a + 16 * duZ_dsx;
vZ_b = vZ_a + 16 * dvZ_dsx;
Z1_b = Z1_a + 16 * dZ1_dsx;
u_b = uZ_b / Z1_b;
v_b = vZ_b / Z1_b;


u = u_a; // начинаем текстурирование с начала куска
v = v_a;
// можно сделать >> 4, используя fixedpoint
du = (u_b - u_a) / 16;
dv = (v_b - v_a) / 16;


// рисуем 16 пикселов старым добрым "аффинным" методом
len = 16;
while (len--) {
putpixel(current_sx, current_sy, texture[(int)v][(int)u]);
u += du;
v += dv;
current_sx++;
}
length -= 16;


// конец куска становится началом следующего куска
uZ_a = uZ_b;
vZ_a = vZ_b;
Z1_a = Z1_b;
u_a = u_b;
v_a = v_b;
}


// дорисовываем "хвост" линии, если он непуст
if (length != 0) {
uZ_b = uZ_a + length * duZ_dsx;
vZ_b = vZ_a + length * dvZ_dsx;
Z1_b = Z1_a + length * dZ1_dsx;
u_b = uZ_b / Z1_b;
v_b = vZ_b / Z1_b;


u = u_a; // начинаем текстурирование с начала куска
v = v_a;
du = (u_b - u_a) / length;
dv = (v_b - v_a) / length;


// рисуем остаток пикселов старым добрым "аффинным" методом
while (length--) {
putpixel(current_sx, current_sy, texture[v][u]);
u += du;
v += dv;
current_sx++;
}
}
// ...




Как и в этом пункте, пройдемся подобным куском кода по всем строкам грани, не забыв вместо "// ..." вставить интерполяцию всяких там [u/v/1]Z_start, содранную с интерполяции u_start.. и - о чудо, текстурированная с учетом перспективы грань!


Осталось сказать еще пару слов о кое-какой оптимизации.


Во-первых, два деления при расчете u и v в цикле прорисовки можно (и нужно) заменить на одно - посчитать tmp = 1/Z, дальше u = uZ * tmp, v = vZ * tmp.


Во-вторых, немного поменяв местами блоки расчета очередной пары точных значений u и v и прорисовки очередного куска линии, можно добиться того, что это самое одно деление, нужное для расчета u и v для *следующего* куска будет находиться сразу перед прорисовкой *текущего* куска. А в этом случае деление может исполняться в сопроцессоре одновременно с отрисовкой куска линии в процессоре. То есть единственная медленная операция будет считать на полную халяву! Получим перспективно-корректное текстурирование, которое (теоретически) будет работать ненамного медленнее аффинного.


В-третьих, деление на length при дорисовке хвостика длиной от 1 до 15 пикселов можно заменить на умножение на 1/length, заранее посчитав табличку для значений 1/length.


И наконец, мелкие треугольники можно текстурировать аффинным методом, а большие - методом с коррекцией. Размер треугольника можно определять хот бы по длине самой длинной горизонтальной линии:


x_start = A.sx+(B.sy-A.sy)*(C.sx-A.sx)/(C.sy-A.sy),
x_end = B.sx,
longest_length = x_end - x_start,


все равно мы ее считаем для расчета du_dsx или duZ_dsx и иже с ними.


<<<  НазадВперед  >>>
 1  2  3  4  5  6 


 8  Комментарии к статье  8 8  Обсудить в чате

 
  
  
    Copyright ©  RIN 2003 - 2004      * Обратная связь