vault backup: 2025-01-23 16:43:55

This commit is contained in:
BlueRose 2025-01-23 16:43:55 +08:00
parent 7cd1d59187
commit 3e605c3668

View File

@ -129,28 +129,48 @@ END_SHADER_PARAMETER_STRUCT()
# ToneMapping # ToneMapping
- UE5 官方文档High Dynamic Range Display Output:https://dev.epicgames.com/documentation/en-us/unreal-engine/high-dynamic-range-display-output-in-unreal-engine?application_version=5.3
- ACES官方文档:https://docs.acescentral.com/#aces-white-point-derivation
- [现代游戏图形中的sRGB18%灰-中性灰的定义](https://zhuanlan.zhihu.com/p/654557489) - [现代游戏图形中的sRGB18%灰-中性灰的定义](https://zhuanlan.zhihu.com/p/654557489)
- UE4/UE5和ACES工作流程:https://zhuanlan.zhihu.com/p/660965710 - 游戏中的后处理渲染流水线、ACES、Tonemapping和 HDR:https://zhuanlan.zhihu.com/p/118272193
- ACES
- ACES 2065-1:ACES 是成像行业的标准,它创建了一个三角形色域,涵盖了我们可以看到的所有光谱轨迹。 ## ACES 色彩空间
- AP0 / AP1这个“精确”的数字基数包括不可见区域和负数称为 AP0。为了使其更易于使用AP1 已改进为不包含负数。 ACES 标准定义了一些色域和色彩空间如下:
- ACEScg色域三角形已被重新定义以改善 ACES2065-1 中的不便。虽然我们不得不放弃一些颜色,但它仍然覆盖了相当大的人类可见区域。
- ACEScctACEScc (Color Correction Space) 色彩校正空间的 AP1 版本。ACEScc [色彩空间](https://zhida.zhihu.com/search?content_id=235009763&content_type=Article&match_order=3&q=%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4&zhida_source=entity)定义略大于 ITU Rec.2020 色彩空间,并进行对数编码,以改进色彩校正器和分级工具中的使用,类似于 Cineon 文件的脚趾行为。 色域有:
- Rec.709:我们的“标准”显示器可以显示的 Rec.709 色域只是非常宽的色彩工作空间的一小部分。 - AP0包含所有颜色的色域
- AP1工作色域
色彩空间有:
- ACES2065-1/ACES 色彩空间,使用 AP0 色域,用于存储颜色,处理色彩转换
- ACEScg 色彩空间,使用 AP1 色域,一个线性的渲染计算工作空间
- ACEScc 色彩空间AP1 色域,指数空间,用于调色
- ACEScct 色彩空间,使用 AP1 色域,和 ACEScc 类似,只是曲线略有不同,适用于不同的场景
## UE5的ACES流程
ACES Viewing Transform在查看流程中将按以下顺序进行
- **Look Modification Transform (LMT)** - 这部分抓取应用了创意"外观"颜色分级和矫正的ACES颜色编码图像 输出由ACES和Reference Rendering TransformRRT及Output Device TransformODT渲染的图像。
- **Reference Rendering Transform (RRT)** - 之后,这部分抓取参考场景的颜色值,将它们转换为参考显示。 在此流程中,它使渲染图像不再依赖于特定显示器,反而能保证它输出到特定显示器时拥有正确而宽泛的色域和动态范围(尚未创建的图像同样如此)。
- **Output Device Transform (ODT)** - 最后这部分抓取RRT的HDR数据输出将其与它们能够显示的不同设备和色彩空间进行比对。 因此每个目标需要将其自身的ODT与Rec709、Rec2020、DCI-P3等进行比对。
## ToneMapping种类 ## ToneMapping种类
- ShaderToy效果演示: https://www.shadertoy.com/view/McG3WW - ShaderToy效果演示:
- ACES - https://www.shadertoy.com/view/McG3WW
- Narkowicz 2015, "ACES Filmic Tone Mapping Curve" - ACES
- https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ - Narkowicz 2015, "ACES Filmic Tone Mapping Curve"
- PBR Neutral https://modelviewer.dev/examples/tone-mapping - https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
- Uncharted tonemapping - PBR Neutral https://modelviewer.dev/examples/tone-mapping
- http://filmicworlds.com/blog/filmic-tonemapping-operators/ - Uncharted tonemapping
- https://www.gdcvault.com/play/1012351/Uncharted-2-HDR - http://filmicworlds.com/blog/filmic-tonemapping-operators/
- AgX - https://www.gdcvault.com/play/1012351/Uncharted-2-HDR
- https://github.com/sobotka/AgX - AgX
- https://www.shadertoy.com/view/cd3XWr - https://github.com/sobotka/AgX
- https://www.shadertoy.com/view/cd3XWr
- https://www.shadertoy.com/view/lslGzl
- https://www.shadertoy.com/view/Xstyzn
- GT-ToneMapping:https://github.com/yaoling1997/GT-ToneMapping
- CCA-ToneMapping:?
## UE中的相关实现 ## UE中的相关实现
UE4版本的笔记[[UE4 ToneMapping]] UE4版本的笔记[[UE4 ToneMapping]]
@ -223,3 +243,116 @@ if (SamplerBuffer.CustomStencil > 1.0f && abs(SamplerBuffer.CustomDepth - Sample
} }
//BlueRose Modify End //BlueRose Modify End
``` ```
### TextureArray参考
FIESAtlasAddTextureCS::FParameters
- SHADER_PARAMETER_TEXTURE_ARRAY
- /Engine/Private/IESAtlas.usf
```c++
static void AddSlotsPassCS(
FRDGBuilder& GraphBuilder,
FGlobalShaderMap* ShaderMap,
const TArray<FAtlasSlot>& Slots,
FRDGTextureRef& OutAtlas)
{
FRDGTextureUAVRef AtlasTextureUAV = GraphBuilder.CreateUAV(OutAtlas);
TShaderMapRef<FIESAtlasAddTextureCS> ComputeShader(ShaderMap);
// Batch new slots into several passes
const uint32 SlotCountPerPass = 8u;
const uint32 PassCount = FMath::DivideAndRoundUp(uint32(Slots.Num()), SlotCountPerPass);
for (uint32 PassIt = 0; PassIt < PassCount; ++PassIt)
{
const uint32 SlotOffset = PassIt * SlotCountPerPass;
const uint32 SlotCount = SlotCountPerPass * (PassIt+1) <= uint32(Slots.Num()) ? SlotCountPerPass : uint32(Slots.Num()) - (SlotCountPerPass * PassIt);
FIESAtlasAddTextureCS::FParameters* Parameters = GraphBuilder.AllocParameters<FIESAtlasAddTextureCS::FParameters>();
Parameters->OutAtlasTexture = AtlasTextureUAV;
Parameters->AtlasResolution = OutAtlas->Desc.Extent;
Parameters->AtlasSliceCount = OutAtlas->Desc.ArraySize;
Parameters->ValidCount = SlotCount;
for (uint32 SlotIt = 0; SlotIt < SlotCountPerPass; ++SlotIt)
{
Parameters->InTexture[SlotIt] = GSystemTextures.BlackDummy->GetRHI();
Parameters->InSliceIndex[SlotIt].X = InvalidSlotIndex;
Parameters->InSampler[SlotIt] = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
}
for (uint32 SlotIt = 0; SlotIt<SlotCount;++SlotIt)
{
const FAtlasSlot& Slot = Slots[SlotOffset + SlotIt];
check(Slot.SourceTexture);
Parameters->InTexture[SlotIt] = Slot.GetTextureRHI();
Parameters->InSliceIndex[SlotIt].X = Slot.SliceIndex;
}
const FIntVector DispatchCount = FComputeShaderUtils::GetGroupCount(FIntVector(Parameters->AtlasResolution.X, Parameters->AtlasResolution.Y, SlotCount), FIntVector(8, 8, 1));
FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("IESAtlas::AddTexture"), ComputeShader, Parameters, DispatchCount);
}
GraphBuilder.UseExternalAccessMode(OutAtlas, ERHIAccess::SRVMask);
}
```
```c++
Texture2D<float4> InTexture_0;
Texture2D<float4> InTexture_1;
Texture2D<float4> InTexture_2;
Texture2D<float4> InTexture_3;
Texture2D<float4> InTexture_4;
Texture2D<float4> InTexture_5;
Texture2D<float4> InTexture_6;
Texture2D<float4> InTexture_7;
uint4 InSliceIndex[8];
SamplerState InSampler_0;
SamplerState InSampler_1;
SamplerState InSampler_2;
SamplerState InSampler_3;
SamplerState InSampler_4;
SamplerState InSampler_5;
SamplerState InSampler_6;
SamplerState InSampler_7;
int2 AtlasResolution;
uint AtlasSliceCount;
uint ValidCount;
RWTexture2DArray<float> OutAtlasTexture;
[numthreads(8, 8, 1)]
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
if (all(DispatchThreadId.xy < uint2(AtlasResolution)))
{
const uint2 DstPixelPos = DispatchThreadId.xy;
uint DstSlice = 0;
const float2 SrcUV = (DstPixelPos + 0.5) / float2(AtlasResolution);
const uint SrcSlice = DispatchThreadId.z;
if (SrcSlice < ValidCount)
{
float Color = 0;
switch (SrcSlice)
{
case 0: Color = InTexture_0.SampleLevel(InSampler_0, SrcUV, 0).x; DstSlice = InSliceIndex[0].x; break;
case 1: Color = InTexture_1.SampleLevel(InSampler_1, SrcUV, 0).x; DstSlice = InSliceIndex[1].x; break;
case 2: Color = InTexture_2.SampleLevel(InSampler_2, SrcUV, 0).x; DstSlice = InSliceIndex[2].x; break;
case 3: Color = InTexture_3.SampleLevel(InSampler_3, SrcUV, 0).x; DstSlice = InSliceIndex[3].x; break;
case 4: Color = InTexture_4.SampleLevel(InSampler_4, SrcUV, 0).x; DstSlice = InSliceIndex[4].x; break;
case 5: Color = InTexture_5.SampleLevel(InSampler_5, SrcUV, 0).x; DstSlice = InSliceIndex[5].x; break;
case 6: Color = InTexture_6.SampleLevel(InSampler_6, SrcUV, 0).x; DstSlice = InSliceIndex[6].x; break;
case 7: Color = InTexture_7.SampleLevel(InSampler_7, SrcUV, 0).x; DstSlice = InSliceIndex[7].x; break;
}
// Ensure there is no NaN value
Color = -min(-Color, 0);
DstSlice = min(DstSlice, AtlasSliceCount-1);
OutAtlasTexture[uint3(DstPixelPos, DstSlice)] = Color;
}
}
}
```