Добил проблему ошибок в A-буфере. Всё дело в том, что правильная индексация в алгоритме возможна при размерностях буфера, кратного степени двойки. Например, 515x256, 1024x1024. Если это не так, получается выход за пределы массива (я проверял "на бумажке"). Хотя в современных GPU можно читать/писать за пределами ресурса (returns 0 on reads, writes are no-ops), prefix sum считается неправильно, и часть изображения портится. При размерностях, кратных степени двойки, всё Ok.
Теперь вот задумался. Простой метод решения проблемы - создавать буфер, кратный степени двойки, не эффективно в случае мониторов 16:9 - лишняя память впустую, а A-buffer и так немаленький. Вычисления, вероятно, можно оптимизировать, чтобы правильный prefix sum строился только в видимой части буфера. Но всё равно, интересно, можно ли написать алгоритм для параллельного сканирования буфера произвольных размеров?
Теперь думаю написать с нуля собственное демо по использованию A-буфера, т. к. смотреть на коды SDK уже тошно.
Update
Пока фарширую демку кодом и подумываю, какую модель использовать для прозрачности. Что-то простенькое или поэффектнее?
Оптимизируя по ходу OIT11, я решил убрать копирование RWTexture2D в RWBuffer. Но получилось медленнее, главным образом из-за того, что ClearUnorderedAccessViewUint() RWBuffer-а с Catalyst 9.12 очень медленный. Вместо очистки я решил использовать Compute Shader. Выбираем оптимальный размер блока, подключаем UAV, вызываем Dispatch() и пишем в шейдере 0. Получается быстрее очистки, что говорит о проблеме в драйвере, а не железе. Одна беда - всё равно получается медленнее, чем копирование! Не то, чтобы сильно медленнее, но всё же. По-видимому, тут проблема с паттерном доступа к памяти. При копировании блоками получается последовательное чтение/записть, RWBuffer это любит. А вот при unordered записи из пиксельного шейдера выгоднее, получается, использовать RWTexture2D. А может быть, доступ к RWBuffer не кэшируется, в отличие от RWTexture*, поэтому конструкция ++buffer[pos.xy] при overdraw для RW-текстуры получается быстрее.
Ещё надо посмотреть, как ведёт себя RWTexture1D.
суббота, 2 января 2010 г.
четверг, 31 декабря 2009 г.
2010
Я оптимизировал, как мог, исходный код сэмпла OIT11 из DX SDK и заставил его выдавать соответствующую D3D11 железу скорость.
Пока адресация заточена под базовые 640x480, поэтому не привожу fps для высоких разрешений. К тому же оптимизации не закончены. О плохом: к концу буфера кадра (внизу окна) вылязят ужасные ошибки. Не знаю, где точно проблема - то ли в read/write contention, то ли в адресации/распределении тредов. Будем бороться - у Мехи же получилось :)
На этой мажорной ноте и закончу research 2009 года. Дай бог, чтобы в следующем году у меня было хоть немного времени на все эти занятные штуковины.
Да!
C Новым Годом! С новым, так сказать, счастьем :)
Пока адресация заточена под базовые 640x480, поэтому не привожу fps для высоких разрешений. К тому же оптимизации не закончены. О плохом: к концу буфера кадра (внизу окна) вылязят ужасные ошибки. Не знаю, где точно проблема - то ли в read/write contention, то ли в адресации/распределении тредов. Будем бороться - у Мехи же получилось :)
На этой мажорной ноте и закончу research 2009 года. Дай бог, чтобы в следующем году у меня было хоть немного времени на все эти занятные штуковины.
Да!
C Новым Годом! С новым, так сказать, счастьем :)
понедельник, 28 декабря 2009 г.
Xerxes
Совсем недавно стало ясно, что продолжение "300 спартанцев" скорее будет, чем нет. Зак Снайдер подтвердил, что работает над приквелом с кодовым именем "Ксеркс".
Wow!
воскресенье, 27 декабря 2009 г.
Parallel Prefix Sum (Scan)
Несколько дней назад я тестировал сэмпл OIT11 из DirectX SDK и был неприятно поражён его производительностью: 12 fps в окне 320х240. Решив разобраться, в чём дело, я реализовал небольшую программу, приблизительно воспроизводящую алгоритм prefix sum из OIT11, а также проштудировал материал по теме.
Вот код программы: OIT prefix sum.
Во-первых, алгоритм сканирования реализован "в лоб". В процессе выполнения видно, что к концу сканирования он плохо параллелится - последний (единственный) тред суммирует половину фрагментов буфера кадра, что никак нельзя назвать параллельной работой. Во-вторых, асимптотическая оценка кол-ва операций этого алгоритма = O(N * log(N)), тогда как достаточно O(N) операций (более точно, этот алгоритм выполняет N * log(sqrt(N)) операций), т. е. для сколь-нибудь большого буфера кадра его производительность ниже оптимального на несколько (!) порядков.
Фактически, применённый алгоритм - это naive prefix scan, описание которого можно найти у NVIDIA. Для пробы я написал код и для него: Naive prefix sum. Асимптотическая оценка времени его выполнения тоже O(N * log(N)). Единственное отличие - для OIT11 не требуется промежуточный буфер, куда записываются результаты суммирования, а потом копируются обратно в основной буфер.
Наиболее оптимальный на сегодняшний день алгоритм был описан Guy E. Blelloch в 1990. Он состоит из двух фаз - reduce (up-sweep) и down-sweep. Алгоритм хорошо распараллеливается, т. к. каждый тред выполняет всего одно сложение (или сложение/обмен в down-sweep). Алгоритм выполняет ~(N * 2) операций, т. е. его асимптотическая оценка - O(N). Inclusive prefix sum.
P.S. Я не очень хорошо разбираюсь в CUDA и нюансах работы тредов, поэтому меня несколько смущают описанные проблемы конфликтов банков памяти при доступе из различных тредов. Я вот задумался: атомические операции в HLSL 11 призваны убрать именно эту проблему или они нужны для чего-то другого?
Вот код программы: OIT prefix sum.
Во-первых, алгоритм сканирования реализован "в лоб". В процессе выполнения видно, что к концу сканирования он плохо параллелится - последний (единственный) тред суммирует половину фрагментов буфера кадра, что никак нельзя назвать параллельной работой. Во-вторых, асимптотическая оценка кол-ва операций этого алгоритма = O(N * log(N)), тогда как достаточно O(N) операций (более точно, этот алгоритм выполняет N * log(sqrt(N)) операций), т. е. для сколь-нибудь большого буфера кадра его производительность ниже оптимального на несколько (!) порядков.
Фактически, применённый алгоритм - это naive prefix scan, описание которого можно найти у NVIDIA. Для пробы я написал код и для него: Naive prefix sum. Асимптотическая оценка времени его выполнения тоже O(N * log(N)). Единственное отличие - для OIT11 не требуется промежуточный буфер, куда записываются результаты суммирования, а потом копируются обратно в основной буфер.
Наиболее оптимальный на сегодняшний день алгоритм был описан Guy E. Blelloch в 1990. Он состоит из двух фаз - reduce (up-sweep) и down-sweep. Алгоритм хорошо распараллеливается, т. к. каждый тред выполняет всего одно сложение (или сложение/обмен в down-sweep). Алгоритм выполняет ~(N * 2) операций, т. е. его асимптотическая оценка - O(N). Inclusive prefix sum.
P.S. Я не очень хорошо разбираюсь в CUDA и нюансах работы тредов, поэтому меня несколько смущают описанные проблемы конфликтов банков памяти при доступе из различных тредов. Я вот задумался: атомические операции в HLSL 11 призваны убрать именно эту проблему или они нужны для чего-то другого?
четверг, 24 декабря 2009 г.
Radeon HD 5770
Решил всё-таки купить новый Radeon. Подарок самому себе на Новый год, так сказать. Денег на 5800-ю серию нет (да и БП не потянет такого монстра), решил приобрести middle-end, а именно Sapphire Radeon HD 5770 1GB. Решил взять чуть подороже, зато Sapphire, чтобы быть уверенным, что производитель не втюхает мне какую-нить убогую память или ещё как-нить облапошит, подсунув законченный и совершенный low-end. Карта мне обошлась в 202$, хотя рекомендованная цена от AMD - 160$. Барыги накручивают - в нищей стране, как известно, платят дважды.
Вот так выглядит коробка:
Внутри видеокарта в антистатическом пакете (с громким предупреждением о необходимости в дополнительном питании для платы!), переходник Molex/6-PinPCI-E, инструкция для упоротых, диск с драйверами под XP/Vista/Seven, переходник DVI/VGA и перемычка для CrossFire:
Вот сама видеокарта в исполнении Sapphire:
А это она уже в системном блоке, с подключенным питанием:
Пыльновато тут, завтра надо бы почистить трудягу. Кстати о питании. AMD рекомендует для HD 5770 и HD 5750 блоки питания как минимум 450 W. У меня 400 W, поэтому были небольшие опасения, что этого может не хватить. Хватило :) В принципе, даже в минимальные требования производителем в таких случаях закладывается определённый "запас", на случай если у пользователя на БП уже висит несколько HDD, 4 гига RAM и т. д. У меня системник этим необременён, поэтому по этому поводу я особенно не волновался.
А вот мои предыдущие видеокарты:
Слева направо: GeForce 6600 256MB (покупалась изначально с компьютером), Radeon X1300 128MB (приобретена исключительно в целях отладки OpenGL-кода), Radeon HD 2400 256MB (приобретена с целью писать код по Direct3D 10).
Значит установил я драйверы и начал тестировать сэмплы из DirectX 11 SDK.
Тесселяция работает замечательно, провалов в скорости, как это было с GS, нет. Я погонял разные демки (их в SDK 3). Например, чайник в демке PNTriangles11 показывает 1620 fps без тесселяции и 250 fps при уровне тесселяции 19. В примере SubD11 скорость падает быстрее, возможно из-за анимации или просто неоптимально написано. Но в общем при "беглом осмотре" тесселятор от AMD при грамотном программировании и без швыряния ресурсов на ветер - вполне юзабелен.
Запустил я OIT11 и закручинился. 8 fps в малюсеньком окошке.
Потом запустил HDRToneMappingCS11. Там есть опция полноэкранного блюра. в 1280x1024 через Pixel Shader получается 340 fps, через Compute Shader - 11 fps. Проблема явно в драйверах, поэтому с полноценным A-буфером и другими экспериментами в CS пока придётся обождать.
Позапускал ещё DX 10.1 сэмплы, но особенно ничего интересного в визуальном плане они не несут. HDAO этот ни к селу ни к городу, DOF 10.1, выглядящий как УГ... Кто его вообще делал???
Скачал с сайта AMD демки Mecha и Ladybug, попробовал запустить. Mecha не пошла, ругалась красным матом о невозможности создать какие-то ресурсы и т. д, рисовала чёрное окно с одним текстом. Вспомнив о проблемах с драйверами, скачал с сайта AMD декабрьские драйверы под XP/Seven, поставил. Проверил сначала OIT11 - было 8 fps, стало 12. Прогресс. Ладно, будем ждать. Зато Mecha на этот раз запустилась безо всяких проблем и показала попиксельную сортировку во всей её красе без подтормаживаний. Впрочем, fps там не отображался (от греха подальше?), надо бы каким-нить фрапсом замерить. В любом случае, там тоже используется каким-то образом CS, и работает этот driver path хорошо. Дальше я запустил Ladybug... Вау, лучший DOF который я видел! Божья коровка такая милая, ну... ну, как наша Юлька-премьерка. Детализированные текстуры, чтобы был эффект микросъёмки, и DOF на заднем плане. Новый алгоритм называется filter spreading (там в демках есть такой teaching mode, где объясняется что и как). Алгоритм был разработан совместными усилиями инженеров AMD и Калифорнийского университета в Беркли. Глядя на приятные глазу цвета, детализацию и качественный Depth-of-Field, понимаешь, что преимущества нового поколения GPU вполне реальны.
Ну вот, пожалуй, и всё на сегодня.
Вот так выглядит коробка:
Внутри видеокарта в антистатическом пакете (с громким предупреждением о необходимости в дополнительном питании для платы!), переходник Molex/6-PinPCI-E, инструкция для упоротых, диск с драйверами под XP/Vista/Seven, переходник DVI/VGA и перемычка для CrossFire:
Вот сама видеокарта в исполнении Sapphire:
А это она уже в системном блоке, с подключенным питанием:
Пыльновато тут, завтра надо бы почистить трудягу. Кстати о питании. AMD рекомендует для HD 5770 и HD 5750 блоки питания как минимум 450 W. У меня 400 W, поэтому были небольшие опасения, что этого может не хватить. Хватило :) В принципе, даже в минимальные требования производителем в таких случаях закладывается определённый "запас", на случай если у пользователя на БП уже висит несколько HDD, 4 гига RAM и т. д. У меня системник этим необременён, поэтому по этому поводу я особенно не волновался.
А вот мои предыдущие видеокарты:
Слева направо: GeForce 6600 256MB (покупалась изначально с компьютером), Radeon X1300 128MB (приобретена исключительно в целях отладки OpenGL-кода), Radeon HD 2400 256MB (приобретена с целью писать код по Direct3D 10).
Значит установил я драйверы и начал тестировать сэмплы из DirectX 11 SDK.
Тесселяция работает замечательно, провалов в скорости, как это было с GS, нет. Я погонял разные демки (их в SDK 3). Например, чайник в демке PNTriangles11 показывает 1620 fps без тесселяции и 250 fps при уровне тесселяции 19. В примере SubD11 скорость падает быстрее, возможно из-за анимации или просто неоптимально написано. Но в общем при "беглом осмотре" тесселятор от AMD при грамотном программировании и без швыряния ресурсов на ветер - вполне юзабелен.
Запустил я OIT11 и закручинился. 8 fps в малюсеньком окошке.
Потом запустил HDRToneMappingCS11. Там есть опция полноэкранного блюра. в 1280x1024 через Pixel Shader получается 340 fps, через Compute Shader - 11 fps. Проблема явно в драйверах, поэтому с полноценным A-буфером и другими экспериментами в CS пока придётся обождать.
Позапускал ещё DX 10.1 сэмплы, но особенно ничего интересного в визуальном плане они не несут. HDAO этот ни к селу ни к городу, DOF 10.1, выглядящий как УГ... Кто его вообще делал???
Скачал с сайта AMD демки Mecha и Ladybug, попробовал запустить. Mecha не пошла, ругалась красным матом о невозможности создать какие-то ресурсы и т. д, рисовала чёрное окно с одним текстом. Вспомнив о проблемах с драйверами, скачал с сайта AMD декабрьские драйверы под XP/Seven, поставил. Проверил сначала OIT11 - было 8 fps, стало 12. Прогресс. Ладно, будем ждать. Зато Mecha на этот раз запустилась безо всяких проблем и показала попиксельную сортировку во всей её красе без подтормаживаний. Впрочем, fps там не отображался (от греха подальше?), надо бы каким-нить фрапсом замерить. В любом случае, там тоже используется каким-то образом CS, и работает этот driver path хорошо. Дальше я запустил Ladybug... Вау, лучший DOF который я видел! Божья коровка такая милая, ну... ну, как наша Юлька-премьерка. Детализированные текстуры, чтобы был эффект микросъёмки, и DOF на заднем плане. Новый алгоритм называется filter spreading (там в демках есть такой teaching mode, где объясняется что и как). Алгоритм был разработан совместными усилиями инженеров AMD и Калифорнийского университета в Беркли. Глядя на приятные глазу цвета, детализацию и качественный Depth-of-Field, понимаешь, что преимущества нового поколения GPU вполне реальны.
Ну вот, пожалуй, и всё на сегодня.
среда, 23 декабря 2009 г.
Аватар
Посмотрел этот 230 000 000-нный сабж. Фильм прекрасен, уплаченные за билет деньги жалко не будет. Желательно смотреть в 3D (хотя эффект временами спорный), если их есть у вас, конечно :)
P.S. Раздражают ублюдочные кинокритики, в жизни ничего хорошего не создавшие и позволяющие себе критиковать работу Мастера.
P.S. Раздражают ублюдочные кинокритики, в жизни ничего хорошего не создавшие и позволяющие себе критиковать работу Мастера.
вторник, 22 декабря 2009 г.
A-buffer/OIT through SM 5.0
Одним из преимуществ Direct3D 11 является то, что в его рамках появляется возможность решить старую проблему компьютерной графики - вывод прозрачных поверхностей без предварительной сортировки геометрии. Возможное решение - сортировка на уровне пикселей - была недоступна на потребительском железе (FFP или программируемом), вплоть до Direct3D 10.
В Direct3D 10 появилась возможность сортировки ограниченного кол-ва пикселей с использованием stencil-routed K-buffer. Данный метод использует отдельные сэмплы текстуры для хранения данных, кол-во сохраняемых фрагментов ограничиваются максимальным уровнем MSAA. Таким образом в один пиксель можно записать до 8 различных фрагментов (цвета, глубины). Этого явно недостаточно, поэтому метод подразумевает использование дополнительных текстур и нескольких проходов по геометрии, чтобы можно было "захватить" больше фрагментов. Известный пример с зелёным драконом из NVIDIA SDK 10 использует два прохода. Кроме досадной необходимости использовать множество проходов, метод требует подготовки/копирования буфера трафарета для роутинга фрагментов, что тоже бьёт по производительности.
С использованием Compute Shader, тесно интегрированного с остальным графическим API, и поддержкой read/write операций в железе стало возможным реализовать полноценный A-буфер, т. е. буфер, способный аккумулировать неограниченное кол-во значений для одного пикселя (L. Carpenter, 1984). Говоря "неограниченное", я, конечно, подразумеваю под этим словом большое, но всё же конечное кол-во значений, т. к. размер A-буфера на практике лимитирован, ну, хотя бы доступной видеопамятью. Однако досадных ограничений вроде MSAA=8 этот метод не имеет.
В SDK доступен небольшой сэмпл, реализующий Order Independent Transparency (OIT). Рассмотрим, как он работает.
Используются новые объекты HLSL 11: RWTexture2D для операций чтения/записи с двумерными массивами и RWBuffer для операций с одномерными. Работа осуществляется в Pixel и Compute Shader. Сперва прозрачная геометрия рисуется без depth-теста в uint буфер, в котором подсчитывается overdraw для каждого фрагмента изображения:
Затем в Compute Shader выполняется так называемый scan получившегося буфера, а точнее - одна из его операций prefix sum. Это хорошо известный и распространённый алгоритм в GPGPU, про него можно почитать в Википедии (Prefix sum) и в GPU Gems 3 (Chapter 39. Parallel Prefix Sum (Scan) with CUDA). Смысл этой операции заключается в том, чтобы за O(n) операций найти для каждого элемента списка сумму предшествующих ему элементов. Например, у нас есть буфер, в котором каждый элемент содержит какое-то заданное значение (в рамках нашей задачи - overdraw):
После parallel prefix sum в Compute Shader получается такой буфер:
Последний элемент содержит сумму всех элементов в буфере.
Prefix sum разбит на два этапа: в первом 2D-текстура копируется в RW-буфер, во втором происходит собственно суммирование. Чтобы подсчитать prefix sum для всего буфера, используется log(N) проходов, т. е. например, для буфера 1024x1024 необходимо 19 проходов. Первое суммирование осуществляется на первом этапе (копировании), поэтому во втором выполняется только log(N)-1 проходов.
Полученные суммы используются для размещения значений цвета и глубины прозрачной геометрии в линейном "глубоком" буфере (deep buffer, или, чтобы следовать придуманной ранее терминологии, A-buffer). Память для deep-буфера конкретно в сэмпле SDK выделяется с расчётом способности хранить до 8 фрагментов на каждый пиксель (это в среднем, в реальности overdraw крайне неравномерен, т. е. для одних пикселей это 0-4, для других 8-10, для третьих 20-30 и т. д. Главное, выделять память таким образом, чтобы по возможности разместились все данные). Размещение происходит в пиксельном шейдере, fragment count буфер перед этим очищается в ноль. Код шейдера:
Из кода легко видеть, что в deep-буфере фрагменты каждого пикселя изображения выстраиваются в виде цепочек, одна за другой.
Затем в Compute Shader каждая такая цепочка сортируется bitonic sort-ом, все её элементы смешиваются по альфе, результат записывается в пиксель, соответствующий этой цепочке.
И всё :)
P.S. Существует демо Mecha от ATI, но т. к. утилиты отладки DX11-программ только на подходе, нет возможности добраться до шейдеров этого демо. Позже я обязательно это сделаю и сравню их код с кодом в OIT11.
P.P.S. Кстати, подобный алгоритм А-буфера уже запатентован Microsoft: PREFIX SUM PASS TO LINEARIZE A-BUFFER STORAGE. Впрочем, подобный патент не выдержит ни одного судебного разбирательства, ибо никто не может запретить кому-либо передвигать биты и байты в кристалле так, как ему это заблагорассудится. Знаете о патенте на колесо австралийца Джона Кеога?
В Direct3D 10 появилась возможность сортировки ограниченного кол-ва пикселей с использованием stencil-routed K-buffer. Данный метод использует отдельные сэмплы текстуры для хранения данных, кол-во сохраняемых фрагментов ограничиваются максимальным уровнем MSAA. Таким образом в один пиксель можно записать до 8 различных фрагментов (цвета, глубины). Этого явно недостаточно, поэтому метод подразумевает использование дополнительных текстур и нескольких проходов по геометрии, чтобы можно было "захватить" больше фрагментов. Известный пример с зелёным драконом из NVIDIA SDK 10 использует два прохода. Кроме досадной необходимости использовать множество проходов, метод требует подготовки/копирования буфера трафарета для роутинга фрагментов, что тоже бьёт по производительности.
С использованием Compute Shader, тесно интегрированного с остальным графическим API, и поддержкой read/write операций в железе стало возможным реализовать полноценный A-буфер, т. е. буфер, способный аккумулировать неограниченное кол-во значений для одного пикселя (L. Carpenter, 1984). Говоря "неограниченное", я, конечно, подразумеваю под этим словом большое, но всё же конечное кол-во значений, т. к. размер A-буфера на практике лимитирован, ну, хотя бы доступной видеопамятью. Однако досадных ограничений вроде MSAA=8 этот метод не имеет.
В SDK доступен небольшой сэмпл, реализующий Order Independent Transparency (OIT). Рассмотрим, как он работает.
Используются новые объекты HLSL 11: RWTexture2D для операций чтения/записи с двумерными массивами и RWBuffer для операций с одномерными. Работа осуществляется в Pixel и Compute Shader. Сперва прозрачная геометрия рисуется без depth-теста в uint буфер, в котором подсчитывается overdraw для каждого фрагмента изображения:
RWTexture2D(Что интересно, в HLSL 11 у объектов появился operator [], который можно использовать как для чтения значений (вместо Buffer.Load()), так и для записи).fragmentCount : register( u1 );
void FragmentCountPS( SceneVS_Output input)
{
fragmentCount[input.pos.xy]++;
}
Затем в Compute Shader выполняется так называемый scan получившегося буфера, а точнее - одна из его операций prefix sum. Это хорошо известный и распространённый алгоритм в GPGPU, про него можно почитать в Википедии (Prefix sum) и в GPU Gems 3 (Chapter 39. Parallel Prefix Sum (Scan) with CUDA). Смысл этой операции заключается в том, чтобы за O(n) операций найти для каждого элемента списка сумму предшествующих ему элементов. Например, у нас есть буфер, в котором каждый элемент содержит какое-то заданное значение (в рамках нашей задачи - overdraw):
После parallel prefix sum в Compute Shader получается такой буфер:
Последний элемент содержит сумму всех элементов в буфере.
Prefix sum разбит на два этапа: в первом 2D-текстура копируется в RW-буфер, во втором происходит собственно суммирование. Чтобы подсчитать prefix sum для всего буфера, используется log(N) проходов, т. е. например, для буфера 1024x1024 необходимо 19 проходов. Первое суммирование осуществляется на первом этапе (копировании), поэтому во втором выполняется только log(N)-1 проходов.
Полученные суммы используются для размещения значений цвета и глубины прозрачной геометрии в линейном "глубоком" буфере (deep buffer, или, чтобы следовать придуманной ранее терминологии, A-buffer). Память для deep-буфера конкретно в сэмпле SDK выделяется с расчётом способности хранить до 8 фрагментов на каждый пиксель (это в среднем, в реальности overdraw крайне неравномерен, т. е. для одних пикселей это 0-4, для других 8-10, для третьих 20-30 и т. д. Главное, выделять память таким образом, чтобы по возможности разместились все данные). Размещение происходит в пиксельном шейдере, fragment count буфер перед этим очищается в ноль. Код шейдера:
void FillDeepBufferPS( SceneVS_Output In )
{
uint x = In.pos.x;
uint y = In.pos.y;
uint prefixSumPos = y * FrameWidth + x;
uint deepBufPos;
if (prefixSumPos == 0)
deepBufPos = fragmentCount[In.pos.xy];
else
deepBufPos = prefixSum[prefixSumPos-1] + fragmentCount[In.pos.xy];
deepBufDepth[deepBufPos] = In.pos.z;
deepBufColor[deepBufPos] = clamp(In.color, 0, 1)*255;
fragmentCount[In.pos.xy]++;
}
Из кода легко видеть, что в deep-буфере фрагменты каждого пикселя изображения выстраиваются в виде цепочек, одна за другой.
Затем в Compute Shader каждая такая цепочка сортируется bitonic sort-ом, все её элементы смешиваются по альфе, результат записывается в пиксель, соответствующий этой цепочке.
И всё :)
P.S. Существует демо Mecha от ATI, но т. к. утилиты отладки DX11-программ только на подходе, нет возможности добраться до шейдеров этого демо. Позже я обязательно это сделаю и сравню их код с кодом в OIT11.
P.P.S. Кстати, подобный алгоритм А-буфера уже запатентован Microsoft: PREFIX SUM PASS TO LINEARIZE A-BUFFER STORAGE. Впрочем, подобный патент не выдержит ни одного судебного разбирательства, ибо никто не может запретить кому-либо передвигать биты и байты в кристалле так, как ему это заблагорассудится. Знаете о патенте на колесо австралийца Джона Кеога?
Подписаться на:
Сообщения (Atom)






