DirectX 11 compute shader for ray/mesh intersect

风格不统一 提交于 2019-12-04 16:36:42

Actually it makes a lot of sense. Here is also a whitepaper which has some useful shader snippets: http://www.graphicon.ru/html/2012/conference/EN2%20-%20Graphics/gc2012Shumskiy.pdf . Also you can use DirectCompute/CUDA/OpenCL in DirectX, but if I might give you a hint, do it in DirectCompute, because I think it is the least hassle to set up and get it running

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.

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);
    }
}

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.

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

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).

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

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).

Then to find minimum distance:

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);   
} 

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.

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);
    }
}

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!