DepthOfFieldBokehBlurPass.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <PostProcess/PostProcessFeatureProcessor.h>
  9. #include <PostProcess/DepthOfField/DepthOfFieldSettings.h>
  10. #include <PostProcessing/DepthOfFieldPencilMap.h>
  11. #include <PostProcessing/DepthOfFieldBokehBlurPass.h>
  12. #include <AzCore/Math/MathUtils.h>
  13. #include <Atom/RPI.Public/RenderPipeline.h>
  14. #include <Atom/RPI.Public/Scene.h>
  15. #include <Atom/RPI.Public/View.h>
  16. namespace AZ
  17. {
  18. namespace Render
  19. {
  20. RPI::Ptr<DepthOfFieldBokehBlurPass> DepthOfFieldBokehBlurPass::Create(const RPI::PassDescriptor& descriptor)
  21. {
  22. RPI::Ptr<DepthOfFieldBokehBlurPass> pass = aznew DepthOfFieldBokehBlurPass(descriptor);
  23. return pass;
  24. }
  25. DepthOfFieldBokehBlurPass::DepthOfFieldBokehBlurPass(const RPI::PassDescriptor& descriptor)
  26. : RPI::FullscreenTrianglePass(descriptor)
  27. // option names from the azsl file
  28. , m_optionName("o_sampleNumber")
  29. , m_optionValues
  30. {
  31. AZ::Name("SampleNumber::Sample6"),
  32. AZ::Name("SampleNumber::Sample18"),
  33. AZ::Name("SampleNumber::Sample36"),
  34. AZ::Name("SampleNumber::Sample60")
  35. }
  36. {
  37. }
  38. void DepthOfFieldBokehBlurPass::InitializeInternal()
  39. {
  40. FullscreenTrianglePass::InitializeInternal();
  41. m_sampleNumberIndex.Reset();
  42. m_radiusMinIndex.Reset();
  43. m_radiusMaxIndex.Reset();
  44. m_sampleTexcoordsRadiusIndex.Reset();
  45. InitializeShaderVariant();
  46. }
  47. void DepthOfFieldBokehBlurPass::InitializeShaderVariant()
  48. {
  49. AZ_Assert(m_shader != nullptr, "DepthOfFieldBokehBlurPass %s has a null shader when calling InitializeShaderVariant.", GetPathName().GetCStr());
  50. // Caching all pipeline state for each shader variation for performance reason.
  51. auto optionValueCount = m_optionValues.size();
  52. for (auto shaderVariantIndex = 0; shaderVariantIndex < optionValueCount; ++shaderVariantIndex)
  53. {
  54. auto shaderOption = m_shader->CreateShaderOptionGroup();
  55. shaderOption.SetValue(m_optionName, m_optionValues[shaderVariantIndex]);
  56. PreloadShaderVariant(m_shader, shaderOption, GetRenderAttachmentConfiguration(), GetMultisampleState());
  57. }
  58. m_needToUpdateShaderVariant = true;
  59. }
  60. void DepthOfFieldBokehBlurPass::UpdateCurrentShaderVariant()
  61. {
  62. AZ_Assert(m_shader != nullptr, "DepthOfFieldBokehBlurPass %s has a null shader when calling UpdateCurrentShaderVariant.", GetPathName().GetCStr());
  63. // Sample6 == 0
  64. // Sample18 == 1
  65. // Sample36 == 2
  66. // Sample60 == 3
  67. int index = 0;
  68. switch (m_sampleNumber)
  69. {
  70. case 6:
  71. index = 0;
  72. break;
  73. case 18:
  74. index = 1;
  75. break;
  76. case 36:
  77. index = 2;
  78. break;
  79. case 60:
  80. index = 3;
  81. break;
  82. default:
  83. AZ_Assert(false, "%s : sampleNumber is illegal when calling Compile.", GetPathName().GetCStr());
  84. break;
  85. }
  86. RPI::ShaderOptionGroup shaderOption = m_shader->CreateShaderOptionGroup();
  87. shaderOption.SetValue(m_optionName, m_optionValues[index]);
  88. UpdateShaderVariant(shaderOption);
  89. m_needToUpdateShaderVariant = false;
  90. }
  91. void DepthOfFieldBokehBlurPass::FrameBeginInternal(FramePrepareParams params)
  92. {
  93. RPI::Scene* scene = GetScene();
  94. PostProcessFeatureProcessor* fp = scene->GetFeatureProcessor<PostProcessFeatureProcessor>();
  95. AZ::RPI::ViewPtr view = GetRenderPipeline()->GetFirstView(GetPipelineViewTag());
  96. if (fp)
  97. {
  98. PostProcessSettings* postProcessSettings = fp->GetLevelSettingsFromView(view);
  99. if (postProcessSettings)
  100. {
  101. DepthOfFieldSettings* dofSettings = postProcessSettings->GetDepthOfFieldSettings();
  102. if (dofSettings)
  103. {
  104. AZ::RHI::Handle<uint32_t> splitSize = dofSettings->GetSplitSizeForPass(GetName());
  105. if (splitSize.IsValid())
  106. {
  107. switch (splitSize.GetIndex())
  108. {
  109. case 2:
  110. UpdateSampleTexcoords(dofSettings->m_sampleRadialDivision2, dofSettings->m_viewAspectRatio);
  111. SetRadiusMinMax(dofSettings->m_minBokehRadiusDivision2, dofSettings->m_maxBokehRadiusDivision2);
  112. break;
  113. case 4:
  114. UpdateSampleTexcoords(dofSettings->m_sampleRadialDivision4, dofSettings->m_viewAspectRatio);
  115. SetRadiusMinMax(dofSettings->m_minBokehRadiusDivision4, dofSettings->m_maxBokehRadiusDivision4);
  116. break;
  117. case 8:
  118. UpdateSampleTexcoords(dofSettings->m_sampleRadialDivision8, dofSettings->m_viewAspectRatio);
  119. SetRadiusMinMax(dofSettings->m_minBokehRadiusDivision8, dofSettings->m_maxBokehRadiusDivision8);
  120. break;
  121. default:
  122. AZ_Assert(false, "DepthOfFieldBokehBlurPass : Failed to get the division number from pass request name for blur.");
  123. break;
  124. }
  125. }
  126. }
  127. }
  128. }
  129. RPI::FullscreenTrianglePass::FrameBeginInternal(params);
  130. }
  131. void DepthOfFieldBokehBlurPass::CompileResources(const RHI::FrameGraphCompileContext& context)
  132. {
  133. AZ_Assert(m_shaderResourceGroup, "DepthOfFieldBokehBlurPass %s has a null shader resource group when calling FrameBeginInternal.", GetPathName().GetCStr());
  134. if (m_needToUpdateShaderVariant)
  135. {
  136. UpdateCurrentShaderVariant();
  137. }
  138. CompileShaderVariant(m_shaderResourceGroup);
  139. m_shaderResourceGroup->SetConstant(m_radiusMinIndex, m_radiusMin);
  140. m_shaderResourceGroup->SetConstant(m_radiusMaxIndex, m_radiusMax);
  141. m_shaderResourceGroup->SetConstantArray(m_sampleTexcoordsRadiusIndex, m_sampleTexcoords);
  142. BindPassSrg(context, m_shaderResourceGroup);
  143. m_shaderResourceGroup->Compile();
  144. }
  145. void DepthOfFieldBokehBlurPass::BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context)
  146. {
  147. AZ_Assert(m_shaderResourceGroup != nullptr, "DepthOfFieldBokehBlurPass %s has a null shader resource group when calling Execute.", GetPathName().GetCStr());
  148. RHI::CommandList* commandList = context.GetCommandList();
  149. commandList->SetViewport(m_viewportState);
  150. commandList->SetScissor(m_scissorState);
  151. SetSrgsForDraw(context);
  152. m_item.SetPipelineState(GetPipelineStateFromShaderVariant());
  153. commandList->Submit(m_item.GetDeviceDrawItem(context.GetDeviceIndex()));
  154. }
  155. void DepthOfFieldBokehBlurPass::SetRadiusMinMax(float min, float max)
  156. {
  157. m_radiusMin = min;
  158. m_radiusMax = max;
  159. }
  160. void DepthOfFieldBokehBlurPass::UpdateSampleTexcoords(uint32_t radialDivisionCount, float viewAspectRatio)
  161. {
  162. // calculate sampling texcoords.
  163. // sample 6 points around center pixel.
  164. // Similarly, sample around it as 12,18,24 points.
  165. AZ_Assert(radialDivisionCount >= 1 && radialDivisionCount <= 4, "DepthOfFieldBokehBlurPass : radialDivisionCount is illegal value.");
  166. // radialDivisionCount | sampleNumber |
  167. // 1 | 6 |
  168. // 2 | 6 + 12 |
  169. // 3 | 6 + 12 + 18 |
  170. // 4 | 6 + 12 + 18 + 24 |
  171. uint32_t newSampleNumber = 3 * radialDivisionCount * (radialDivisionCount + 1);
  172. // Switch shader variant when value changes.
  173. m_needToUpdateShaderVariant = m_needToUpdateShaderVariant || (m_sampleNumber != newSampleNumber);
  174. m_sampleNumber = newSampleNumber;
  175. AZ_Assert(m_sampleNumber <= SampleMax, "DepthOfFieldBokehBlurPass : sampleNumber is over.");
  176. // It is possible that copying this whole function in shader, may execute faster than passing the baked result as a memory-bound resource.
  177. // Doing the verification depends on how much we want to invest in optimization investigation.
  178. for (uint32_t index = 0; index < m_sampleNumber; index++)
  179. {
  180. // index[ 0- 5] -> radiusStep[1], angleStep[0- 5]
  181. // index[ 6-17] -> radiusStep[2], angleStep[0-11]
  182. // index[18-35] -> radiusStep[3], angleStep[0-17]
  183. // index[36-59] -> radiusStep[4], angleStep[0-23]
  184. uint32_t radiusStep = 0;
  185. if (index < 6)
  186. {
  187. radiusStep = 1;
  188. }
  189. else if (index < 18)
  190. {
  191. radiusStep = 2;
  192. }
  193. else if (index < 36)
  194. {
  195. radiusStep = 3;
  196. }
  197. else if (index < 60)
  198. {
  199. radiusStep = 4;
  200. }
  201. else
  202. {
  203. AZ_Assert(false, "DepthOfFieldBokehBlurPass : index is illegal.");
  204. }
  205. uint32_t angleStep = index - 3 * radiusStep * (radiusStep - 1);
  206. AZ_Assert(radiusStep >= 1 && radiusStep <= 4, "DepthOfFieldBokehBlurPass : radiusStep is illegal value.");
  207. // Divide by 'sampleRadial + 1' instead of 'sampleRadial' to shift the sampling inwards and increase useful sampling.
  208. float radius = static_cast<float>(radiusStep) / static_cast<float>(radialDivisionCount + 1);
  209. constexpr float AngleOffset = 0.5f;
  210. float angle = static_cast<float>(angleStep + AngleOffset) * (2.0f * AZ::Constants::Pi) / static_cast<float>(radius * 6);
  211. // The coordinates of the color buffer to be sampled.
  212. float colorTexcoordU = cosf(angle) * radius;
  213. float colorTexcoordV = sinf(angle) * radius;
  214. colorTexcoordU /= viewAspectRatio;
  215. m_sampleTexcoords[index][0] = colorTexcoordU;
  216. m_sampleTexcoords[index][1] = colorTexcoordV;
  217. // pencilmap
  218. // texcoord U is calculated based on depth in shader. It is not calculated here.
  219. // texcoord V is based on radius. The coordinate is radius 0 at the bottom and radius 1 at the top.
  220. float pencilmapTexcoordV = 1.0f - radius;
  221. m_sampleTexcoords[index][2] = pencilmapTexcoordV;
  222. m_sampleTexcoords[index][3] = 0.0f;
  223. }
  224. }
  225. } // namespace Render
  226. } // namespace AZ