DirectX 11计算着色器,用于光线/网格相交 [英] DirectX 11 compute shader for ray/mesh intersect

查看:158
本文介绍了DirectX 11计算着色器,用于光线/网格相交的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近将DirectX 9应用程序转换为DirectX 11,该应用程序使用D3DXIntersect查找射线/网格交点到DirectX11。由于D3DXIntersect在DX11中不可用,因此我编写了自己的代码来查找交点,该交点仅遍历了所有三角形。对网格进行测试,并跟踪最接近原点的点。这是在CPU端完成的,可以很好地通过GUI进行拾取,但是我有应用程序的另一部分,它基于几种不同的观点从现有的网格中创建一个新的网格,我需要检查每个三角形的视线在网格中多次。这会变得很慢。

I recently converted a DirectX 9 application that was using D3DXIntersect to find ray/mesh intersections to DirectX 11. Since D3DXIntersect is not available in DX11, I wrote my own code to find the intersection, which just loops over all the triangles in the mesh and tests them, keeping track of the closest hit to the origin. This is done on the CPU side and works fine for picking via the GUI, but I have another part of the application that creates a new mesh from an existing one based on several different viewpoints, and I need to check line of sight for every triangle in the mesh many times. This gets pretty slow.

使用DX11计算着色器执行此操作是否有意义(即在CPU上进行处理会大大提高速度)?我搜索了互联网,但找不到现有的示例。

Does it make sense to use a DX11 compute shader to do this (i.e. would there be a significant speedup from doing it on the CPU)? I searched the internet but could not find an existing example.

假设答案是肯定的,这是我在想的方法:

Assuming the answer is yes, here is the approach I am thinking of:


  • 为我的网格中的每个三角形启动一个线程

  • 每个线程都会计算到该三角形的命中距离,或返回最大浮动想念。每个线程在缓冲区中存储一个值。

  • 然后进行缩减并返回最小(非负)值。

我希望我可以使用DirectX中的CUDA Thrust之类的东西,因为我认为编写减少代码将是一件痛苦的事情。这就是为什么我要问的原因,所以我什么也不做!

I wish I had access to something like CUDA Thrust in DirectX, because I think coding up that reduction is going to be a pain. That's why I'm asking, so I don't do a bunch of work for nothing!

推荐答案

这完全可行,这是一些HLSL代码,可以执行该操作(并且还可以处理两个相同距离的三角形的情况)。

This is totally doable, here is some HLSL code that allows to perform that (and also handles the case where you hit 2 triangles with the same distance).

我假设您知道如何创建资源(结构化缓冲区)并将其绑定到计算管道。

I assume that you know how to create resources (Structured Buffer) and bind them to compute pipeline.

另外,我将认为您的几何体已索引。

Also I'll consider that your geometry is Indexed.

第一步是收集经过的三角形考试。

The first step is to collect triangles that pass the test. Instead of using a "Hit" flag, we will use an Append buffer to only push elements that pass the test.

首先创建2个结构化的缓冲区(位置和三角形索引),而不是使用 Hit标志,而是使用追加缓冲区仅推送通过测试的元素。 ,然后将模型数据复制到这些数据上。

First create 2 structured buffers (position and triangle indices), and copy your model data onto those.

然后创建带有可追加的无序视图的结构化缓冲区。

Then create a Structured Buffer with an Appendable Unordered view.

要执行命中检测,可以使用以下计算代码:

To perform Hit detection, you can use the following Compute code:

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(最坏的情况是Triangle Count,例如:每个三角形都被射线击中)。

Please note that you need to provide a sufficient size for appendRayHitBuffer (worst case scenario is Triangle Count, eg :every triangle is hit by the ray).

完成此操作后,缓冲区的开始部分将包含命中数据,

Once this is done, the beginning part of the buffer contains hit data, and the unordered view counter the number of triangles that passed the test.

然后,您需要创建一个参数缓冲区和一个小字节地址缓冲区(大小为16,因为我不要以为运行时允许12)

Then you need to create an argument buffer, and a small Byte Address Buffer (size 16, since I don't think runtime will allow 12)

您还需要一个小的结构化缓冲区(一个元素就足够了),该缓冲区将用于存储最小距离

You also need a small structured buffer (one element is enough), which will be used to store minimum distance

使用 CopyStructureCount 将UnorderedView计数器传递到这些缓冲区(请注意,Argument缓冲区的第二个和第三个元素必须都设置为1,因为它们将成为使用调度的参数)。

Use CopyStructureCount to pass the UnorderedView counter into those buffers (plase note that second and third element of the Argument buffer needs to be both set to 1, as they will be arguments for use dispatch).

使用UINT_MAXVALUE清除较小的StructuredBuffer缓冲区,并将Argument缓冲区与 DispatchIndirect

Clear the small StructuredBuffer Buffer using UINT_MAXVALUE, and use the Argument buffer with DispatchIndirect

我认为您不会有太多匹配,因此下一部分numthreads将是设置为1,1,1(如果要使用更大的组,则需要运行另一个计算着色器以构建参数缓冲区)。

I assume that you will not have many hits, so for next part numthreads will be set to 1,1,1 (if you want to use larger groups, you will need to run another compute shader to build the argument buffer).

然后查找最小值距离:

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,因此此部分现在仅适用于先前通过测试的元素。

Since we expect that hit distance will be greater than zero, we can use asuint and InterlockedMin in that scenario. Also since we use DispatchIndirect, this part is now only applied to the elements that previously passed the test.

现在,您的单个元素缓冲区包含最小距离,但不包含索引(或索引)。

Now your single element buffer contains the minimum distance, but not the index( or indices).

最后,我们需要最终提取处于最小命中距离的三角形索引。

Last part, we need to finally extract triangle index that is at the minimum hit distance.

您再次需要一个带有UnorderedView的新StructuredBuffer来存储最小索引。

You need again a new StructuredBuffer with an UnorderedView to store the minimum index.

使用与以前相同的调度参数(间接),并执行以下操作:

Use the same dispatch arguments as before (indirect), and perform the following:

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包含最接近的三角形索引(或几种(如果有这种情况),则可以使用暂存资源将其复制回并映射资源以供阅读。

Now appendMinHitIndexBuffer contains the triangle index that is the closest (or several if you have that scenario), you can copy it back using a Staging resource and Map your resource for reading.

这篇关于DirectX 11计算着色器,用于光线/网格相交的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆