From 9557b57fa30007e065a887971be92afd739af3b1 Mon Sep 17 00:00:00 2001 From: Nikita Krupitskas Date: Sun, 24 Nov 2024 16:15:51 +0100 Subject: [PATCH] Finally RTX Pathtracer index indices, vertices and material data correct, both outputs shows correct albedo --- Shaders/ForwardPassPS.hlsl | 3 +- Shaders/HitRT.hlsl | 128 +++++++++++-------- Shaders/MissRT.hlsl | 14 +- Shaders/RayGenRT.hlsl | 65 ++++++++-- Yasno/Graphics/RenderScene.hpp | 12 -- Yasno/Graphics/Techniques/RaytracingPass.cpp | 42 +++++- Yasno/Renderer/RaytracingContext.cpp | 4 +- 7 files changed, 179 insertions(+), 89 deletions(-) diff --git a/Shaders/ForwardPassPS.hlsl b/Shaders/ForwardPassPS.hlsl index 2132c01..01f4aa9 100644 --- a/Shaders/ForwardPassPS.hlsl +++ b/Shaders/ForwardPassPS.hlsl @@ -183,5 +183,6 @@ float4 main(RS2PS input) : SV_Target float3 f = C_ambient + C_diffuse * in_shadow + emissive.xyz; // + cubeMapSample.xyz + Specular - return float4(f.xyz, 1.0); + return float4(base_color.rgb, 1); + //return float4(f.xyz, 1.0); } diff --git a/Shaders/HitRT.hlsl b/Shaders/HitRT.hlsl index 9c934e9..f58ae27 100644 --- a/Shaders/HitRT.hlsl +++ b/Shaders/HitRT.hlsl @@ -9,7 +9,17 @@ // D3D12_RAYTRACING_SHADER_CONFIG pipeline subobjet. struct HitInfo { - float4 color_distance; + float4 encoded_normals; // Octahedron encoded + + float3 hit_position; + uint material_id; + + float2 uvs; + + bool has_hit() + { + return material_id != -1; + } }; // Attributes output by the raytracing when hitting a surface, @@ -19,15 +29,35 @@ struct Attributes float2 bary; }; -inline uint PackInstanceID(uint material_id, uint geometry_id) +// octahedron encoding of normals +float2 octWrap(float2 v) +{ + return float2((1.0f - abs(v.y)) * (v.x >= 0.0f ? 1.0f : -1.0f), (1.0f - abs(v.x)) * (v.y >= 0.0f ? 1.0f : -1.0f)); +} + +float2 encodeNormalOctahedron(float3 n) { - return ((geometry_id & 0x3FFF) << 10) | (material_id & 0x3FF); + float2 p = float2(n.x, n.y) * (1.0f / (abs(n.x) + abs(n.y) + abs(n.z))); + p = (n.z < 0.0f) ? octWrap(p) : p; + return p; } -inline void UnpackInstanceID(uint instanceID, out uint material_id, out uint geometry_id) +float3 decodeNormalOctahedron(float2 p) { - material_id = instanceID & 0x3FF; - geometry_id = (instanceID >> 10) & 0x3FFF; + float3 n = float3(p.x, p.y, 1.0f - abs(p.x) - abs(p.y)); + float2 tmp = (n.z < 0.0f) ? octWrap(float2(n.x, n.y)) : float2(n.x, n.y); + n.x = tmp.x; + n.y = tmp.y; + return normalize(n); +} + +float4 EncodeNormals(float3 geometryNormal, float3 shadingNormal) { + return float4(encodeNormalOctahedron(geometryNormal), encodeNormalOctahedron(shadingNormal)); +} + +void DecodeNormals(float4 encodedNormals, out float3 geometryNormal, out float3 shadingNormal) { + geometryNormal = decodeNormalOctahedron(encodedNormals.xy); + shadingNormal = decodeNormalOctahedron(encodedNormals.zw); } // Vertex data for usage in Hit shader @@ -84,10 +114,9 @@ uint3 GetIndices(uint geometry_id, uint triangle_index) { PerInstanceData data = PerInstanceBuffer[geometry_id]; - uint base_index = (triangle_index * 3); - int address = data.indices_before + base_index; + int address = data.indices_before + triangle_index * 3; - uint i0 = IndexBuffer[address]; + uint i0 = IndexBuffer[address + 0]; uint i1 = IndexBuffer[address + 1]; uint i2 = IndexBuffer[address + 2]; @@ -101,42 +130,38 @@ VertexData GetVertexData(uint geometry_id, uint triangle_index, float3 barycentr VertexData v = (VertexData)0; + VertexLayout input_vertex[3]; + float3 triangle_vertices[3]; + int address = PerInstanceBuffer[geometry_id].vertices_before; + // Interpolate the vertex attributes - //for (uint i = 0; i < 3; i++) - //{ - // //int address = (indices[i] * 12) * 4; - // int addess = VertexBuffer[geometry_id].vertices_before; - // - // // Load and interpolate position and transform it to world space - // triangle_vertices[i] = mul(ObjectToWorld3x4(), float4(asfloat(VertexBuffer[geometry_id].Load3(address)), 1.0f)).xyz; - // v.position += triangle_vertices[i] * barycentrics[i]; - // address += 12; - // - // // Load and interpolate normal - // v.normal += asfloat(VertexBuffer[geometry_id].Load3(address)) * barycentrics[i]; - // address += 12; - // - // // Load and interpolate tangent - // address += 12; - // - // // Load bitangent direction - // address += 4; - // - // // Load and interpolate texture coordinates - // v.uv += asfloat(vertices[geometry_id].Load2(address)) * barycentrics[i]; - //} - - //// Transform normal from local to world space - //v.normal = normalize(mul(ObjectToWorld3x4(), float4(v.normal, 0.0f)).xyz); - - //// Calculate geometry normal from triangle vertices positions - //float3 edge20 = triangle_vertices[2] - triangle_vertices[0]; - //float3 edge21 = triangle_vertices[2] - triangle_vertices[1]; - //float3 edge10 = triangle_vertices[1] - triangle_vertices[0]; - - //v.geometryNormal = normalize(cross(edge20, edge10)); + for (uint i = 0; i < 3; i++) + { + input_vertex[i] = VertexBuffer[address + indices[i]]; + + // Load and interpolate position and transform it to world space + triangle_vertices[i] = mul(ObjectToWorld3x4(), float4(input_vertex[i].position.xyz, 1.0f)).xyz; + + v.position += triangle_vertices[i] * barycentrics[i]; + + // Load and interpolate normal + v.shading_normal += input_vertex[i].normal * barycentrics[i]; + + // Load and interpolate texture coordinates + v.uv += input_vertex[i].texcoord_0 * barycentrics[i]; + } + + // Transform normal from local to world space + v.shading_normal = normalize(mul(ObjectToWorld3x4(), float4(v.shading_normal, 0.0f)).xyz); + + // Calculate geometry normal from triangle vertices positions + float3 edge20 = triangle_vertices[2] - triangle_vertices[0]; + float3 edge21 = triangle_vertices[2] - triangle_vertices[1]; + float3 edge10 = triangle_vertices[1] - triangle_vertices[0]; + + v.geometry_normal = normalize(cross(edge20, edge10)); return v; } @@ -144,21 +169,12 @@ VertexData GetVertexData(uint geometry_id, uint triangle_index, float3 barycentr [shader("closesthit")] void ClosestHit(inout HitInfo payload, Attributes attrib) { - uint primitive_index = PrimitiveIndex(); - float3 barycentrics = float3(1.f - attrib.bary.x - attrib.bary.y, attrib.bary.x, attrib.bary.y); - uint material_id; - uint geometry_id; - UnpackInstanceID(primitive_index, material_id, geometry_id); - - VertexData vertex_data = GetVertexData(geometry_id, primitive_index, barycentrics); - - const float3 A = float3(1, 0, 0); - const float3 B = float3(0, 1, 0); - const float3 C = float3(0, 0, 1); - - float3 hit_color = A * barycentrics.x + B * barycentrics.y + C * barycentrics.z; + VertexData vertex = GetVertexData(InstanceIndex(), PrimitiveIndex(), barycentrics); - payload.color_distance = float4(hit_color, RayTCurrent()); + payload.encoded_normals = EncodeNormals(vertex.geometry_normal, vertex.shading_normal); + payload.hit_position = vertex.position; + payload.material_id = PerInstanceBuffer[InstanceIndex()].material_id; + payload.uvs = vertex.uv; } diff --git a/Shaders/MissRT.hlsl b/Shaders/MissRT.hlsl index 6bea3ad..02854fe 100644 --- a/Shaders/MissRT.hlsl +++ b/Shaders/MissRT.hlsl @@ -9,7 +9,17 @@ // D3D12_RAYTRACING_SHADER_CONFIG pipeline subobjet. struct HitInfo { - float4 color_distance; + float4 encoded_normals; // Octahedron encoded + + float3 hit_position; + uint material_id; + + float2 uvs; + + bool has_hit() + { + return material_id != -1; + } }; // Attributes output by the raytracing when hitting a surface, here the barycentric coordinates @@ -21,5 +31,5 @@ struct Attributes [shader("miss")] void Miss(inout HitInfo payload : SV_RayPayload) { - payload.color_distance = float4(0.2f, 0.2f, 0.8f, -1.f); + payload.material_id = -1; } diff --git a/Shaders/RayGenRT.hlsl b/Shaders/RayGenRT.hlsl index e359ece..3041693 100644 --- a/Shaders/RayGenRT.hlsl +++ b/Shaders/RayGenRT.hlsl @@ -2,9 +2,17 @@ struct HitInfo { - float4 color_distance; + float4 encoded_normals; // Octahedron encoded - bool has_hit; + float3 hit_position; + uint material_id; + + float2 uvs; + + bool has_hit() + { + return material_id != -1; + } }; // Attributes output by the raytracing when hitting a surface, @@ -81,6 +89,38 @@ uint4 Pcg4d(uint4 v) return v; } + +// octahedron encoding of normals +float2 octWrap(float2 v) +{ + return float2((1.0f - abs(v.y)) * (v.x >= 0.0f ? 1.0f : -1.0f), (1.0f - abs(v.x)) * (v.y >= 0.0f ? 1.0f : -1.0f)); +} + +float2 encodeNormalOctahedron(float3 n) +{ + float2 p = float2(n.x, n.y) * (1.0f / (abs(n.x) + abs(n.y) + abs(n.z))); + p = (n.z < 0.0f) ? octWrap(p) : p; + return p; +} + +float3 decodeNormalOctahedron(float2 p) +{ + float3 n = float3(p.x, p.y, 1.0f - abs(p.x) - abs(p.y)); + float2 tmp = (n.z < 0.0f) ? octWrap(float2(n.x, n.y)) : float2(n.x, n.y); + n.x = tmp.x; + n.y = tmp.y; + return normalize(n); +} + +float4 EncodeNormals(float3 geometryNormal, float3 shadingNormal) { + return float4(encodeNormalOctahedron(geometryNormal), encodeNormalOctahedron(shadingNormal)); +} + +void DecodeNormals(float4 encodedNormals, out float3 geometryNormal, out float3 shadingNormal) { + geometryNormal = decodeNormalOctahedron(encodedNormals.xy); + shadingNormal = decodeNormalOctahedron(encodedNormals.zw); +} + struct VertexLayout { float3 position; @@ -128,7 +168,6 @@ void RayGen() { // Initialize the ray payload HitInfo payload; - payload.color_distance = float4(0, 0, 0, 0); // Get the location within the dispatched 2D grid of work items // (often maps to pixels, so this could represent a pixel coordinate). @@ -163,13 +202,23 @@ void RayGen() payload ); - SurfaceShaderParameters mat = MaterialBuffer[0]; + float2 uvs = float2(0.0f, 0.0f); - Texture2D albedo_texture = ResourceDescriptorHeap[mat.albedo_texture_index]; + if(!payload.has_hit()) + { + gOutput[pixel_xy] = float4(255, 0, 0, 1.f); + return; + } - float2 uvs = float2(0.0f, 0.0f); + float3 geometry_normal; + float3 shading_normal; + DecodeNormals(payload.encoded_normals, geometry_normal, shading_normal); + + SurfaceShaderParameters mat = MaterialBuffer[payload.material_id]; + + Texture2D albedo_texture = ResourceDescriptorHeap[mat.albedo_texture_index]; - float3 albedo_color = albedo_texture.SampleLevel(LinearSampler, uvs, 0.0f).rgb; + float3 albedo_color = albedo_texture.SampleLevel(LinearSampler, payload.uvs, 0.0f).rgb; - gOutput[pixel_xy] = float4(payload.color_distance.rgb * albedo_color.rgb, 1.f); + gOutput[pixel_xy] = float4(albedo_color, 1.f); } diff --git a/Yasno/Graphics/RenderScene.hpp b/Yasno/Graphics/RenderScene.hpp index 2cc3e90..a5a4675 100644 --- a/Yasno/Graphics/RenderScene.hpp +++ b/Yasno/Graphics/RenderScene.hpp @@ -45,18 +45,6 @@ namespace ysn std::vector sampler_descs; }; - // TODO: Move to some shared place between C++ and HLSL - inline uint32_t PackInstanceID(uint32_t material_id, uint32_t geometry_id) - { - return ((geometry_id & 0x3FFF) << 10) | (material_id & 0x3FF); - } - - inline void UnpackInstanceID(uint32_t instance_id, uint32_t& material_id, uint32_t& geometry_id) - { - material_id = instance_id & 0x3FF; - geometry_id = (instance_id >> 10) & 0x3FFF; - } - // Per instance data for rendering, this can be split into smaller parts YSN_SHADER_STRUCT RenderInstanceData { diff --git a/Yasno/Graphics/Techniques/RaytracingPass.cpp b/Yasno/Graphics/Techniques/RaytracingPass.cpp index ea622eb..981de77 100644 --- a/Yasno/Graphics/Techniques/RaytracingPass.cpp +++ b/Yasno/Graphics/Techniques/RaytracingPass.cpp @@ -13,6 +13,14 @@ namespace ysn { + struct HitInfo + { + DirectX::XMFLOAT4 encoded_normals; // Octahedron encoded + DirectX::XMFLOAT3 hit_position; + uint32_t material_id; + DirectX::XMFLOAT2 uvs; + }; + // The ray generation shader needs to access 2 resources: the raytracing output // and the top-level acceleration structure wil::com_ptr RaytracingPass::CreateRayGenSignature(std::shared_ptr renderer) @@ -36,6 +44,16 @@ namespace ysn wil::com_ptr RaytracingPass::CreateHitSignature(std::shared_ptr renderer) { nv_helpers_dx12::RootSignatureGenerator rsc; + + rsc.AddHeapRangesParameter( + { {0 /*u0*/, 1 /*1 descriptor */, 0 /*use the implicit register space 0*/, D3D12_DESCRIPTOR_RANGE_TYPE_UAV /* UAV representing the output buffer*/, 0 /*heap slot where the UAV is defined*/}, + {0 /*t0*/, 1, 0, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1}, // TLAS + {1 /*t1*/, 1, 0, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 2}, // VertexBuffer + {2 /*t2*/, 1, 0, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 3}, // IndexBuffer + {3 /*t3*/, 1, 0, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 4}, // MaterialBuffer + {4 /*t4*/, 1, 0, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 5}, // PerInstanceBuffer + {0 /*b0*/, 1, 0, D3D12_DESCRIPTOR_RANGE_TYPE_CBV /*Camera parameters*/, 6} }); + return rsc.Generate(renderer->GetDevice().get(), true, m_static_samplers); } @@ -44,6 +62,16 @@ namespace ysn wil::com_ptr RaytracingPass::CreateMissSignature(std::shared_ptr renderer) { nv_helpers_dx12::RootSignatureGenerator rsc; + + rsc.AddHeapRangesParameter( + { {0 /*u0*/, 1 /*1 descriptor */, 0 /*use the implicit register space 0*/, D3D12_DESCRIPTOR_RANGE_TYPE_UAV /* UAV representing the output buffer*/, 0 /*heap slot where the UAV is defined*/}, + {0 /*t0*/, 1, 0, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1}, // TLAS + {1 /*t1*/, 1, 0, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 2}, // VertexBuffer + {2 /*t2*/, 1, 0, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 3}, // IndexBuffer + {3 /*t3*/, 1, 0, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 4}, // MaterialBuffer + {4 /*t4*/, 1, 0, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 5}, // PerInstanceBuffer + {0 /*b0*/, 1, 0, D3D12_DESCRIPTOR_RANGE_TYPE_CBV /*Camera parameters*/, 6} }); + return rsc.Generate(renderer->GetDevice().get(), true, m_static_samplers); } @@ -190,7 +218,7 @@ namespace ysn // exchanged between shaders, such as the HitInfo structure in the HLSL code. // It is important to keep this value as low as possible as a too high value // would result in unnecessary memory consumption and cache trashing. - pipeline.SetMaxPayloadSize(4 * sizeof(float)); // RGB + distance + pipeline.SetMaxPayloadSize(sizeof(HitInfo)); // Upon hitting a surface, DXR can provide several attributes to the hit. In // our sample we just use the barycentric coordinates defined by the weights @@ -258,7 +286,7 @@ namespace ysn // The pointer to the beginning of the heap is the only parameter required by shaders without root parameters //const auto srvUavHeapHandle = renderer->GetCbvSrvUavDescriptorHeap()->GetNewHandle(); - // First SRV again scene + // 1. SRV again scene const auto scene_color_uav_handle = renderer->GetCbvSrvUavDescriptorHeap()->GetNewHandle(); { D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; @@ -268,10 +296,10 @@ namespace ysn renderer->GetDevice()->CreateUnorderedAccessView(scene_color.get(), nullptr, &uavDesc, scene_color_uav_handle.cpu); } - // Second SRV - TLAS + // 2. SRV - TLAS rtx_context.CreateTlasSrv(renderer); - // Third SRV - Vertex Buffer + // 3. SRV - Vertex Buffer { // Create SRV D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {}; @@ -286,7 +314,7 @@ namespace ysn renderer->GetDevice()->CreateShaderResourceView(vertex_buffer.get(), &srv_desc, vertices_buffer_srv.cpu); } - // Fourth SRV - Index Buffer + // 4. SRV - Index Buffer { // Create SRV D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {}; @@ -352,10 +380,10 @@ namespace ysn // The miss and hit shaders do not access any external resources: instead they // communicate their results through the ray payload - m_sbt_helper.AddMissProgram(L"Miss", {}); + m_sbt_helper.AddMissProgram(L"Miss", { heap_pointer }); // Adding the triangle hit shader - m_sbt_helper.AddHitGroup(L"HitGroup", {}); + m_sbt_helper.AddHitGroup(L"HitGroup", { heap_pointer }); // Compute the size of the SBT given the number of shaders and their // parameters diff --git a/Yasno/Renderer/RaytracingContext.cpp b/Yasno/Renderer/RaytracingContext.cpp index a0106d5..64d76ab 100644 --- a/Yasno/Renderer/RaytracingContext.cpp +++ b/Yasno/Renderer/RaytracingContext.cpp @@ -167,8 +167,6 @@ void ysn::RaytracingContext::CreateAccelerationStructures(wil::com_ptr