diff --git a/03-UnrealEngine/卡通渲染相关资料/渲染功能/PostProcess/ToonPostProcess.md b/03-UnrealEngine/卡通渲染相关资料/渲染功能/PostProcess/ToonPostProcess.md index 8b674f5..23c5fca 100644 --- a/03-UnrealEngine/卡通渲染相关资料/渲染功能/PostProcess/ToonPostProcess.md +++ b/03-UnrealEngine/卡通渲染相关资料/渲染功能/PostProcess/ToonPostProcess.md @@ -129,28 +129,48 @@ END_SHADER_PARAMETER_STRUCT() # 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) -- UE4/UE5和ACES工作流程:https://zhuanlan.zhihu.com/p/660965710 - - ACES - - ACES 2065-1:ACES 是成像行业的标准,它创建了一个三角形色域,涵盖了我们可以看到的所有光谱轨迹。 - - AP0 / AP1:这个“精确”的数字基数,包括不可见区域和负数,称为 AP0。为了使其更易于使用,AP1 已改进为不包含负数。 - - ACEScg:色域三角形已被重新定义,以改善 ACES2065-1 中的不便。虽然我们不得不放弃一些颜色,但它仍然覆盖了相当大的人类可见区域。 - - ACEScct:ACEScc (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 色域只是非常宽的色彩工作空间的一小部分。 +- 游戏中的后处理(三):渲染流水线、ACES、Tonemapping和 HDR:https://zhuanlan.zhihu.com/p/118272193 + +## ACES 色彩空间 +ACES 标准定义了一些色域和色彩空间如下: + +色域有: +- 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 Transform(RRT)及Output Device Transform(ODT)渲染的图像。 +- **Reference Rendering Transform (RRT)** - 之后,这部分抓取参考场景的颜色值,将它们转换为参考显示。 在此流程中,它使渲染图像不再依赖于特定显示器,反而能保证它输出到特定显示器时拥有正确而宽泛的色域和动态范围(尚未创建的图像同样如此)。 +- **Output Device Transform (ODT)** - 最后,这部分抓取RRT的HDR数据输出,将其与它们能够显示的不同设备和色彩空间进行比对。 因此,每个目标需要将其自身的ODT与Rec709、Rec2020、DCI-P3等进行比对。 ## ToneMapping种类 -- ShaderToy效果演示: https://www.shadertoy.com/view/McG3WW - - ACES - - Narkowicz 2015, "ACES Filmic Tone Mapping Curve" - - https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ - - PBR Neutral https://modelviewer.dev/examples/tone-mapping - - Uncharted tonemapping - - http://filmicworlds.com/blog/filmic-tonemapping-operators/ - - https://www.gdcvault.com/play/1012351/Uncharted-2-HDR - - AgX - - https://github.com/sobotka/AgX - - https://www.shadertoy.com/view/cd3XWr - +- ShaderToy效果演示: + - https://www.shadertoy.com/view/McG3WW + - ACES + - Narkowicz 2015, "ACES Filmic Tone Mapping Curve" + - https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ + - PBR Neutral https://modelviewer.dev/examples/tone-mapping + - Uncharted tonemapping + - http://filmicworlds.com/blog/filmic-tonemapping-operators/ + - https://www.gdcvault.com/play/1012351/Uncharted-2-HDR + - AgX + - 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中的相关实现 UE4版本的笔记:[[UE4 ToneMapping]] @@ -222,4 +242,117 @@ if (SamplerBuffer.CustomStencil > 1.0f && abs(SamplerBuffer.CustomDepth - Sample OutColor = TonemapCommonPS(UV, InVignette, GrainUV, ScreenPos, FullViewUV, SvPosition, Luminance); } //BlueRose Modify End +``` + +### TextureArray参考 +FIESAtlasAddTextureCS::FParameters: +- SHADER_PARAMETER_TEXTURE_ARRAY +- /Engine/Private/IESAtlas.usf + +```c++ +static void AddSlotsPassCS( + FRDGBuilder& GraphBuilder, + FGlobalShaderMap* ShaderMap, + const TArray& Slots, + FRDGTextureRef& OutAtlas) +{ + FRDGTextureUAVRef AtlasTextureUAV = GraphBuilder.CreateUAV(OutAtlas); + TShaderMapRef 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(); + 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::GetRHI(); + } + for (uint32 SlotIt = 0; SlotItInTexture[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 InTexture_0; +Texture2D InTexture_1; +Texture2D InTexture_2; +Texture2D InTexture_3; +Texture2D InTexture_4; +Texture2D InTexture_5; +Texture2D InTexture_6; +Texture2D 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 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; + } + } +} ``` \ No newline at end of file