这是完全可行的,这里有一些 HLSL 代码,可以执行该操作(并且还可以处理以相同距离击中 2 个三角形的情况)。
我假设您知道如何创建资源(结构化缓冲区)并将它们绑定到计算管道。
另外,我会考虑您的几何图形已索引。
第一步是收集通过测试的三角形。我们将使用追加缓冲区来仅推送通过测试的元素,而不是使用“Hit”标志。
首先创建 2 个结构化缓冲区(位置和三角形索引),并将模型数据复制到其中。
然后创建一个带有可追加无序视图的结构化缓冲区。
要执行命中检测,您可以使用以下计算代码:
struct rayHit
{
uint triangleID;
float distanceToTriangle;
};
cbuffer cbRaySettings : register(b0)
{
float3 rayFrom;
float3 rayDir;
uint TriangleCount;
};
StructuredBuffer<float3> positionBuffer : register(t0);
StructuredBuffer<uint3> indexBuffer : register(t1);
AppendStructuredBuffer<rayHit> appendRayHitBuffer : register(u0);
void TestTriangle(float3 p1, float3 p2, float3 p3, out bool hit, out float d)
{
//Perform ray/triangle intersection
hit = false;
d = 0.0f;
}
[numthreads(64,1,1)]
void CS_RayAppend(uint3 tid : SV_DispatchThreadID)
{
if (tid.x >= TriangleCount)
return;
uint3 indices = indexBuffer[tid.x];
float3 p1 = positionBuffer[indices.x];
float3 p2 = positionBuffer[indices.y];
float3 p3 = positionBuffer[indices.z];
bool hit;
float d;
TestTriangle(p1,p2,p3,hit, d);
if (hit)
{
rayHit hitData;
hitData.triangleID = tid.x;
hitData.distanceToTriangle = d;
appendRayHitBuffer.Append(hitData);
}
}
请注意,您需要为appendRayHitBuffer提供足够的大小(最坏的情况是三角形计数,例如:每个三角形都被射线击中)。
完成此操作后,缓冲区的开始部分将包含命中数据,并且无序视图计数器会计算通过测试的三角形的数量。
然后你需要创建一个参数缓冲区和一个小的字节地址缓冲区(大小为 16,因为我认为运行时不允许 12)
您还需要一个小型结构化缓冲区(一个元素就足够了),它将用于存储最小距离
Use 复制结构计数 https://msdn.microsoft.com/en-us/library/windows/desktop/ff476393(v=vs.85).aspx将 UnorderedView 计数器传递到这些缓冲区中(请注意,参数缓冲区的第二个和第三个元素需要均设置为 1,因为它们将成为使用调度的参数)。
使用 UINT_MAXVALUE 清除小型 StructuredBuffer 缓冲区,并使用参数缓冲区间接调度 https://msdn.microsoft.com/en-us/library/windows/desktop/ff476406(v=vs.85).aspx
我假设您不会有很多点击,因此对于下一部分,numthreads 将设置为 1,1,1(如果您想使用更大的组,您将需要运行另一个计算着色器来构建参数缓冲区)。
然后求最小距离:
StructuredBuffer<rayHit> rayHitbuffer : register(t0);
ByteAddressBuffer rayHitCount : register(t1);
RWStructuredBuffer<uint> rwMinBuffer : register(u0);
[numthreads(1,1,1)]
void CS_CalcMin(uint3 tid : SV_DispatchThreadID)
{
uint count = rayHitCount.Load(0);
if (tid.x >= count)
return;
rayHit hit = rayHitbuffer[tid.x];
uint dummy;
InterlockedMin(rwMinBuffer[0],asuint(hit.distanceToTriangle), dummy);
}
由于我们预计命中距离将大于零,因此我们可以在这种情况下使用 asuint 和 InterlockedMin。另外,由于我们使用 DispatchIndirect,因此这部分现在仅应用于之前通过测试的元素。
现在,您的单个元素缓冲区包含最小距离,但不包含索引。
最后一部分,我们需要最终提取位于最小命中距离的三角形索引。
您再次需要一个带有 UnorderedView 的新 StructuredBuffer 来存储最小索引。
使用与之前相同的调度参数(间接),并执行以下操作:
ByteAddressBuffer rayHitCount : register(t1);
StructuredBuffer<uint> MinDistanceBuffer : register(t2);
AppendStructuredBuffer<uint> appendMinHitIndexBuffer : register(u0);
[numthreads(1,1,1)]
void CS_AppendMin(uint3 tid : SV_DispatchThreadID)
{
uint count = rayHitCount.Load(0);
if (tid.x >= count)
return;
rayHit hit = rayHitbuffer[tid.x];
uint minDist = MinDistanceBuffer[0];
uint d = asuint(hit.distanceToTriangle);
if (d == minDist)
{
appendMinHitIndexBuffer.Append(hit.triangleID);
}
}
现在,appendMinHitIndexBuffer 包含最接近的三角形索引(如果有这种情况,则包含多个),您可以使用暂存资源将其复制回来并映射资源以供读取。