Обновить

Комментарии 6

Генерировать мипы за один вызов хорошо, но есть опенсорсные проверенные решения:
https://gpuopen.com/manuals/fidelityfx_sdk/techniques/single-pass-downsampler/
Bank conflicts, Morton z code, gather, все это когда-то было важно но на современном железе это все можно выкинуть и написать влоб и будет быстрее. Проверте перфу прежде чем это все использовать, это все могло быть написанно во времена динозавров.
Округление до степени двойки - тоже очень сомнительно, этот буфер можно будет использовать только для hzb. А например Ssr, Ao вы тоже скорее скорее всего захотите делать не в полном разрешниии и там тоже потребуется downsampled depth только нормальная, не степени двойки. В итоге придется готовить отдельный downsampled depth mip chain для остальных эффектов что двойная работа.

Для SSR и AO такой HZB тоже можно использовать. В Unreal, например, текстура SSRTAmbientOcclusion создается с использованием такого POT HZB, и создается в half resolution. Там просто маппятся UV координаты.

Вот, например, как вычисляется один из параметров, который используется для маппинга:
const FVector2D HZBUvFactor(float(InViewRect.Width()) / float(2 * HZBMipmap0Size.X), float(InViewRect.Height()) / float(2 * HZBMipmap0Size.Y));
Исходник тут: Engine\Source\Runtime\Renderer\Private\HZB.cpp

Согласен с тем что есть опенсорсные решения, вроде FidelityFX SPD. В статье я хотел разобрать именно реализацию HZB из Unreal Engine, а что использовать в итоге уже каждый решает сам. Кстати, даже у SPD есть нюансы с размерами текстур - при работе с нечетными текстурами он отбрасывает какие-то значения (как указано в разделе Limitations).

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

Получается для мобильного профиля в UE другой подход?

Для мобилок используется вариант генерации HZB через пиксельный шейдер. Там такой простенький код:

void HZBBuildPS(float4 SvPosition : SV_POSITION, out float4 OutColor : SV_Target0)
{
	float2 BufferUV = SvPosition.xy * DispatchThreadIdToBufferUV.xy + DispatchThreadIdToBufferUV.zw;
	float4 DeviceZ = Gather4(ParentTextureMip, ParentTextureMipSampler, BufferUV);

	float FurthestDeviceZ = min(min(DeviceZ.x, DeviceZ.y), min(DeviceZ.z, DeviceZ.w));

	OutColor = FurthestDeviceZ;
}

Для всех остальных устройств можно выбирать какой вариант использовать: через compute шейдер или пиксельный шейдер. Например, через консоль:

r.HZB.BuildUseCompute

Как в UE не знаю, но я тестировал разные варианты и на всех встройках быстрее через пиксельный и с sampler min фильтром вместо gather4. Округление до степени 2 тоже быстрее работает, хоть и меньше точности.

Тут мой ресерч по HiZ.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации