RenderTargetTextureExampleComponent.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  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 <RenderTargetTextureExampleComponent.h>
  9. #include <Atom/Component/DebugCamera/NoClipControllerComponent.h>
  10. #include <Atom/RHI/Device.h>
  11. #include <Atom/RHI/Factory.h>
  12. #include <Atom/RPI.Public/DynamicDraw/DynamicDrawInterface.h>
  13. #include <Atom/RPI.Public/Image/AttachmentImagePool.h>
  14. #include <Atom/RPI.Public/Image/ImageSystemInterface.h>
  15. #include <Atom/RPI.Public/RPIUtils.h>
  16. #include <Atom/RPI.Public/View.h>
  17. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  18. #include <Atom/RPI.Reflect/Model/ModelAsset.h>
  19. #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
  20. #include <Atom/RPI.Reflect/Pass/PassDescriptor.h>
  21. #include <Atom/RPI.Reflect/Pass/RasterPassData.h>
  22. #include <AzCore/Asset/AssetManagerBus.h>
  23. #include <AzCore/Component/Entity.h>
  24. #include <AzCore/Math/MatrixUtils.h>
  25. #include <AzCore/std/smart_ptr/make_shared.h>
  26. #include <AzFramework/Components/TransformComponent.h>
  27. #include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
  28. #include <SampleComponentManager.h>
  29. #include <SampleComponentConfig.h>
  30. #include <EntityUtilityFunctions.h>
  31. #include <Automation/ScriptableImGui.h>
  32. #include <Automation/ScriptRunnerBus.h>
  33. #include <RHI/BasicRHIComponent.h>
  34. namespace AtomSampleViewer
  35. {
  36. using namespace AZ;
  37. namespace
  38. {
  39. static constexpr const char TextureFilePath[] = "textures/default/checker_uv_basecolor.png.streamingimage";
  40. }
  41. void RenderTargetTextureExampleComponent::Reflect(ReflectContext* context)
  42. {
  43. if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
  44. {
  45. serializeContext->Class<RenderTargetTextureExampleComponent, Component>()
  46. ->Version(0)
  47. ;
  48. }
  49. }
  50. RenderTargetTextureExampleComponent::RenderTargetTextureExampleComponent()
  51. : m_imguiSidebar("@user@/RenderTargetTextureExampleComponent/sidebar.xml")
  52. {
  53. }
  54. void RenderTargetTextureExampleComponent::Activate()
  55. {
  56. m_ibl.PreloadAssets();
  57. // Don't continue the script until assets are ready and scene is setup
  58. ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::PauseScriptWithTimeout, 120.0f);
  59. // preload assets
  60. AZStd::vector<AssetCollectionAsyncLoader::AssetToLoadInfo> assetList = {
  61. {DefaultPbrMaterialPath, azrtti_typeid<RPI::MaterialAsset>()},
  62. {BunnyModelFilePath, azrtti_typeid<RPI::ModelAsset>()},
  63. {TextureFilePath, azrtti_typeid<RPI::StreamingImageAsset>()}
  64. };
  65. PreloadAssets(assetList);
  66. }
  67. void RenderTargetTextureExampleComponent::AddRenderTargetPass()
  68. {
  69. m_renderTargetPassDrawListTag = AZ::Name("rt_1");
  70. const Name renderPassTemplateName = Name{ "RenderTargetPassTemplate" };
  71. RHI::ClearValue clearValue = RHI::ClearValue::CreateVector4Float(1, 0, 0, 1);
  72. // Create the template if it doesn't exist
  73. if (!RPI::PassSystemInterface::Get()->HasTemplate(renderPassTemplateName))
  74. {
  75. // first add a pass template from code (an alternative way then using .pass asset for a new template)
  76. const AZStd::shared_ptr<RPI::PassTemplate>rtPassTemplate = AZStd::make_shared<RPI::PassTemplate>();
  77. rtPassTemplate->m_name = renderPassTemplateName;
  78. rtPassTemplate->m_passClass = "RasterPass";
  79. // only need one slot for render target output
  80. RPI::PassSlot slot;
  81. slot.m_name = Name("ColorOutput");
  82. slot.m_slotType = RPI::PassSlotType::Output;
  83. slot.m_scopeAttachmentUsage = RHI::ScopeAttachmentUsage::RenderTarget;
  84. //slot.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::DontCare;
  85. slot.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Clear;
  86. slot.m_loadStoreAction.m_clearValue = clearValue;
  87. rtPassTemplate->AddSlot(slot);
  88. // connect the slot to attachment
  89. RPI::PassConnection connection;
  90. connection.m_localSlot = Name("ColorOutput");
  91. connection.m_attachmentRef.m_pass = Name("This");
  92. connection.m_attachmentRef.m_attachment = Name("RenderTarget");
  93. rtPassTemplate->AddOutputConnection(connection);
  94. RPI::PassSystemInterface::Get()->AddPassTemplate(renderPassTemplateName, rtPassTemplate);
  95. }
  96. // Create pass
  97. RPI::PassRequest createPassRequest;
  98. createPassRequest.m_templateName = renderPassTemplateName;
  99. createPassRequest.m_passName = Name("RenderTargetPass");
  100. // Create AttacmentImage which is used for pass render target
  101. const uint32_t imageWidth = 512;
  102. const uint32_t imageHeight = 512;
  103. Data::Instance<RPI::AttachmentImagePool> pool = RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool();
  104. RPI::CreateAttachmentImageRequest createRequest;
  105. createRequest.m_imagePool = pool.get();
  106. createRequest.m_imageDescriptor = RHI::ImageDescriptor::Create2D(AZ::RHI::ImageBindFlags::Color|AZ::RHI::ImageBindFlags::ShaderRead, imageWidth, imageHeight, RHI::Format::R8G8B8A8_UNORM);
  107. createRequest.m_imageName = Name("$RT1");
  108. createRequest.m_isUniqueName = true;
  109. createRequest.m_optimizedClearValue = &clearValue;
  110. AZStd::shared_ptr<AZ::RPI::RasterPassData> passData = AZStd::make_shared<AZ::RPI::RasterPassData>();
  111. passData->m_drawListTag = m_renderTargetPassDrawListTag;
  112. passData->m_pipelineViewTag = AZ::Name("MainCamera");
  113. passData->m_overrideScissor = RHI::Scissor(0, 0, imageWidth, imageHeight);
  114. passData->m_overrideViewport = RHI::Viewport(0, imageWidth, 0, imageHeight);
  115. createPassRequest.m_passData = passData;
  116. m_renderTarget = RPI::AttachmentImage::Create(createRequest);
  117. // Add image from asset
  118. RPI::PassImageAttachmentDesc imageAttachment;
  119. imageAttachment.m_name = "RenderTarget";
  120. imageAttachment.m_lifetime = RHI::AttachmentLifetimeType::Imported;
  121. imageAttachment.m_assetRef.m_assetId = m_renderTarget->GetAssetId();
  122. createPassRequest.m_imageAttachmentOverrides.push_back(imageAttachment);
  123. // Create the pass
  124. m_renderTargetPass = RPI::PassSystemInterface::Get()->CreatePassFromRequest(&createPassRequest);
  125. // add the pass to render pipeline
  126. RPI::RenderPipelinePtr renderPipeline = m_scene->GetDefaultRenderPipeline();
  127. RPI::Ptr<RPI::ParentPass> rootPass = renderPipeline->GetRootPass();
  128. // Insert to the beginning so it will be done before all the other rendering
  129. rootPass->InsertChild(m_renderTargetPass, RPI::ParentPass::ChildPassIndex(0));
  130. // Add a preview pass to preview the render target
  131. RPI::PassDescriptor descriptor(Name("RenderTargetPreview"));
  132. m_previewPass = RPI::ImageAttachmentPreviewPass::Create(descriptor);
  133. rootPass->AddChild(m_previewPass);
  134. // we need process queued changes to build the pass properly
  135. // which Scene::RebuildPipelineStatesLookup() requires
  136. renderPipeline->ProcessQueuedPassChanges();
  137. m_scene->RebuildPipelineStatesLookup();
  138. }
  139. void RenderTargetTextureExampleComponent::RemoveRenderTargetPass()
  140. {
  141. m_previewPass->ClearPreviewAttachment();
  142. m_previewPass->QueueForRemoval();
  143. m_previewPass = nullptr;
  144. m_renderTargetPass->QueueForRemoval();
  145. m_renderTargetPass = nullptr;
  146. m_renderTarget = nullptr;
  147. }
  148. void RenderTargetTextureExampleComponent::CreateDynamicDraw()
  149. {
  150. const char* shaderFilepath = "Shaders/SimpleTextured.azshader";
  151. AZ::Data::Instance<AZ::RPI::Shader> shader = AZ::RPI::LoadCriticalShader(shaderFilepath);
  152. RHI::DrawListTagRegistry* drawListTagRegistry = RHI::RHISystemInterface::Get()->GetDrawListTagRegistry();
  153. RHI::DrawListTag newTag = drawListTagRegistry->FindTag(m_renderTargetPassDrawListTag);
  154. AZ::RPI::ShaderOptionList shaderOptions;
  155. shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("true")));
  156. shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("true")));
  157. m_dynamicDraw = AZ::RPI::DynamicDrawInterface::Get()->CreateDynamicDrawContext();
  158. m_dynamicDraw->InitDrawListTag(newTag);
  159. m_dynamicDraw->InitShaderWithVariant(shader, &shaderOptions);
  160. m_dynamicDraw->InitVertexFormat(
  161. { {"POSITION", AZ::RHI::Format::R32G32B32_FLOAT},
  162. {"COLOR", AZ::RHI::Format::B8G8R8A8_UNORM},
  163. {"TEXCOORD0", AZ::RHI::Format::R32G32_FLOAT} });
  164. m_dynamicDraw->AddDrawStateOptions(RPI::DynamicDrawContext::DrawStateOptions::BlendMode);
  165. m_dynamicDraw->SetOutputScope(m_scene);
  166. m_dynamicDraw->EndInit();
  167. RHI::TargetBlendState blendState;
  168. blendState.m_enable = false;
  169. m_dynamicDraw->SetTarget0BlendState(blendState);
  170. // load texture used for the dynamic draw
  171. m_texture = AZ::RPI::LoadStreamingTexture(TextureFilePath);
  172. }
  173. void RenderTargetTextureExampleComponent::OnAllAssetsReadyActivate()
  174. {
  175. // create render target pass
  176. AddRenderTargetPass();
  177. CreateDynamicDraw();
  178. // load mesh and material
  179. auto meshFeatureProcessor = GetMeshFeatureProcessor();
  180. // material
  181. auto materialAsset = RPI::AssetUtils::LoadAssetByProductPath<RPI::MaterialAsset>(DefaultPbrMaterialPath,
  182. RPI::AssetUtils::TraceLevel::Assert);
  183. m_material = AZ::RPI::Material::FindOrCreate(materialAsset);
  184. // bunny mesh
  185. auto bunnyAsset = RPI::AssetUtils::LoadAssetByProductPath<RPI::ModelAsset>(BunnyModelFilePath,
  186. RPI::AssetUtils::TraceLevel::Assert);
  187. m_meshHandle = meshFeatureProcessor->AcquireMesh(Render::MeshHandleDescriptor(bunnyAsset, m_material));
  188. meshFeatureProcessor->SetTransform(m_meshHandle, Transform::CreateTranslation(Vector3(0.f, 0.f, 0.21f)));
  189. // Set camera to use no clip controller and adjust its fov and transform
  190. AZ::Debug::CameraControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::CameraControllerRequestBus::Events::Enable,
  191. azrtti_typeid<AZ::Debug::NoClipControllerComponent>());
  192. AZ::Debug::NoClipControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::NoClipControllerRequestBus::Events::SetFov, AZ::DegToRad(90));
  193. AZ::Debug::NoClipControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::NoClipControllerRequestBus::Events::SetPosition, Vector3(1.244541f, -0.660081f, 1.902831f));
  194. AZ::Debug::NoClipControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::NoClipControllerRequestBus::Events::SetHeading, AZ::DegToRad(61.274673f));
  195. AZ::Debug::NoClipControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::NoClipControllerRequestBus::Events::SetPitch, AZ::DegToRad(-36.605690f));
  196. // Setup IBL
  197. m_ibl.Init(m_scene);
  198. // set render target texture to material
  199. auto propertyId = m_material->FindPropertyIndex(AZ::Name{ "baseColor.textureMap" });
  200. auto image = RPI::AttachmentImage::FindByUniqueName(Name("$RT1"));
  201. if (propertyId.IsValid())
  202. {
  203. m_baseColorTexture = m_material->GetPropertyValue<Data::Instance<RPI::Image>>(propertyId);
  204. m_material->SetPropertyValue(propertyId, (Data::Instance<RPI::Image>)image);
  205. }
  206. else
  207. {
  208. AZ_Error("ASV", false, "Failed to file property 'baseColor.textureMap' in the material");
  209. }
  210. AZ::TickBus::Handler::BusConnect();
  211. m_imguiSidebar.Activate();
  212. ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::ResumeScript);
  213. UpdateRenderTargetPreview();
  214. }
  215. void RenderTargetTextureExampleComponent::Deactivate()
  216. {
  217. AZ::TickBus::Handler::BusDisconnect();
  218. m_imguiSidebar.Deactivate();
  219. // release model and mesh
  220. GetMeshFeatureProcessor()->ReleaseMesh(m_meshHandle);
  221. m_material = nullptr;
  222. m_baseColorTexture = nullptr;
  223. m_modelAsset = {};
  224. m_ibl.Reset();
  225. AZ::Debug::CameraControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::CameraControllerRequestBus::Events::Disable);
  226. // Release dynamic draw context
  227. m_dynamicDraw = nullptr;
  228. RemoveRenderTargetPass();
  229. }
  230. void RenderTargetTextureExampleComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  231. {
  232. // Note, the Compile function need to be called to apply material property change in the next frame after material was created which called copmile()
  233. // Compile() only can be called once per frame
  234. if (m_material->NeedsCompile())
  235. {
  236. m_material->Compile();
  237. }
  238. bool drawToRenderTarget = false;
  239. if (m_imguiSidebar.Begin())
  240. {
  241. if (ScriptableImGui::Button("Next Frame"))
  242. {
  243. drawToRenderTarget = true;
  244. m_currentFrame = (m_currentFrame+1)%4;
  245. }
  246. ImGui::Separator();
  247. if (ScriptableImGui::Checkbox("Show Preview", &m_showRenderTargetPreview))
  248. {
  249. UpdateRenderTargetPreview();
  250. }
  251. ImGui::Separator();
  252. ScriptableImGui::Checkbox("Update Per Second", &m_updatePerSecond);
  253. if (m_updatePerSecond)
  254. {
  255. auto currentTime = time.GetSeconds();
  256. if (currentTime > m_lastUpdateTime + 1)
  257. {
  258. drawToRenderTarget = true;
  259. m_lastUpdateTime = currentTime;
  260. m_currentFrame = (m_currentFrame+1)%4;
  261. }
  262. }
  263. else if (m_lastUpdateTime == 0)
  264. {
  265. drawToRenderTarget = true;
  266. m_lastUpdateTime = time.GetSeconds();
  267. }
  268. ImGui::Separator();
  269. ImGui::Text("Current frame: %d", m_currentFrame+1);
  270. m_imguiSidebar.End();
  271. }
  272. // have to disable the pass if there is no drawing. otherwise the render target would be cleared.
  273. m_renderTargetPass->SetEnabled(drawToRenderTarget);
  274. if (drawToRenderTarget)
  275. {
  276. // Draw something to the render target pass
  277. DrawToRenderTargetPass();
  278. }
  279. }
  280. void RenderTargetTextureExampleComponent::UpdateRenderTargetPreview()
  281. {
  282. if (m_showRenderTargetPreview)
  283. {
  284. // Add attachment preview after pass queued changes processed
  285. // m_renderTargetPass only has one attachment
  286. m_previewPass->PreviewImageAttachmentForPass(m_renderTargetPass.get(), m_renderTargetPass->GetAttachmentBindings()[0].GetAttachment().get());
  287. }
  288. else
  289. {
  290. m_previewPass->ClearPreviewAttachment();
  291. }
  292. }
  293. void RenderTargetTextureExampleComponent::DrawToRenderTargetPass()
  294. {
  295. const int numVerts = 4;
  296. const int numFrames = 4;
  297. struct Vertex
  298. {
  299. float position[3];
  300. uint32_t color;
  301. float uv[2];
  302. };
  303. uint32_t colors[numFrames] =
  304. {
  305. 0xff0000ff,
  306. 0xffff00ff,
  307. 0xff00ffff,
  308. 0xffffffff
  309. };
  310. float uvoffset[numFrames][2] =
  311. {
  312. {0, 0.5f},
  313. {0.5f, 0.5f},
  314. {0, 0},
  315. {0.5f, 0},
  316. };
  317. Vertex vertices[numVerts];
  318. // Create a vertex offset from the position to draw from based on the icon size
  319. // Vertex positions are in screen space coordinates
  320. auto createVertex = [&](float x, float y, float u, float v) -> Vertex
  321. {
  322. Vertex vertex;
  323. vertex.position[0] = x;
  324. vertex.position[1] = y;
  325. vertex.position[2] = 0.5f;
  326. vertex.color = colors[m_currentFrame];
  327. vertex.uv[0] = u + uvoffset[m_currentFrame][0];
  328. vertex.uv[1] = v + uvoffset[m_currentFrame][1];
  329. return vertex;
  330. };
  331. vertices[0] = createVertex(0.f, 0.f, 0.f, 0.f);
  332. vertices[1] = createVertex(0.f, 1.f, 0.f, 0.5f);
  333. vertices[2] = createVertex(1.f, 1.f, 0.5f, 0.5f);
  334. vertices[3] = createVertex(1.f, 0.f, 0.5f, 0.f);
  335. using Indice = AZ::u16;
  336. AZStd::array<Indice, 6> indices = {0, 1, 2, 0, 2, 3};
  337. // setup draw srg
  338. auto drawSrg = m_dynamicDraw->NewDrawSrg();
  339. AZ::RHI::ShaderInputNameIndex imageInputIndex = "m_texture";
  340. AZ::RHI::ShaderInputNameIndex viewProjInputIndex = "m_worldToProj";
  341. // Set projection matrix
  342. const float viewX = 0;
  343. const float viewY = 0;
  344. const float viewWidth = 1;
  345. const float viewHeight = 1;
  346. const float zf = 0;
  347. const float zn = 1;
  348. AZ::Matrix4x4 modelViewProjMat;
  349. AZ::MakeOrthographicMatrixRH(modelViewProjMat, viewX, viewX + viewWidth, viewY + viewHeight, viewY, zn, zf);
  350. drawSrg->SetConstant(viewProjInputIndex, modelViewProjMat);
  351. drawSrg->SetImage(imageInputIndex, m_texture);
  352. drawSrg->Compile();
  353. m_dynamicDraw->DrawIndexed(vertices, numVerts, &indices, aznumeric_cast<uint32_t>(indices.size()), RHI::IndexFormat::Uint16, drawSrg);
  354. }
  355. } // namespace AtomSampleViewer