From 40435fcb2e5326bc28cb750aaa3df270ba263ff9 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Tue, 25 May 2021 23:52:19 -0700 Subject: [PATCH] Added RayTracingMaterialSrg. Added UV buffer to the RayTracingSceneSrg mesh buffers. Added RayTracingSceneUtils and RayTracingMaterialUtils shader includes. --- .../RayTracing/RayTracingMaterialSrg.azsli | 48 ++++ .../RayTracing/RayTracingMaterialUtils.azsli | 69 +++++ .../RayTracing/RayTracingSceneSrg.azsli | 33 ++- .../RayTracing/RayTracingSceneUtils.azsli | 126 +++++++++ .../Atom/Features/SrgSemantics.azsli | 5 + .../diffuseprobegridraytracing.azshader | Bin 79334 -> 79466 bytes ...probegridraytracing_dx12_0.azshadervariant | Bin 34086 -> 34738 bytes ...probegridraytracing_null_0.azshadervariant | Bin 4502 -> 4502 bytes ...obegridraytracing_vulkan_0.azshadervariant | Bin 36232 -> 36332 bytes ...fuseprobegridraytracingclosesthit.azshader | Bin 79344 -> 79476 bytes ...aytracingclosesthit_dx12_0.azshadervariant | Bin 16754 -> 17150 bytes ...aytracingclosesthit_null_0.azshadervariant | Bin 4502 -> 4502 bytes ...tracingclosesthit_vulkan_0.azshadervariant | Bin 9172 -> 9452 bytes ...raytracingcommon_raytracingglobalsrg.azsrg | 24 +- .../diffuseprobegridraytracingmiss.azshader | Bin 79338 -> 79470 bytes ...egridraytracingmiss_dx12_0.azshadervariant | Bin 16838 -> 17222 bytes ...egridraytracingmiss_null_0.azshadervariant | Bin 4502 -> 4502 bytes ...ridraytracingmiss_vulkan_0.azshadervariant | Bin 10364 -> 10364 bytes .../Code/Source/Mesh/MeshFeatureProcessor.cpp | 191 ++++++++++++-- .../RayTracingAccelerationStructurePass.cpp | 4 +- .../RayTracing/RayTracingFeatureProcessor.cpp | 240 ++++++++++++++---- .../RayTracing/RayTracingFeatureProcessor.h | 111 +++++++- .../Code/Source/RayTracing/RayTracingPass.cpp | 11 +- .../Code/Source/RayTracing/RayTracingPass.h | 1 + 24 files changed, 766 insertions(+), 97 deletions(-) create mode 100644 Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialSrg.azsli create mode 100644 Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialUtils.azsli create mode 100644 Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneUtils.azsli diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialSrg.azsli new file mode 100644 index 0000000000..40faba24f9 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialSrg.azsli @@ -0,0 +1,48 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +ShaderResourceGroup RayTracingMaterialSrg : SRG_RayTracingMaterial +{ + Sampler LinearSampler + { + AddressU = Wrap; + AddressV = Wrap; + MinFilter = Linear; + MagFilter = Linear; + MipFilter = Linear; + MaxAnisotropy = 16; + }; + + // material info structured buffer + struct MaterialInfo + { + float4 m_baseColor; + float m_metallicFactor; + float m_roughnessFactor; + uint m_textureFlags; + uint m_textureStartIndex; + }; + + // hit shaders can retrieve the MaterialInfo for a mesh hit using: RayTracingMaterialSrg::m_materialInfo[InstanceIndex()] + StructuredBuffer m_materialInfo; + + // texture flag bits indicating if optional textures are present + #define TEXTURE_FLAG_BASECOLOR 1 + #define TEXTURE_FLAG_NORMAL 2 + #define TEXTURE_FLAG_METALLIC 4 + #define TEXTURE_FLAG_ROUGHNESS 8 + + // unbounded array of Material textures + Texture2D m_materialTextures[]; +} \ No newline at end of file diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialUtils.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialUtils.azsli new file mode 100644 index 0000000000..d6dd77f4fa --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialUtils.azsli @@ -0,0 +1,69 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +struct TextureData +{ + float4 m_baseColor; + float3 m_normal; + float m_metallic; + float m_roughness; +}; + +TextureData GetHitTextureData(RayTracingMaterialSrg::MaterialInfo materialInfo, float2 uv) +{ + TextureData textureData = (TextureData)0; + + uint textureIndex = materialInfo.m_textureStartIndex; + + // base color + if (materialInfo.m_textureFlags & TEXTURE_FLAG_BASECOLOR) + { + textureData.m_baseColor = RayTracingMaterialSrg::m_materialTextures[textureIndex++].SampleLevel(RayTracingMaterialSrg::LinearSampler, uv, 0); + } + else + { + textureData.m_baseColor = materialInfo.m_baseColor; + } + + // normal + if (materialInfo.m_textureFlags & TEXTURE_FLAG_NORMAL) + { + textureData.m_normal = RayTracingMaterialSrg::m_materialTextures[textureIndex++].SampleLevel(RayTracingMaterialSrg::LinearSampler, uv, 0); + } + else + { + textureData.m_normal = float3(0.0f, 0.0f, 1.0f); + } + + // metallic + if (materialInfo.m_textureFlags & TEXTURE_FLAG_METALLIC) + { + textureData.m_metallic = RayTracingMaterialSrg::m_materialTextures[textureIndex++].SampleLevel(RayTracingMaterialSrg::LinearSampler, uv, 0); + } + else + { + textureData.m_metallic = materialInfo.m_metallicFactor; + } + + // roughness + if (materialInfo.m_textureFlags & TEXTURE_FLAG_ROUGHNESS) + { + textureData.m_roughness = RayTracingMaterialSrg::m_materialTextures[textureIndex++].SampleLevel(RayTracingMaterialSrg::LinearSampler, uv, 0); + } + else + { + textureData.m_roughness = materialInfo.m_roughnessFactor; + } + + return textureData; +} + \ No newline at end of file diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneSrg.azsli index fdf7ba92de..2352f5d09b 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneSrg.azsli @@ -136,18 +136,35 @@ ShaderResourceGroup RayTracingSceneSrg : SRG_RayTracingScene uint m_indexOffset; uint m_positionOffset; uint m_normalOffset; + uint m_tangentOffset; + uint m_bitangentOffset; + uint m_uvOffset; + float m_padding0[2]; + float4 m_irradianceColor; float3x3 m_worldInvTranspose; + float m_padding1[1]; + + uint m_bufferFlags; + uint m_bufferStartIndex; }; - + + // hit shaders can retrieve the MeshInfo for a mesh hit using: RayTracingSceneSrg::m_meshInfo[InstanceIndex()] StructuredBuffer m_meshInfo; - // unbounded array of Index, VertexPosition, and VertexNormal buffers - // each mesh has three entries in this array starting at its InstanceIndex() * BUFFER_COUNT_PER_MESH - #define BUFFER_COUNT_PER_MESH 3 - #define MESH_INDEX_BUFFER_OFFSET 0 - #define MESH_POSITION_BUFFER_OFFSET 1 - #define MESH_NORMAL_BUFFER_OFFSET 2 - + // buffer array index offsets for buffers that are always present for each mesh + #define MESH_INDEX_BUFFER_OFFSET 0 + #define MESH_POSITION_BUFFER_OFFSET 1 + #define MESH_NORMAL_BUFFER_OFFSET 2 + #define MESH_TANGENT_BUFFER_OFFSET 3 + #define MESH_BITANGENT_BUFFER_OFFSET 4 + + // buffer flag bits indicating if optional buffers are present + #define MESH_BUFFER_FLAG_UV 1 + + // Unbounded array of mesh stream buffers: + // - Index, Position, Normal, Tangent, and Bitangent stream buffers are always present + // - Optional stream buffers such as UV are indicated in the MeshInfo.m_bufferFlags field + // - Buffers for a particular mesh start at MeshInfo.m_bufferStartIndex ByteAddressBuffer m_meshBuffers[]; } \ No newline at end of file diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneUtils.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneUtils.azsli new file mode 100644 index 0000000000..f858b9a11a --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneUtils.azsli @@ -0,0 +1,126 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +// returns the normalized camera view ray into the scene for this raytracing dispatch thread +float3 GetViewRayDirection(float4x4 viewProjectionInverseMatrix) +{ + float2 pixel = ((float2)DispatchRaysIndex().xy + float2(0.5f, 0.5f)) / (float2)DispatchRaysDimensions(); + float2 ndc = pixel * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f); + return normalize(mul(viewProjectionInverseMatrix, float4(ndc, 0.0f, 1.0f)).xyz); +} + +// returns the vertex indices for the primitive hit by the ray +// Note: usable only in a raytracing Hit shader +uint3 GetHitIndices(RayTracingSceneSrg::MeshInfo meshInfo) +{ + // compute the array index of the index buffer for this mesh in the m_meshBuffers unbounded array + uint meshIndexBufferArrayIndex = meshInfo.m_bufferStartIndex + MESH_INDEX_BUFFER_OFFSET; + + // compute the offset into the index buffer for this primitve of the mesh + uint offsetBytes = meshInfo.m_indexOffset + (PrimitiveIndex() * 12); + + // load the indices for this primitive from the index buffer + return RayTracingSceneSrg::m_meshBuffers[meshIndexBufferArrayIndex].Load3(offsetBytes); +} + +// returns the interpolated vertex data for the primitive hit by the ray +// Note: usable only in a raytracing hit shader +struct VertexData +{ + float3 m_position; + float3 m_normal; + float3 m_tangent; + float3 m_bitangent; + float2 m_uv; +}; + +VertexData GetHitInterpolatedVertexData(RayTracingSceneSrg::MeshInfo meshInfo, float2 builtInBarycentrics) +{ + // retrieve the poly indices + uint3 indices = GetHitIndices(meshInfo); + + // compute barycentrics + float3 barycentrics = float3((1.0f - builtInBarycentrics.x - builtInBarycentrics.y), builtInBarycentrics.x, builtInBarycentrics.y); + + // compute the vertex data using barycentric interpolation + VertexData vertexData = (VertexData)0; + for (uint i = 0; i < 3; ++i) + { + // position + { + // array index of the position buffer for this mesh in the m_meshBuffers unbounded array + uint meshVertexPositionArrayIndex = meshInfo.m_bufferStartIndex + MESH_POSITION_BUFFER_OFFSET; + + // offset into the position buffer for this vertex + uint positionOffset = meshInfo.m_positionOffset + (indices[i] * 12); + + // load the position data + vertexData.m_position += asfloat(RayTracingSceneSrg::m_meshBuffers[meshVertexPositionArrayIndex].Load3(positionOffset)) * barycentrics[i]; + } + + // normal + { + // array index of the normal buffer for this mesh in the m_meshBuffers unbounded array + uint meshVertexNormalArrayIndex = meshInfo.m_bufferStartIndex + MESH_NORMAL_BUFFER_OFFSET; + + // offset into the normal buffer for this vertex + uint normalOffset = meshInfo.m_normalOffset + (indices[i] * 12); + + // load the normal data + vertexData.m_normal += asfloat(RayTracingSceneSrg::m_meshBuffers[meshVertexNormalArrayIndex].Load3(normalOffset)) * barycentrics[i]; + } + + // tangent + { + // array index of the tangent buffer for this mesh in the m_meshBuffers unbounded array + uint meshVertexTangentArrayIndex = meshInfo.m_bufferStartIndex + MESH_TANGENT_BUFFER_OFFSET; + + // offset into the tangent buffer for this vertex + uint tangentOffset = meshInfo.m_tangentOffset + (indices[i] * 12); + + // load the tangent data + vertexData.m_tangent += asfloat(RayTracingSceneSrg::m_meshBuffers[meshVertexTangentArrayIndex].Load3(tangentOffset)) * barycentrics[i]; + } + + // bitangent + { + // array index of the bitangent buffer for this mesh in the m_meshBuffers unbounded array + uint meshVertexBitangentArrayIndex = meshInfo.m_bufferStartIndex + MESH_BITANGENT_BUFFER_OFFSET; + + // offset into the bitangent buffer for this vertex + uint bitangentOffset = meshInfo.m_bitangentOffset + (indices[i] * 12); + + // load the bitangent data + vertexData.m_bitangent += asfloat(RayTracingSceneSrg::m_meshBuffers[meshVertexBitangentArrayIndex].Load3(bitangentOffset)) * barycentrics[i]; + } + + // optional streams begin after MESH_BITANGENT_BUFFER_OFFSET + uint optionalBufferOffset = MESH_BITANGENT_BUFFER_OFFSET + 1; + + // UV + if (meshInfo.m_bufferFlags & MESH_BUFFER_FLAG_UV) + { + // array index of the UV buffer for this mesh in the m_meshBuffers unbounded array + uint meshVertexUVArrayIndex = meshInfo.m_bufferStartIndex + optionalBufferOffset++; + + // offset into the UV buffer for this vertex + uint uvOffset = meshInfo.m_uvOffset + (indices[i] * 8); + + // load the UV data + vertexData.m_uv += asfloat(RayTracingSceneSrg::m_meshBuffers[meshVertexUVArrayIndex].Load2(uvOffset)) * barycentrics[i]; + } + } + + vertexData.m_normal = normalize(vertexData.m_normal); + + return vertexData; +} \ No newline at end of file diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/SrgSemantics.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/SrgSemantics.azsli index c134a9d293..6d1b05d797 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/SrgSemantics.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/SrgSemantics.azsli @@ -64,3 +64,8 @@ ShaderResourceGroupSemantic SRG_RayTracingScene { FrequencyId = 1; }; + +ShaderResourceGroupSemantic SRG_RayTracingMaterial +{ + FrequencyId = 2; +}; diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing.azshader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing.azshader index 804b534277c99e83211f6276d08f434bc053295d..746ae357ee6493061a828bd02010fab4308b1164 100644 GIT binary patch delta 430 zcmaF%n&s6SmJOP0LhSOBjs?zccCK6<6U6v&)1iup$rEiv7#Su%ob0f^`U|^iQGwb!_^A}19 zIb<`$<35)+Gh>Fzb8!x*?l*6|oftQ7e(;f%9j9TyfOy9^`GF$K<`=(iS#nf93#i<5 e{&>^m?kp{q28e4`PhV`xXwBjG??Z48(18HC4!d;# delta 414 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 literal 36232 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 M+%~eyrkm9MA3e>YGXMYp diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit.azshader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit.azshader index c4a83a26dcc61d813d22d92798d7918b141877b3..0f42cd04d6971885c5d9acfd0d5665eee62d132a 100644 GIT binary patch delta 430 zcmezHn&rzImJP;iLhSOBjs?zccCK6<6U6v&)1iup$p@uG7#Sx0bl5!q;%3Il1}rR- z?@krn9KbP;d2)WS)Mmb->r9LZlNF^UH~%j=C(d#3V^5TA(UA$8&#SSr<1}y6&v#(6 zHs76k%aWrc)_9x9y&L;C@66<6WMrKD?)>Y?>$1CX>ZYx0PRev#asHkdG-JLH+z0shC7Tm{XApMalNq7;fb*1 g*2xFXYq2yiFn|JM_4Ku-jMf}}|Gub*GcYg!0Q-Em`Tzg` delta 492 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 VwsNenAw;~GzVgop{_cS1{{>MESCaq$ delta 7827 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 Rx-Q`=Vn@pQ;}D?1{{pwXlkfll diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_null_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_null_0.azshadervariant index b7bdabeb4e9dfea986d9adf30d0f57c1225bedd9..d9d30c99f7c6609f3be3cfba81ec330469fb67fe 100644 GIT binary patch delta 16 XcmbQHJWY8+pCE_dzb`7{3=9kaH0}jO delta 16 XcmbQHJWY8+pCHFZ->{jC3=9kaF>D05 diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_vulkan_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_vulkan_0.azshadervariant index 1eeeffa8be8df2020435b46d5b5de152d8ece66b..e2f2a13ca3738fe19122583db01704237b19ea4a 100644 GIT binary patch literal 9452 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 h(SV%BmzPvt9Y1)pbK=9&-}`>& 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 delta 451 zcmaF&hUL|3mJPaWf^71Wjs?zccCK6<6U6v&)1ivVbw3?8vtQcGIN4z?%Vhm&qMH*q z<}pvM=aQQIpp#>>0?&D-$+|bBHrL!LVVpd{mu2&p+h$yo1J)T%j((OjSwK#2a>8qg z%@d^ExjEQwmhS$ubB^z3!CqE&Oq({>{d@7HpdlXqunu{1C+uuN|-Wqii5(U0S)5(5JR0P`Ti AlmGw# diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_dx12_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_dx12_0.azshadervariant index 839efa72787ebf96ba18125c20d57da3997d6704..c144350c7ff1c7da6a32247b50d0e80f1d29ce31 100644 GIT binary patch delta 5097 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 A%m4rY delta 4676 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 delta 1362 zcmZvbJ7`l;7{^c2#HJX9Yx8K6Cimv%MM$GyC{c^FzNpfgroL-it@x-)B|ZiRhpUsD z4&UJFrs7b=!HPq1b`*rtK^G~milA7k|NqUolEJ`_@0{=P|IRu0%UWZt@uF^QE^Yjn zdVEektLaj0`MbR9?3Si6;{O_XwP>t|ou~?^s8Y%T{FU`S*4qBt$FcXd#e1+0wC#Jb z{sUsFLnUA>5JyH5mV4&1S0Ahew3g#KQgCztHfM4g@J9g?mOjS&)dqKOVa*&5uPy`M z!}s<1i1I7jjd!UqEHyl+#S1fTtEy5ZNOO|~6ygbUpi}QzkO%k;yKz}H%f5tkTch&L zipaVZ3c9xx$4WY^=23f~)DO!D@6M}KV5#N@eb`4}8RuK3Rj=9t%VSc*yLK6C*8C6V z5if?C?QT;#+Y8y4&C+;31D1_g#CM3wXl9$wlslQK(Hew+8i1dNOv+LwiL0(;9t|+R z29&zHv=vw60e1kd8QZDX)Yzrhwmh+moA|JUfR7iI6?<@kwZmWta1bU`(AC?cee6vi zwf1TsACW#AcAxeIJ)g{FOiqC#maIdjDGYed>RfmKVr)=o3qydoVl1RU0sU=?*Az{mV)z&yt8Y2OUi)L|@)5jJHZpNum5 zEI0!=%5JuN%2#KoeICpL{v6z&z6*eUum44?XMy)+m~%<{cxJC|PWv1Dr%JO>PVv51 z!4+^BP=RmPy8G)w)u7fjFb{ahD{gFYwW2H^W<0pINx s^bf%P)b(modelLods.size() - 1); const Data::Instance& modelLod = modelLods[rayTracingLod]; - // setup a stream layout and shader input contract for the position and normal streams + // setup a stream layout and shader input contract for the vertex streams static const char* PositionSemantic = "POSITION"; static const char* NormalSemantic = "NORMAL"; - static const RHI::Format StreamFormat = RHI::Format::R32G32B32_FLOAT; + static const char* TangentSemantic = "TANGENT"; + static const char* BitangentSemantic = "BITANGENT"; + static const char* UVSemantic = "UV"; + static const RHI::Format PositionStreamFormat = RHI::Format::R32G32B32_FLOAT; + static const RHI::Format NormalStreamFormat = RHI::Format::R32G32B32_FLOAT; + static const RHI::Format TangentStreamFormat = RHI::Format::R32G32B32_FLOAT; + static const RHI::Format BitangentStreamFormat = RHI::Format::R32G32B32_FLOAT; + static const RHI::Format UVStreamFormat = RHI::Format::R32G32_FLOAT; RHI::InputStreamLayoutBuilder layoutBuilder; - layoutBuilder.AddBuffer()->Channel(PositionSemantic, StreamFormat); - layoutBuilder.AddBuffer()->Channel(NormalSemantic, StreamFormat); + layoutBuilder.AddBuffer()->Channel(PositionSemantic, PositionStreamFormat); + layoutBuilder.AddBuffer()->Channel(NormalSemantic, NormalStreamFormat); + layoutBuilder.AddBuffer()->Channel(UVSemantic, UVStreamFormat); + layoutBuilder.AddBuffer()->Channel(TangentSemantic, TangentStreamFormat); + layoutBuilder.AddBuffer()->Channel(BitangentSemantic, BitangentStreamFormat); RHI::InputStreamLayout inputStreamLayout = layoutBuilder.End(); RPI::ShaderInputContract::StreamChannelInfo positionStreamChannelInfo; positionStreamChannelInfo.m_semantic = RHI::ShaderSemantic(AZ::Name(PositionSemantic)); - positionStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(StreamFormat); + positionStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(PositionStreamFormat); RPI::ShaderInputContract::StreamChannelInfo normalStreamChannelInfo; normalStreamChannelInfo.m_semantic = RHI::ShaderSemantic(AZ::Name(NormalSemantic)); - normalStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(StreamFormat); + normalStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(NormalStreamFormat); + + RPI::ShaderInputContract::StreamChannelInfo tangentStreamChannelInfo; + tangentStreamChannelInfo.m_semantic = RHI::ShaderSemantic(AZ::Name(TangentSemantic)); + tangentStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(TangentStreamFormat); + + RPI::ShaderInputContract::StreamChannelInfo bitangentStreamChannelInfo; + bitangentStreamChannelInfo.m_semantic = RHI::ShaderSemantic(AZ::Name(BitangentSemantic)); + bitangentStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(BitangentStreamFormat); + + RPI::ShaderInputContract::StreamChannelInfo uvStreamChannelInfo; + uvStreamChannelInfo.m_semantic = RHI::ShaderSemantic(AZ::Name(UVSemantic)); + uvStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(UVStreamFormat); + uvStreamChannelInfo.m_isOptional = true; RPI::ShaderInputContract shaderInputContract; shaderInputContract.m_streamChannels.emplace_back(positionStreamChannelInfo); shaderInputContract.m_streamChannels.emplace_back(normalStreamChannelInfo); + shaderInputContract.m_streamChannels.emplace_back(tangentStreamChannelInfo); + shaderInputContract.m_streamChannels.emplace_back(bitangentStreamChannelInfo); + shaderInputContract.m_streamChannels.emplace_back(uvStreamChannelInfo); // setup the raytracing data for each sub-mesh const size_t meshCount = modelLod->GetMeshes().size(); @@ -739,16 +765,44 @@ namespace AZ { const RPI::ModelLod::Mesh& mesh = modelLod->GetMeshes()[meshIndex]; + // retrieve the material + Data::Instance material = mesh.m_material; + + const MaterialAssignmentId materialAssignmentId(rayTracingLod, material ? material->GetAssetId() : AZ::Data::AssetId()); + const MaterialAssignment& materialAssignment = GetMaterialAssignmentFromMapWithFallback(m_materialAssignments, materialAssignmentId); + if (materialAssignment.m_materialInstance.get()) + { + material = materialAssignment.m_materialInstance; + } + // retrieve vertex/index buffers RPI::ModelLod::StreamBufferViewList streamBufferViews; - [[maybe_unused]] bool result = modelLod->GetStreamsForMesh(inputStreamLayout, streamBufferViews, shaderInputContract, meshIndex); + [[maybe_unused]] bool result = modelLod->GetStreamsForMesh( + inputStreamLayout, + streamBufferViews, + shaderInputContract, + meshIndex, + materialAssignment.m_matModUvOverrides, + material->GetAsset()->GetMaterialTypeAsset()->GetUvNameMap()); AZ_Assert(result, "Failed to retrieve mesh stream buffer views"); // note that the element count is the size of the entire buffer, even though this mesh may only // occupy a portion of the vertex buffer. This is necessary since we are accessing it using // a ByteAddressBuffer in the raytracing shaders and passing the byte offset to the shader in a constant buffer. - uint32_t vertexBufferByteCount = const_cast(streamBufferViews[0].GetBuffer())->GetDescriptor().m_byteCount; - RHI::BufferViewDescriptor vertexBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, vertexBufferByteCount); + uint32_t positionBufferByteCount = const_cast(streamBufferViews[0].GetBuffer())->GetDescriptor().m_byteCount; + RHI::BufferViewDescriptor positionBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, positionBufferByteCount); + + uint32_t normalBufferByteCount = const_cast(streamBufferViews[1].GetBuffer())->GetDescriptor().m_byteCount; + RHI::BufferViewDescriptor normalBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, normalBufferByteCount); + + uint32_t tangentBufferByteCount = const_cast(streamBufferViews[2].GetBuffer())->GetDescriptor().m_byteCount; + RHI::BufferViewDescriptor tangentBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, tangentBufferByteCount); + + uint32_t bitangentBufferByteCount = const_cast(streamBufferViews[3].GetBuffer())->GetDescriptor().m_byteCount; + RHI::BufferViewDescriptor bitangentBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, bitangentBufferByteCount); + + uint32_t uvBufferByteCount = const_cast(streamBufferViews[4].GetBuffer())->GetDescriptor().m_byteCount; + RHI::BufferViewDescriptor uvBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, uvBufferByteCount); const RHI::IndexBufferView& indexBufferView = mesh.m_indexBufferView; uint32_t indexElementSize = indexBufferView.GetIndexFormat() == RHI::IndexFormat::Uint16 ? 2 : 4; @@ -759,41 +813,124 @@ namespace AZ indexBufferDescriptor.m_elementSize = indexElementSize; indexBufferDescriptor.m_elementFormat = indexBufferView.GetIndexFormat() == RHI::IndexFormat::Uint16 ? RHI::Format::R16_UINT : RHI::Format::R32_UINT; - // retrieve the material - Data::Instance material = mesh.m_material; + // set the SubMesh data to pass to the RayTracingFeatureProcessor, starting with vertex/index data + RayTracingFeatureProcessor::SubMesh subMesh; + subMesh.m_positionFormat = PositionStreamFormat; + subMesh.m_positionVertexBufferView = streamBufferViews[0]; + subMesh.m_positionShaderBufferView = const_cast(streamBufferViews[0].GetBuffer())->GetBufferView(positionBufferDescriptor); - const MaterialAssignmentId materialAssignmentId(rayTracingLod, material ? material->GetAssetId() : AZ::Data::AssetId()); - const MaterialAssignment& materialAssignment = GetMaterialAssignmentFromMapWithFallback(m_materialAssignments, materialAssignmentId); - if (materialAssignment.m_materialInstance.get()) + subMesh.m_normalFormat = NormalStreamFormat; + subMesh.m_normalVertexBufferView = streamBufferViews[1]; + subMesh.m_normalShaderBufferView = const_cast(streamBufferViews[1].GetBuffer())->GetBufferView(normalBufferDescriptor); + + subMesh.m_tangentFormat = TangentStreamFormat; + subMesh.m_tangentVertexBufferView = streamBufferViews[2]; + subMesh.m_tangentShaderBufferView = const_cast(streamBufferViews[2].GetBuffer())->GetBufferView(tangentBufferDescriptor); + + subMesh.m_bitangentFormat = BitangentStreamFormat; + subMesh.m_bitangentVertexBufferView = streamBufferViews[3]; + subMesh.m_bitangentShaderBufferView = const_cast(streamBufferViews[3].GetBuffer())->GetBufferView(bitangentBufferDescriptor); + + if (uvBufferByteCount > 0) { - material = materialAssignment.m_materialInstance; + subMesh.m_bufferFlags |= RayTracingSubMeshBufferFlags::UV; + subMesh.m_uvFormat = UVStreamFormat; + subMesh.m_uvVertexBufferView = streamBufferViews[4]; + subMesh.m_uvShaderBufferView = const_cast(streamBufferViews[4].GetBuffer())->GetBufferView(uvBufferDescriptor); } - AZ::Color irradianceColor(1.0f, 1.0f, 1.0f, 1.0f); + subMesh.m_indexBufferView = mesh.m_indexBufferView; + subMesh.m_indexShaderBufferView = const_cast(mesh.m_indexBufferView.GetBuffer())->GetBufferView(indexBufferDescriptor); + + // add material data if (material) { + // irradiance color RPI::MaterialPropertyIndex propertyIndex = material->FindPropertyIndex(AZ::Name("irradiance.color")); if (propertyIndex.IsValid()) { - irradianceColor = material->GetPropertyValue(propertyIndex); + subMesh.m_irradianceColor = material->GetPropertyValue(propertyIndex); } propertyIndex = material->FindPropertyIndex(AZ::Name("irradiance.factor")); if (propertyIndex.IsValid()) { - irradianceColor *= material->GetPropertyValue(propertyIndex); + subMesh.m_irradianceColor *= material->GetPropertyValue(propertyIndex); + } + + // base color + propertyIndex = material->FindPropertyIndex(AZ::Name("baseColor.color")); + if (propertyIndex.IsValid()) + { + subMesh.m_baseColor = material->GetPropertyValue(propertyIndex); + } + + propertyIndex = material->FindPropertyIndex(AZ::Name("baseColor.factor")); + if (propertyIndex.IsValid()) + { + subMesh.m_baseColor *= material->GetPropertyValue(propertyIndex); + } + + // metallic + propertyIndex = material->FindPropertyIndex(AZ::Name("metallic.factor")); + if (propertyIndex.IsValid()) + { + subMesh.m_metallicFactor = material->GetPropertyValue(propertyIndex); + } + + // roughness + propertyIndex = material->FindPropertyIndex(AZ::Name("roughness.factor")); + if (propertyIndex.IsValid()) + { + subMesh.m_roughnessFactor = material->GetPropertyValue(propertyIndex); + } + + // textures + propertyIndex = material->FindPropertyIndex(AZ::Name("baseColor.textureMap")); + if (propertyIndex.IsValid()) + { + Data::Instance image = material->GetPropertyValue>(propertyIndex); + if (image.get()) + { + subMesh.m_textureFlags |= RayTracingSubMeshTextureFlags::BaseColor; + subMesh.m_baseColorImageView = image->GetImageView(); + } + } + + propertyIndex = material->FindPropertyIndex(AZ::Name("normal.textureMap")); + if (propertyIndex.IsValid()) + { + Data::Instance image = material->GetPropertyValue>(propertyIndex); + if (image.get()) + { + subMesh.m_textureFlags |= RayTracingSubMeshTextureFlags::Normal; + subMesh.m_normalImageView = image->GetImageView(); + } + } + + propertyIndex = material->FindPropertyIndex(AZ::Name("metallic.textureMap")); + if (propertyIndex.IsValid()) + { + Data::Instance image = material->GetPropertyValue>(propertyIndex); + if (image.get()) + { + subMesh.m_textureFlags |= RayTracingSubMeshTextureFlags::Metallic; + subMesh.m_metallicImageView = image->GetImageView(); + } + } + + propertyIndex = material->FindPropertyIndex(AZ::Name("roughness.textureMap")); + if (propertyIndex.IsValid()) + { + Data::Instance image = material->GetPropertyValue>(propertyIndex); + if (image.get()) + { + subMesh.m_textureFlags |= RayTracingSubMeshTextureFlags::Roughness; + subMesh.m_roughnessImageView = image->GetImageView(); + } } } - RayTracingFeatureProcessor::SubMesh subMesh; - subMesh.m_vertexFormat = StreamFormat; - subMesh.m_positionVertexBufferView = streamBufferViews[0]; - subMesh.m_positionShaderBufferView = const_cast(streamBufferViews[0].GetBuffer())->GetBufferView(vertexBufferDescriptor); - subMesh.m_normalVertexBufferView = streamBufferViews[1]; - subMesh.m_normalShaderBufferView = const_cast(streamBufferViews[1].GetBuffer())->GetBufferView(vertexBufferDescriptor); - subMesh.m_indexBufferView = mesh.m_indexBufferView; - subMesh.m_indexShaderBufferView = const_cast(mesh.m_indexBufferView.GetBuffer())->GetBufferView(indexBufferDescriptor); - subMesh.m_irradianceColor = irradianceColor; subMeshes.push_back(subMesh); } diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp index 2bb2fa2ac2..92cd41b4e8 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp @@ -115,11 +115,11 @@ namespace AZ } } - // update and compile the RayTracingSceneSrg + // update and compile the RayTracingSceneSrg and RayTracingMaterialSrg // Note: the timing of this update is very important, it needs to be updated after the TLAS is allocated so it can // be set on the RayTracingSceneSrg for this frame, and the ray tracing mesh data in the RayTracingSceneSrg must // exactly match the TLAS. Any mismatch in this data may result in a TDR. - rayTracingFeatureProcessor->UpdateRayTracingSceneSrg(); + rayTracingFeatureProcessor->UpdateRayTracingSrgs(); } } diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp index c4e9306dc9..c28ac32381 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp @@ -71,6 +71,13 @@ namespace AZ AZ_Assert(rayTracingSceneSrgAsset.IsReady(), "Failed to load RayTracingSceneSrg asset"); m_rayTracingSceneSrg = RPI::ShaderResourceGroup::Create(rayTracingSceneSrgAsset); + + // load the RayTracingMaterialSrg asset + Data::Asset rayTracingMaterialSrgAsset = + RPI::AssetUtils::LoadAssetByProductPath("shaderlib/atom/features/raytracing/raytracingmaterialsrg_raytracingmaterialsrg.azsrg", RPI::AssetUtils::TraceLevel::Error); + AZ_Assert(rayTracingMaterialSrgAsset.IsReady(), "Failed to load RayTracingMaterialSrg asset"); + + m_rayTracingMaterialSrg = RPI::ShaderResourceGroup::Create(rayTracingMaterialSrgAsset); } void RayTracingFeatureProcessor::SetMesh(const ObjectId objectId, const SubMeshVector& subMeshes) @@ -104,7 +111,7 @@ namespace AZ RHI::RayTracingBlasDescriptor blasDescriptor; blasDescriptor.Build() ->Geometry() - ->VertexFormat(subMesh.m_vertexFormat) + ->VertexFormat(subMesh.m_positionFormat) ->VertexBuffer(subMesh.m_positionVertexBufferView) ->IndexBuffer(subMesh.m_indexBufferView) ; @@ -124,6 +131,7 @@ namespace AZ m_subMeshCount += aznumeric_cast(subMeshes.size()); m_meshInfoBufferNeedsUpdate = true; + m_materialInfoBufferNeedsUpdate = true; } void RayTracingFeatureProcessor::RemoveMesh(const ObjectId objectId) @@ -142,6 +150,7 @@ namespace AZ } m_meshInfoBufferNeedsUpdate = true; + m_materialInfoBufferNeedsUpdate = true; } void RayTracingFeatureProcessor::SetMeshTransform(const ObjectId objectId, const AZ::Transform transform, const AZ::Vector3 nonUniformScale) @@ -162,14 +171,14 @@ namespace AZ m_meshInfoBufferNeedsUpdate = true; } - void RayTracingFeatureProcessor::UpdateRayTracingSceneSrg() + void RayTracingFeatureProcessor::UpdateRayTracingSrgs() { if (!m_tlas->GetTlasBuffer()) { return; } - if (m_rayTracingSceneSrg->IsQueuedForCompile()) + if (m_rayTracingSceneSrg->IsQueuedForCompile() || m_rayTracingMaterialSrg->IsQueuedForCompile()) { //[GFX TODO][ATOM-14792] AtomSampleViewer: Reset scene and feature processors before switching to sample return; @@ -178,7 +187,144 @@ namespace AZ // update the mesh info buffer with the latest ray tracing enabled meshes UpdateMeshInfoBuffer(); + // update the material info buffer with the latest ray tracing enabled meshes + UpdateMaterialInfoBuffer(); + // update the RayTracingSceneSrg + UpdateRayTracingSceneSrg(); + + // update the RayTracingMaterialSrg + UpdateRayTracingMaterialSrg(); + } + + void RayTracingFeatureProcessor::UpdateMeshInfoBuffer() + { + if (m_meshInfoBufferNeedsUpdate && (m_subMeshCount > 0)) + { + TransformServiceFeatureProcessor* transformFeatureProcessor = GetParentScene()->GetFeatureProcessor(); + + AZStd::vector meshInfos; + meshInfos.reserve(m_subMeshCount); + + uint32_t newMeshByteCount = m_subMeshCount * sizeof(MeshInfo); + + if (m_meshInfoBuffer == nullptr) + { + // allocate the MeshInfo structured buffer + RPI::CommonBufferDescriptor desc; + desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; + desc.m_bufferName = "RayTracingMeshInfo"; + desc.m_byteCount = newMeshByteCount; + desc.m_elementSize = sizeof(MeshInfo); + m_meshInfoBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); + } + else if (m_meshInfoBuffer->GetBufferSize() < newMeshByteCount) + { + // resize for the new sub-mesh count + m_meshInfoBuffer->Resize(newMeshByteCount); + } + + // keep track of the start index of the buffers for each mesh, this is put into the MeshInfo + // entry for each mesh so it knows where to find the start of its buffers in the unbounded array + uint32_t bufferStartIndex = 0; + + for (const auto& mesh : m_meshes) + { + AZ::Transform meshTransform = transformFeatureProcessor->GetTransformForId(TransformServiceFeatureProcessorInterface::ObjectId(mesh.first)); + AZ::Transform noScaleTransform = meshTransform; + noScaleTransform.ExtractScale(); + AZ::Matrix3x3 rotationMatrix = Matrix3x3::CreateFromTransform(noScaleTransform); + rotationMatrix = rotationMatrix.GetInverseFull().GetTranspose(); + + const RayTracingFeatureProcessor::SubMeshVector& subMeshes = mesh.second.m_subMeshes; + for (const auto& subMesh : subMeshes) + { + MeshInfo meshInfo; + meshInfo.m_indexOffset = subMesh.m_indexBufferView.GetByteOffset(); + meshInfo.m_positionOffset = subMesh.m_positionVertexBufferView.GetByteOffset(); + meshInfo.m_normalOffset = subMesh.m_normalVertexBufferView.GetByteOffset(); + meshInfo.m_tangentOffset = subMesh.m_tangentVertexBufferView.GetByteOffset(); + meshInfo.m_bitangentOffset = subMesh.m_bitangentVertexBufferView.GetByteOffset(); + + if (RHI::CheckBitsAll(subMesh.m_bufferFlags, RayTracingSubMeshBufferFlags::UV)) + { + meshInfo.m_uvOffset = subMesh.m_uvVertexBufferView.GetByteOffset(); + } + + subMesh.m_irradianceColor.StoreToFloat4(meshInfo.m_irradianceColor.data()); + rotationMatrix.StoreToRowMajorFloat9(meshInfo.m_worldInvTranspose.data()); + meshInfo.m_bufferFlags = subMesh.m_bufferFlags; + meshInfo.m_bufferStartIndex = bufferStartIndex; + + // add the count of buffers present in this subMesh to the start index for the next subMesh + // note that the Index, Position, Normal, Tangent, and Bitangent buffers are always counted since they are guaranteed + static const uint32_t RayTracingSubMeshFixedStreamCount = 5; + bufferStartIndex += (RayTracingSubMeshFixedStreamCount + RHI::CountBitsSet(aznumeric_cast(meshInfo.m_bufferFlags))); + + meshInfos.emplace_back(meshInfo); + } + } + + m_meshInfoBuffer->UpdateData(meshInfos.data(), newMeshByteCount); + m_meshInfoBufferNeedsUpdate = false; + } + } + + void RayTracingFeatureProcessor::UpdateMaterialInfoBuffer() + { + if (m_materialInfoBufferNeedsUpdate && (m_subMeshCount > 0)) + { + AZStd::vector materialInfos; + materialInfos.reserve(m_subMeshCount); + + uint32_t newMaterialByteCount = m_subMeshCount * sizeof(MaterialInfo); + + if (m_materialInfoBuffer == nullptr) + { + // allocate the MaterialInfo structured buffer + RPI::CommonBufferDescriptor desc; + desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; + desc.m_bufferName = "RayTracingMaterialInfo"; + desc.m_byteCount = newMaterialByteCount; + desc.m_elementSize = sizeof(MaterialInfo); + m_materialInfoBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); + } + else if (m_materialInfoBuffer->GetBufferSize() < newMaterialByteCount) + { + // resize for the new sub-mesh count + m_materialInfoBuffer->Resize(newMaterialByteCount); + } + + // keep track of the start index of the textures for each mesh, this is put into the MaterialInfo + // entry for each mesh so it knows where to find the start of its textures in the unbounded array + uint32_t textureStartIndex = 0; + + for (const auto& mesh : m_meshes) + { + const RayTracingFeatureProcessor::SubMeshVector& subMeshes = mesh.second.m_subMeshes; + for (const auto& subMesh : subMeshes) + { + MaterialInfo materialInfo; + subMesh.m_baseColor.StoreToFloat4(materialInfo.m_baseColor.data()); + materialInfo.m_metallicFactor = subMesh.m_metallicFactor; + materialInfo.m_roughnessFactor = subMesh.m_roughnessFactor; + materialInfo.m_textureFlags = subMesh.m_textureFlags; + materialInfo.m_textureStartIndex = textureStartIndex; + + // add the count of textures present in this subMesh to the start index for the next subMesh + textureStartIndex += RHI::CountBitsSet(aznumeric_cast(materialInfo.m_textureFlags)); + + materialInfos.emplace_back(materialInfo); + } + } + + m_materialInfoBuffer->UpdateData(materialInfos.data(), newMaterialByteCount); + m_materialInfoBufferNeedsUpdate = false; + } + } + + void RayTracingFeatureProcessor::UpdateRayTracingSceneSrg() + { const RHI::ShaderResourceGroupLayout* srgLayout = m_rayTracingSceneSrg->GetLayout(); RHI::ShaderInputImageIndex imageIndex; RHI::ShaderInputBufferIndex bufferIndex; @@ -272,11 +418,18 @@ namespace AZ const SubMeshVector& subMeshes = mesh.second.m_subMeshes; for (const auto& subMesh : subMeshes) { - // add the index, position, and normal buffers for this sub-mesh to the mesh buffer list, this will - // go into the shader as an unbounded array in the Srg + // add the stream buffers for this sub-mesh to the mesh buffer list, + // this is sent to the shader as an unbounded array in the Srg meshBuffers.push_back(subMesh.m_indexShaderBufferView.get()); meshBuffers.push_back(subMesh.m_positionShaderBufferView.get()); meshBuffers.push_back(subMesh.m_normalShaderBufferView.get()); + meshBuffers.push_back(subMesh.m_tangentShaderBufferView.get()); + meshBuffers.push_back(subMesh.m_bitangentShaderBufferView.get()); + + if (RHI::CheckBitsAll(subMesh.m_bufferFlags, RayTracingSubMeshBufferFlags::UV)) + { + meshBuffers.push_back(subMesh.m_uvShaderBufferView.get()); + } } } @@ -287,58 +440,53 @@ namespace AZ m_rayTracingSceneSrg->Compile(); } - void RayTracingFeatureProcessor::UpdateMeshInfoBuffer() + void RayTracingFeatureProcessor::UpdateRayTracingMaterialSrg() { - if (m_meshInfoBufferNeedsUpdate && (m_subMeshCount > 0)) - { - TransformServiceFeatureProcessor* transformFeatureProcessor = GetParentScene()->GetFeatureProcessor(); - - AZStd::vector meshInfos; - meshInfos.reserve(m_subMeshCount); + const RHI::ShaderResourceGroupLayout* srgLayout = m_rayTracingMaterialSrg->GetLayout(); + RHI::ShaderInputImageIndex imageIndex; + RHI::ShaderInputBufferIndex bufferIndex; + RHI::ShaderInputConstantIndex constantIndex; - uint32_t newMeshByteCount = m_subMeshCount * sizeof(MeshInfo); - - if (m_meshInfoBuffer == nullptr) - { - // allocate the MeshInfo structured buffer - RPI::CommonBufferDescriptor desc; - desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; - desc.m_bufferName = "RayTracingMeshInfo"; - desc.m_byteCount = newMeshByteCount; - desc.m_elementSize = sizeof(MeshInfo); - m_meshInfoBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); - } - else if (m_meshInfoBuffer->GetBufferSize() < newMeshByteCount) - { - // resize for the new sub-mesh count - m_meshInfoBuffer->Resize(newMeshByteCount); - } + bufferIndex = srgLayout->FindShaderInputBufferIndex(AZ::Name("m_materialInfo")); + m_rayTracingMaterialSrg->SetBufferView(bufferIndex, m_materialInfoBuffer->GetBufferView()); + if (m_subMeshCount) + { + AZStd::vector materialTextures; for (const auto& mesh : m_meshes) { - AZ::Transform meshTransform = transformFeatureProcessor->GetTransformForId(TransformServiceFeatureProcessorInterface::ObjectId(mesh.first)); - AZ::Transform noScaleTransform = meshTransform; - noScaleTransform.ExtractScale(); - AZ::Matrix3x3 rotationMatrix = Matrix3x3::CreateFromTransform(noScaleTransform); - rotationMatrix = rotationMatrix.GetInverseFull().GetTranspose(); - - const RayTracingFeatureProcessor::SubMeshVector& subMeshes = mesh.second.m_subMeshes; + const SubMeshVector& subMeshes = mesh.second.m_subMeshes; for (const auto& subMesh : subMeshes) { - MeshInfo meshInfo; - meshInfo.m_indexOffset = subMesh.m_indexBufferView.GetByteOffset(); - meshInfo.m_positionOffset = subMesh.m_positionVertexBufferView.GetByteOffset(); - meshInfo.m_normalOffset = subMesh.m_normalVertexBufferView.GetByteOffset(); - subMesh.m_irradianceColor.StoreToFloat4(meshInfo.m_irradianceColor.data()); - rotationMatrix.StoreToRowMajorFloat9(meshInfo.m_worldInvTranspose.data()); - - meshInfos.emplace_back(meshInfo); + // add the baseColor, normal, metallic, and roughness images for this sub-mesh to the material texture list, + // this is sent to the shader as an unbounded array in the Srg + if (RHI::CheckBitsAll(subMesh.m_textureFlags, RayTracingSubMeshTextureFlags::BaseColor)) + { + materialTextures.push_back(subMesh.m_baseColorImageView.get()); + } + + if (RHI::CheckBitsAll(subMesh.m_textureFlags, RayTracingSubMeshTextureFlags::Normal)) + { + materialTextures.push_back(subMesh.m_normalImageView.get()); + } + + if (RHI::CheckBitsAll(subMesh.m_textureFlags, RayTracingSubMeshTextureFlags::Metallic)) + { + materialTextures.push_back(subMesh.m_metallicImageView.get()); + } + + if (RHI::CheckBitsAll(subMesh.m_textureFlags, RayTracingSubMeshTextureFlags::Roughness)) + { + materialTextures.push_back(subMesh.m_roughnessImageView.get()); + } } } - m_meshInfoBuffer->UpdateData(meshInfos.data(), newMeshByteCount); - m_meshInfoBufferNeedsUpdate = false; + RHI::ShaderInputImageUnboundedArrayIndex textureUnboundedArrayIndex = srgLayout->FindShaderInputImageUnboundedArrayIndex(AZ::Name("m_materialTextures")); + m_rayTracingMaterialSrg->SetImageViewUnboundedArray(textureUnboundedArrayIndex, materialTextures); } + + m_rayTracingMaterialSrg->Compile(); } } } diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h index f317f1c096..d89b61b2f9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -23,6 +24,28 @@ namespace AZ { namespace Render { + static const uint32_t RayTracingGlobalSrgBindingSlot = 0; + static const uint32_t RayTracingSceneSrgBindingSlot = 1; + static const uint32_t RayTracingMaterialSrgBindingSlot = 2; + + enum class RayTracingSubMeshBufferFlags : uint32_t + { + None = 0, + + UV = AZ_BIT(0) + }; + AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::Render::RayTracingSubMeshBufferFlags); + + enum class RayTracingSubMeshTextureFlags : uint32_t + { + None = 0, + BaseColor = AZ_BIT(0), + Normal = AZ_BIT(1), + Metallic = AZ_BIT(2), + Roughness = AZ_BIT(3) + }; + AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::Render::RayTracingSubMeshTextureFlags); + //! This feature processor manages ray tracing data for a Scene class RayTracingFeatureProcessor : public RPI::FeatureProcessor @@ -42,20 +65,53 @@ namespace AZ //! Contains data for a single sub-mesh struct SubMesh { - // vertex/index buffer data - RHI::Format m_vertexFormat = RHI::Format::Unknown; + // vertex streams + RHI::Format m_positionFormat = RHI::Format::Unknown; RHI::StreamBufferView m_positionVertexBufferView; RHI::Ptr m_positionShaderBufferView; + + RHI::Format m_normalFormat = RHI::Format::Unknown; RHI::StreamBufferView m_normalVertexBufferView; RHI::Ptr m_normalShaderBufferView; + + RHI::Format m_tangentFormat = RHI::Format::Unknown; + RHI::StreamBufferView m_tangentVertexBufferView; + RHI::Ptr m_tangentShaderBufferView; + + RHI::Format m_bitangentFormat = RHI::Format::Unknown; + RHI::StreamBufferView m_bitangentVertexBufferView; + RHI::Ptr m_bitangentShaderBufferView; + + RHI::Format m_uvFormat = RHI::Format::Unknown; + RHI::StreamBufferView m_uvVertexBufferView; + RHI::Ptr m_uvShaderBufferView; + + // index buffer RHI::IndexBufferView m_indexBufferView; RHI::Ptr m_indexShaderBufferView; + // vertex buffer usage flags + RayTracingSubMeshBufferFlags m_bufferFlags = RayTracingSubMeshBufferFlags::None; + // color of the bounced light from this sub-mesh - AZ::Color m_irradianceColor; + AZ::Color m_irradianceColor = AZ::Color(1.0f); // ray tracing Blas RHI::Ptr m_blas; + + // material data + AZ::Color m_baseColor = AZ::Color(0.0f); + float m_metallicFactor = 0.0f; + float m_roughnessFactor = 0.0f; + + // material texture usage flags + RayTracingSubMeshTextureFlags m_textureFlags = RayTracingSubMeshTextureFlags::None; + + // material textures + RHI::Ptr m_baseColorImageView; + RHI::Ptr m_normalImageView; + RHI::Ptr m_metallicImageView; + RHI::Ptr m_roughnessImageView; }; using SubMeshVector = AZStd::vector; @@ -98,6 +154,9 @@ namespace AZ //! Retrieves the RayTracingSceneSrg Data::Instance GetRayTracingSceneSrg() const { return m_rayTracingSceneSrg; } + //! Retrieves the RayTracingMaterialSrg + Data::Instance GetRayTracingMaterialSrg() const { return m_rayTracingMaterialSrg; } + //! Retrieves the RayTracingTlas const RHI::Ptr& GetTlas() const { return m_tlas; } RHI::Ptr& GetTlas() { return m_tlas; } @@ -118,14 +177,20 @@ namespace AZ //! Retrieves the GPU buffer containing information for all ray tracing meshes. const Data::Instance GetMeshInfoBuffer() const { return m_meshInfoBuffer; } - //! Updates the RayTracingSceneSrg, called after the TLAS is allocated in the RayTracingAccelerationStructurePass - void UpdateRayTracingSceneSrg(); + //! Retrieves the GPU buffer containing information for all ray tracing materials. + const Data::Instance GetMaterialInfoBuffer() const { return m_materialInfoBuffer; } + + //! Updates the RayTracingSceneSrg and RayTracingMaterialSrg, called after the TLAS is allocated in the RayTracingAccelerationStructurePass + void UpdateRayTracingSrgs(); private: AZ_DISABLE_COPY_MOVE(RayTracingFeatureProcessor); void UpdateMeshInfoBuffer(); + void UpdateMaterialInfoBuffer(); + void UpdateRayTracingSceneSrg(); + void UpdateRayTracingMaterialSrg(); // flag indicating if RayTracing is enabled, currently based on device support bool m_rayTracingEnabled = false; @@ -143,6 +208,9 @@ namespace AZ // ray tracing scene Srg Data::Instance m_rayTracingSceneSrg; + // ray tracing material Srg + Data::Instance m_rayTracingMaterialSrg; + // current revision number of ray tracing data uint32_t m_revision = 0; @@ -158,18 +226,43 @@ namespace AZ // structure for data in the m_meshInfoBuffer, shaders that use the buffer must match this type struct MeshInfo { - uint32_t m_indexOffset; - uint32_t m_positionOffset; - uint32_t m_normalOffset; + uint32_t m_indexOffset; + uint32_t m_positionOffset; + uint32_t m_normalOffset; + uint32_t m_tangentOffset; + uint32_t m_bitangentOffset; + uint32_t m_uvOffset; + float m_padding0[2]; + AZStd::array m_irradianceColor; // float4 AZStd::array m_worldInvTranspose; // float3x3 + float m_padding1[1]; + + RayTracingSubMeshBufferFlags m_bufferFlags = RayTracingSubMeshBufferFlags::None; + uint32_t m_bufferStartIndex = 0; }; // buffer containing a MeshInfo for each sub-mesh Data::Instance m_meshInfoBuffer; - // flag indicating we need to update the mesh info GPU buffer + // structure for data in the m_materialInfoBuffer, shaders that use the buffer must match this type + struct MaterialInfo + { + AZStd::array m_baseColor; // float4 + float m_metallicFactor = 0.0f; + float m_roughnessFactor = 0.0f; + RayTracingSubMeshTextureFlags m_textureFlags = RayTracingSubMeshTextureFlags::None; + uint32_t m_textureStartIndex = 0; + }; + + // buffer containing a MaterialInfo for each sub-mesh + Data::Instance m_materialInfoBuffer; + + // flag indicating we need to update the meshInfo buffer bool m_meshInfoBufferNeedsUpdate = false; + + // flag indicating we need to update the materialInfo buffer + bool m_materialInfoBufferNeedsUpdate = false; }; } } diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.cpp index 2c0c7e986e..0fbe7dbc48 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.cpp @@ -111,7 +111,6 @@ namespace AZ AZ_Assert(m_globalPipelineState, "Failed to acquire ray tracing global pipeline state"); // create global srg - static const uint32_t RayTracingGlobalSrgBindingSlot = 0; Data::Asset globalSrgAsset = m_rayGenerationShader->FindShaderResourceGroupAsset(RayTracingGlobalSrgBindingSlot); AZ_Error("PassSystem", globalSrgAsset.GetId().IsValid(), "RayTracingPass [%s] Failed to find RayTracingGlobalSrg asset", GetPathName().GetCStr()); AZ_Error("PassSystem", globalSrgAsset.IsReady(), "RayTracingPass [%s] asset is not loaded for shader", GetPathName().GetCStr()); @@ -120,10 +119,13 @@ namespace AZ AZ_Assert(m_shaderResourceGroup, "RayTracingPass [%s]: Failed to create RayTracingGlobalSrg", GetPathName().GetCStr()); RPI::PassUtils::BindDataMappingsToSrg(m_passDescriptor, m_shaderResourceGroup.get()); - // check to see if the shader requires a ViewSrg + // check to see if the shader requires the View and RayTracingMaterial Srgs Data::Asset viewSrgAsset = m_rayGenerationShader->FindShaderResourceGroupAsset(RPI::SrgBindingSlot::View); m_requiresViewSrg = viewSrgAsset.GetId().IsValid(); + Data::Asset rayTracingMaterialSrgAsset = m_rayGenerationShader->FindShaderResourceGroupAsset(RayTracingMaterialSrgBindingSlot); + m_requiresRayTracingMaterialSrg = rayTracingMaterialSrgAsset.GetId().IsValid(); + // build the ray tracing pipeline state descriptor RHI::RayTracingPipelineStateDescriptor descriptor; descriptor.Build() @@ -298,6 +300,11 @@ namespace AZ } } + if (m_requiresRayTracingMaterialSrg) + { + shaderResourceGroups.push_back(rayTracingFeatureProcessor->GetRayTracingMaterialSrg()->GetRHIShaderResourceGroup()); + } + dispatchRaysItem.m_shaderResourceGroupCount = aznumeric_cast(shaderResourceGroups.size()); dispatchRaysItem.m_shaderResourceGroups = shaderResourceGroups.data(); dispatchRaysItem.m_rayTracingPipelineState = m_rayTracingPipelineState.get(); diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.h b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.h index 935d034513..6ad082e894 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.h @@ -76,6 +76,7 @@ namespace AZ RHI::ConstPtr m_globalPipelineState; RHI::Ptr m_rayTracingShaderTable; bool m_requiresViewSrg = false; + bool m_requiresRayTracingMaterialSrg = false; }; } // namespace RPI } // namespace AZ