Войти
ПрограммированиеФорумГрафика

Z-интерполяция

Страницы: 1 2 Следующая »
#0
20:56, 4 ноя. 2017

Всем привет!
Может кто занимался написанием z-интерполяции в треугольнике?
Очень хочется избавиться от деления единицы на произведение векторов.

+ Показать

Pt.X/Y/Z - барицентрические координаты точки в треугольнике.
Z.X/Y/Z - вектор глубины треугольника, т.е. единица поделенная на Z вершин.

Есть идеи или рекомендации по этому поводу?
Лучше одно доп. умножение, чем деление.
Убрав единицу в итоге неплохо поднимается ФПС в софтрендере.

#1
23:53, 4 ноя. 2017

Можно попробовать заменить деление на поиск приближенного обратного числа. Ни чего другого тут не изменишь. Максимум надо смотреть всю реализацию, возможно что то переставить, чтобы делений было не так много.
eDmk
> произведение векторов.
Это скалярное произведение, используется для проекции вектора на ось Z.

Функция пересчета трехмерных координат в проекцию у меня тоже имеет одно деление и я особенно не парился. Только это деление используется для нормализации значений под размеры проекционного куба, а не для расчета глубины. То есть если убрать деление то, остальные расчеты нужно будет производить уже с учетом домножения на r[3] и последующим делением на r[3]. А иначе картинка будет просто ортогональной, а не перспективной.

+ Показать
#2
1:44, 5 ноя. 2017

Да у меня 2 формулы перспективной интерполяции и везде надо единицу делить на выражение.
Видимо из-за перспективной матрицы, которая тоже приведена к единице.
Жаль. Это деление тормозит расчеты довольно сильно.

#3
2:00, 5 ноя. 2017

Обратное число это тоже самое число только в минус первой степени. Если посмотреть на вычисление быстрого обратного корня то
i  = 0x7F800000 - ( i );
Будет первым приближением обратного числа. А дальше можно уточнить его по такой формуле:
y  = y * ( 1.5 - ( x2 * y * y ) );
Или просто написать на асме с использованием соответствующей команды для вычисления обратного значения. Хотя на сколько я помню это делается автоматически компилятором, если используется формат float.

У меня софтверный треугольник рисуется так

+ Показать

Как видишь тут полно делений.

#4
2:07, 5 ноя. 2017

eDmk
XYZ - это индексы вершин? При этом начальная Z - это именно координата?

Не так страшно деление, как его малюют. Инструкция приблизительного деления (rcpss) примерно в ту же скорость что и умножение. Даже полноценное деление не такое уж долгое. А ещё можно 4 деления одновременно, с SIMD.

В кваке вроде делили раз в несколько пикселей, в посередине использовали результат от соседей.

Pt.X * Z.X + Pt.Y * Z.Y + Pt.Z * Z.Z можно без умножений, т. к. результат между соседними пикселями в строке отличается на константу.
Не обязательно использовать 3 барицентрические координаты: Pt.Z=1-(Pt.X+Pt.Y). Подставить, преобразовать.

Если у тебя паскаль - хз как там с оптимизациями (и 32-битными флоатами и SIMD). Можешь поспрашивать у тех, кто с этим больше работает.

#5
2:58, 5 ноя. 2017

Просто если заменить на умножение, то получается быстрее на 5 fps на ядро.
В совокупности на 4-х ядернике это почти + ~15 fps. На моем 10-и ядернике + ~30 fps.
Для софтрендера это хороший прирост. Вот и заморочился вопросом.
Да на асме все и написано. Все работает достаточно быстро, но без деления еще быстрее.
Правда результат пока неправильный.

+ Показать

>Не обязательно использовать 3 барицентрические координаты
К сожалению обязательно. Иначе треугольник неправильный.

#6
4:40, 5 ноя. 2017

eDmk
>К сожалению обязательно. Иначе треугольник неправильный.
Можно подробнее?
Pt.X * Z.X + Pt.Y * Z.Y + Pt.Z * Z.Z = Pt.X * Z.X + Pt.Y * Z.Y + (1.0-(Pt.X+Pt.Y)) * Z.Z = Z.Z + Pt.X * (Z.X - Z.Z) + Pt.Y * (Z.Y - Z.Z).
(Z.X - Z.Z) и (Z.Y - Z.Z) - константы.
Или у тебя Pt.X + Pt.Y + Pt.Z = 1.0 не выполняется? А почему?

  //Делим единицу на выражение
  divsd xmm0, xmm1

  //Конвертируем в single
  cvtsd2ss xmm0, xmm0
Нафига? Деление float32 заметно быстрее, чем деление float64.
Можно
  //Конвертируем в single
  cvtsd2ss xmm0, xmm0

  //Делим единицу на выражение
  divss xmm0, xmm1
или
  //Конвертируем в single
  cvtsd2ss xmm0, xmm0

  //Делим единицу на выражение
  rcpss xmm0, xmm0
если точности 14 бит достаточно.

И вообще нафига тебе double?

И можно считать 4 за раз (divps/rcpps).

Выражение Pt.X * Z.X + Pt.Y * Z.Y + Pt.Z * Z.Z меняется линейно вдоль строчки пикселей. Инкремент можно посчитать один раз, и дальше складывать с предыдущим результатом. Одно сложение, а не 3 умножения ж.

#7
10:15, 5 ноя. 2017

Тут правда без глубины.

+ Показать

С глубиной так

+ Показать

_S4AFDLSdrawDEF и _S4AFSdrawDEF рисуют горизонтальную линию для разных режимов, с тестом глубины и без.
Тут интерполяция представлена в целых числах как числа с фиксированной точкой. Для цвета 8 бит после запятой, для глубины 15 бит после запятой. Буфер глубины 16 битный. Глубина для следующей точки как и цвет вычисляется одним сложением.
((S4AFDSdata*)data)->z+=((S4AFDSdata*)data)->za;

triangle_MultyColor_8888_loop2_deff - собирает часть треугольника из этих линий.

#8
13:05, 5 ноя. 2017

>Можно подробнее?
Константа только вектор Z. Это глубина треугольника по вершинам в барицентрических координатах.

+ Показать

Вектор точки Pt у меня не константа. Это интерполируемый край. AX+BY+C=0.
Он приращается простым сложением. Pt := Pt + Delta; Получается следующий пиксел.
Если пиксел <= 0, то точка внутри треугольника. (У меня отрицательная площадь).
Потом идет приведение пиксела к барицентрическим координатам в треугольнике, т.е. умножаем на площадь.
Потом вычисляем Z (формула в первом посте) и сравниваем с Z-Buffer'ом.
У меня приложение x64, поэтому double. Он быстрее обрабатывается в x64 чем single.
Кроме того у single глюки. Z-Fighting краев полигона. Видно если прозрачность включить.
С даблом меньше проблем.

#9
13:07, 5 ноя. 2017

>foxes
Это неинтерполируемый треугольник видимо.
Таких алгоритмов у меня куча. Они не правильные.
Интерполяция идет только по формуле:  AX+BY+C=0.
Остальное неправильно. Хотя на треугольник похоже.

Получается идеально, но медленно :(

+ Показать

#10
14:13, 5 ноя. 2017

eDmk
Как вариант, если вектора нормализированные и скалярное произведение меньше 1, то можно воспользоваться разложением в ряд тейлора и считать до скажем 4-го члена ряда:
Изображение
при
Изображение

#11
14:42, 5 ноя. 2017

eDmk
> Это неинтерполируемый треугольник видимо.
Что значит неинтерполируемый?
foxes
> интерполяция представлена в целых числах как числа с фиксированной точкой. Для
> цвета 8 бит после запятой, для глубины 15 бит после запятой.

#12
18:24, 5 ноя. 2017

eDmk
> Потом вычисляем Z (формула в первом посте) и сравниваем с Z-Buffer'ом.
Для целей Z-буффера деление нафиг не нужно, 1/Z работает так же хорошо, как и Z.
Перспективное деление требуется для корректной интерполяции текстурных координат и прочих атрибутов.

> У меня приложение x64, поэтому double. Он быстрее обрабатывается в x64 чем single.
Полный бред. SSE/AVX позволяют работать с float-ами в два (и больше в случае комплексных команд) раза быстрее.
Растеризаторы ложаться под SIMD идеально, поэтому никаких оправданий использованию double нет.
Собственно, то что GPU используют преимущественно float является подтверждению этого тезиса.

> Кроме того у single глюки. Z-Fighting краев полигона. Видно если прозрачность включить.
Вот поэтому все нормальные растеризаторы работают с целыми числами для границ треугольников.
Заодно отсекаются узкие сверхтонкие треугольники, которые являются проблемой.

#13
19:43, 5 ноя. 2017

}:+()___ [Smile]
>Полный бред. SSE/AVX позволяют работать с float-ами в два (и больше в случае комплексных команд) раза быстрее.
Комплексных? Даже несчастные умножение/деление - скалярные - быстрее (умножение - только по latency).

InstructionLatencyReciprocal throughput
MULSS MULPS41
MULSD MULPD51
DIVSS DIVPS7-147-14
DIVSD DIVPD7-227-22
RCPSS/PS32

Это Агнер Фог, для i7.

eDmk
>Константа только вектор Z.
>Вектор точки Pt у меня не константа.
И что?
Как это противоречит тому, что
Pt.X*Z.X+Pt.Y*Z.Y+Pt.Z*Z.Z=S*Z.Z+Pt.X*(Z.X-Z.Z)+Pt.Y*(Z.Y-Z.Z)
где Pt.X+Pt.Y+Pt.Z=S (которое константа что по смыслу барицентрических координат)?
Ну и в любом случае, если
>Константа только вектор Z.
>Он приращается простым сложением. Pt := Pt + Delta;
то InvZ=Pt.X*Z.X+Pt.Y*Z.Y+Pt.Z*Z.Z тоже инкрементируется простым сложением: InvZ:=InvZ+DeltaInvZ, где DeltaInvZ=Delta.X*Z.X+Delta.Y*Z.Y+Delta.Z*Z.Z.

>У меня приложение x64, поэтому double. Он быстрее обрабатывается в x64 чем single.
Очень странное утверждение. Походу неправда. }:+()___ [Smile] дело говорит.

>Кроме того у single глюки. Z-Fighting краев полигона. Видно если прозрачность включить.
Опять же, }:+()___ [Smile] в теме: обычно pixel coverage делают в целых числах с субпиксельной точностью.

#14
21:37, 5 ноя. 2017

FordPerfect
> Комплексных? Даже несчастные умножение/деление - скалярные - быстрее (умножение - только по latency).
Комплексные — это те, которые не имеют специализированного железа и выполняются софтварно микрокодом.
Отличить их можно по плавающей производительности — это деление, корень и все что сложнее.

Умножение же — это чисто хардварная операция (возможно, за исключением NaN-ов и денормалов).
Скорее всего, выполняется на общем железе для float/double (а возможно и для целых), непонятно откуда там дополнительная задержка.

Кстати, хорошо видно, что "скалярные" операции на самом деле векторные с отключенной записью результата.

Страницы: 1 2 Следующая »
ПрограммированиеФорумГрафика

Тема в архиве.