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

DepthStencil: переход от 2-pass к 1-pass

Страницы: 1 2 3 4 Следующая »
#0
(Правка: 12:49) 12:21, 22 мар. 2019

Добрый день! Работаю с существующим не игровым 3D-движком на D3D11. Оптимизирую работу с картой глубины, которая сейчас строится в отдельном проходе. Хочу прояснить ряд моментов, но обо всём по порядку.

Требования: 
1. Чтение 32-битной глубины из CPU (для определения мировых координат курсора).
2. Наличие трафарета.
3. Максимально быстрая отрисовка (т.е. избавиться от второго прохода).
4. Визуализация текстуры глубины и трафарета (больше для отладки и из любопытства).

Вижу два пути решения: 1) использовать стандартную DepthStencil-текстуру, 2) создавать дополнительную RenderTarget-текстуру с модификацией всех шейдеров. В первом случае вижу экономию ресурсов, во втором — больший контроль над ситуацией.

Реализовал первый вариант, остались вопросы:
1. Влияет ли DXGI-формат текстуры на содержимое при копировании? Ожидал, что в DS-буфере будет логарифмическая глубина, но достаю привычные линейные значения. Как это возможно?
2. При визуализации на видеокарте рисуется только красный канал с глубиной, причём больше похоже на логарифм (много красного). При экспорте в BMP с помощью WIC-кодека получаю красно-зелёное полосатое изображение (градиенты от красного к зелёному). Трафарет не рисуется нигде. В идеале хочу картинку, где есть и глубина, и трафарет, и чтобы она выглядела одинаково в BMP и на экране. Как это сделать правильно?
3. Какие ещё нюансы и подводные камни есть в этих вариантах? Какой вариант предпочтительнее в перспективе (планируются PSSM-тени, буфер для выделения объектов)?

Весь код привести не могу, ключевые моменты:
— DS-текстура создаётся в формате DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS;
— включается в конвейер как DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
— копируется в ОЗУ как DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS (пробовал также DXGI_FORMAT_R32G8X24_TYPELESS — в этом формате не лезет обратно в видеокарту);
— кодируется в BMP как GUID_WICPixelFormat64bppRGBA -> GUID_WICPixelFormat32bppBGRA.

Заранее спасибо за любые подсказки!


#1
15:30, 22 мар. 2019

1)  Влияет

2) DXGI_FORMAT_R32G8X24_TYPELESS и другие  в R32 - RED как раз и хранится глубина.

3) ...

#2
16:06, 22 мар. 2019

VsM
> Ожидал, что в DS-буфере будет логарифмическая глубина
С чего бы вдруг ей там оказаться?

#3
18:06, 22 мар. 2019

VsM
> Ожидал, что в DS-буфере будет логарифмическая глубина, но достаю привычные
> линейные значения. Как это возможно?

а какие значения привычные?

#4
22:43, 22 мар. 2019

VsM
В Depth buffer хранится в первом приближении 1/Z (а точнее некоторая дробно-линейная функция, подгоняющая диапазон zn-zf к диапазону целых чисел 32 бит), потому что 1/Z линейно меняется в пространстве экрана

#5
(Правка: 13:02) 12:50, 25 мар. 2019

prowkan
> С чего бы вдруг ей там оказаться?
innuendo
> а какие значения привычные?
Окей, попробую яснее изложить свои мысли.

Что было в старой реализации: была универсальная функция, которая на выходе формировала карту (текстуру) глубины/видимости/теней, под условным названием CreateDepthMap(W,H). Работала функция очень просто: вызывала отрисовку всей сцены в стандартный RenderTarget, со стандартным DepthStencil, с 2 отличиями от обычной отрисовки:
1) шейдер глобально для всех объектов подменялся на специальный (условно Depth.hlsl), который для каждого пикселя записывал  значение:

SV_TARGET = pos.z / pos.w, где 
pos = model_pos * MVP
2) для RenderTarget устанавливался формат текстуры DXGI_FORMAT_R32G32B32A32_FLOAT.
На выходе мы получали grayscale-изображение, которое корректно конвертировалось в grayscale-битмап, и также в виде grayscale-текстуры рендерилось видеокартой.

Дальше меня сбила с толку формула (2) для вычисления Fdepth из статьи: https://habr.com/ru/post/342610/ — ведь это мало похоже на формулу из "моей" старой реализации, и больше похоже на то о чём написал Aslan. Тем не менее, я как и раньше умножаю полученную экранную точку (X,Y,Depth) на матрицу MVP^-1 (с точностью до транспонирования матриц), и получаю правильную модельную точку (?!) — это момент, оставшийся для меня непонятным.

Но практических вопроса осталось два:
1. Как отрендерить "красную" DepthStencil-текстуру более контрастно, чтобы видеть на ней очертания далёких объектов, а не только те, что перед носом.
2. Как отрендерить канал трафарета (например, зелёным цветом на той же картинке).

Причём, если рендеринг буфера в формате DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS даёт красную карту глубины, я ожидаю, что DXGI_FORMAT_X32_TYPELESS_G8X24_UINT даст зелёную карту тарафарета, но это не так.

P.S. Спасибо вам всем за отклик! Давно читаю этот форум, и даже ники уже знакомы :-)

#6
14:35, 25 мар. 2019

VsM
> Как отрендерить "красную" DepthStencil-текстуру более контрастно, чтобы видеть
> на ней очертания далёких объектов, а не только те, что перед носом.
А зачем?

#7
(Правка: 15:26) 15:25, 25 мар. 2019

VsM
> 2) для RenderTarget устанавливался формат текстуры
> DXGI_FORMAT_R32G32B32A32_FLOAT.

Это же не depthStencil ?

> со стандартным DepthStencil, с 2 отличиями от обычной отрисовки:

когда делаешь в depthStencil, то color не нужен

есть графический отладчик - там всё видно

#8
7:35, 26 мар. 2019

VsM
> для RenderTarget устанавливался формат текстуры DXGI_FORMAT_R32G32B32A32_FLOAT.
боль. всё упрётся в филлрейт. тем более если ты рендеришь только глубину, вот глубину и рендерь, color attachment'ы тебе тут вообще не нужны.

#9
(Правка: 29 мар. 2019, 11:58) 14:02, 28 мар. 2019

innuendo
> а какие значения привычные?
С этим разобрался. Привычные значения — это линейная функция от 1/z, что прямо следует из устройства Proj-матрицы и содержания w-компоненты. Поэтому всё как работало раньше, так и работает сейчас. На языке формул:

pos.z / pos.w = (az + b)/z = b*1/z + a

maks242
> А зачем?
Ок, вопрос отпал.
UPD: С форматами текстур и их конвертацией разобрался.

innuendo
> Это же не depthStencil ?
Suslik
> боль. всё упрётся в филлрейт. тем более если ты рендеришь только глубину, вот
> глубину и рендерь, color attachment'ы тебе тут вообще не нужны.
Такова старая реализация: фактически построение Z-буфера выполняется 3 раза за кадр (1 раз в RT и 2 раза в DS). Это именно то, от чего я избавился в новой реализации :-)

innuendo
> есть графический отладчик - там всё видно
Типа RenderDoc? Использую его тоже, хотя это менее удобно, чем встроенные инструменты.

———
Далее новые для меня факты, которые пока не затрагивал:
1. Вычитал, что в случае с FLOAT получается дополнительное уплотнение вблизи Znear, из-за чего падает точность везде, кроме как вблизи.
2. Для больших пространств рекомендуется Float-буфер + Reversed Depth. Второе заключается в небольшой модификации матрицы.

Но тут небольшой офтоп. В моём случае матрица изначально имеет странный вид:

m33 = -f/(f-n) вместо f/(f-n)
m34 = -1 вместо 1
Что могут означать эти минусы? Попытка их убрать приводит к проблемам отображения и управления.

#10
(Правка: 6:15) 6:12, 4 апр. 2019

После перехода на новую реализацию возникли вопросы по антиалиасингу:
1. Верно ли я понял, что SampleDesc должен быть одинаковым для всех RenderTarget- и DepthStencil-текстур внутри MRT, а для всех представлений нужно выставить ViewDimension = ..._DIMENSION_TEXTURE2DMS?
2. Как скопировать MS-текстуру глубины, если целевой формат DXGI_FORMAT_R24_UNORM_X8_TYPELESS не поддерживает MSAA (возникает ошибка несоответствия типов при вызове CopySubresourceRegion)?

#11
6:37, 4 апр. 2019

VsM
> 1. Верно ли я понял, что SampleDesc должен быть одинаковым для всех
> RenderTarget- и DepthStencil-текстур внутри MRT, а для всех представлений нужно
> выставить ViewDimension = ..._DIMENSION_TEXTURE2DMS?
конечно

VsM
> 2. Как скопировать MS-текстуру глубины, если целевой формат
> DXGI_FORMAT_R24_UNORM_X8_TYPELESS не поддерживает MSAA (возникает ошибка
> несоответствия типов при вызове CopySubresourceRegion)?
нужно использовать операции вроде ResolveSubresource(), либо пиши в шейдере вручную усреднённые значения субпикселей.

#12
(Правка: 7:36) 6:52, 4 апр. 2019

Suslik
> нужно использовать операции вроде ResolveSubresource(), либо пиши в шейдере
> вручную усреднённые значения субпикселей.
Спасибо, буду изучать! Наивно полагал, что MSAA не изменяет количество пикселей, и копирование пиксель-в-пиксель не должно представлять проблемы.

P.S. Сделал ResolveSubresource, указал Typed-формат (D24_UNORM_S8_UINT) по документации. Пока жёстко ругается:
1) на указанный формат ("is never able to resolve MS resources");
2) на флаг BIND_DEPTH_STENCIL у исходной текстуры.

#13
7:49, 4 апр. 2019

VsM

код dxsdk

#14
(Правка: 8:03) 8:01, 4 апр. 2019

innuendo
> код dxsdk
Можно чуть точнее? Знаю что в SDK 2010 есть пример AntiAlias 2004 года, написанный на DirectX 9, о нём ли речь? Или смотреть в сторону TextureConverter?

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