C# - 使用计算着色器

时间:2018-04-24 08:30:41

标签: c# directx-11 compute-shader

我尝试使用SharpDX11实现使用GPU的光线/网格交叉方法。我从较旧的帖子(Older post)中看到,可以使用Compute Shader完成此操作;但我需要帮助才能在.hlsl代码之外创建和定义缓冲区。

我的HLSL代码如下:

struct rayHit
{
     float3 intersection;
};

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 float3 intersection)
{
//Perform ray/triangle intersection
//Compute vectors along two edges of the triangle.
float3 edge1, edge2;
float distance;

//Edge 1
edge1.x = p2.x - p1.x;
edge1.y = p2.y - p1.y;
edge1.z = p2.z - p1.z;

//Edge2
edge2.x = p3.x - p1.x;
edge2.y = p3.y - p1.y;
edge2.z = p3.z - p1.z;

//Cross product of ray direction and edge2 - first part of determinant.
float3 directioncrossedge2;
directioncrossedge2.x = (rayDir.y * edge2.z) - (rayDir.z * edge2.y);
directioncrossedge2.y = (rayDir.z * edge2.x) - (rayDir.x * edge2.z);
directioncrossedge2.z = (rayDir.x * edge2.y) - (rayDir.y * edge2.x);

//Compute the determinant.
float determinant;
//Dot product of edge1 and the first part of determinant.
determinant = (edge1.x * directioncrossedge2.x) + (edge1.y * directioncrossedge2.y) + (edge1.z * directioncrossedge2.z);

//If the ray is parallel to the triangle plane, there is no collision.
//This also means that we are not culling, the ray may hit both the
//back and the front of the triangle.
if (determinant == 0)
{
    distance = 0.0f;
    intersection = float3(0, 0, 0);
    hit = false;
}

float inversedeterminant = 1.0f / determinant;

//Calculate the U parameter of the intersection point.
float3 distanceVector;
distanceVector.x = rayFrom.x - p1.x;
distanceVector.y = rayFrom.y - p1.y;
distanceVector.z = rayFrom.z - p1.z;

float triangleU;
triangleU = (distanceVector.x * directioncrossedge2.x) + (distanceVector.y * directioncrossedge2.y) + (distanceVector.z * directioncrossedge2.z);
triangleU = triangleU * inversedeterminant;

//Make sure it is inside the triangle.
if (triangleU < 0.0f || triangleU > 1.0f)
{
    distance = 0.0f;
    intersection = float3(0, 0, 0);
    hit = false;
}

//Calculate the V parameter of the intersection point.
float3 distancecrossedge1;
distancecrossedge1.x = (distanceVector.y * edge1.z) - (distanceVector.z * edge1.y);
distancecrossedge1.y = (distanceVector.z * edge1.x) - (distanceVector.x * edge1.z);
distancecrossedge1.z = (distanceVector.x * edge1.y) - (distanceVector.y * edge1.x);

float triangleV;
triangleV = ((rayDir.x * distancecrossedge1.x) + (rayDir.y * distancecrossedge1.y)) + (rayDir.z * distancecrossedge1.z);
triangleV = triangleV * inversedeterminant;

//Make sure it is inside the triangle.
if (triangleV < 0.0f || triangleU + triangleV > 1.0f)
{
    distance = 0.0f;
    intersection = float3(0, 0, 0);
    hit = false;
}

//Compute the distance along the ray to the triangle.
float raydistance;
raydistance = (edge2.x * distancecrossedge1.x) + (edge2.y * distancecrossedge1.y) + (edge2.z * distancecrossedge1.z);
raydistance = raydistance * inversedeterminant;

//Is the triangle behind the ray origin?
if (raydistance < 0.0f)
{
    distance = 0.0f;
    intersection = float3(0, 0, 0);
    hit = false;
}

intersection = rayFrom + (rayDir * distance);
hit = true;
}

[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;
float3 p;
TestTriangle(p1, p2, p3, hit, p);

if (hit)
{
    rayHit hitData;
    hitData.intersection = p;
    appendRayHitBuffer.Append(hitData);
}
}

虽然以下是我的c#实现的一部分,但我无法理解如何为计算着色器加载缓冲区。

int count = obj.Mesh.Triangles.Count;
        int size = 8; //int+float for every hit
        BufferDescription bufferDesc = new BufferDescription() {
            BindFlags = BindFlags.UnorderedAccess | BindFlags.ShaderResource,
            Usage = ResourceUsage.Default,
            CpuAccessFlags = CpuAccessFlags.None,
            OptionFlags = ResourceOptionFlags.BufferStructured,
            StructureByteStride = size,
            SizeInBytes = size * count
        };
        Buffer buffer = new Buffer(device, bufferDesc);
        UnorderedAccessViewDescription uavDescription = new UnorderedAccessViewDescription() {
            Buffer = new UnorderedAccessViewDescription.BufferResource() { FirstElement = 0, Flags = UnorderedAccessViewBufferFlags.None, ElementCount = count },
            Format = SharpDX.DXGI.Format.Unknown,
            Dimension = UnorderedAccessViewDimension.Buffer
        };
        UnorderedAccessView uav = new UnorderedAccessView(device, buffer, uavDescription);
        context.ComputeShader.SetUnorderedAccessView(0, uav);

        var code = HLSLCompiler.CompileFromFile(@"Shaders\TestTriangle.hlsl", "CS_RayAppend", "cs_5_0");
        ComputeShader _shader = new ComputeShader(device, code);
        Buffer positionsBuffer = new Buffer(device, Utilities.SizeOf<Vector3>(), ResourceUsage.Default, BindFlags.None, CpuAccessFlags.None, ResourceOptionFlags.None, 0);
        context.UpdateSubresource(ref data, positionsBuffer);
        context.ComputeShader.Set(_shader);

在我的c#实现中,我只考虑一条光线(其原点和方向),我想使用着色器检查网格的所有三角形的交点。我已经能够使用CPU做到这一点,但对于20k +三角形,即使我已经使用并行编码,计算也花费的时间太长了。

0 个答案:

没有答案