Это вполне выполнимо, вот некоторый код 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)
Также вам понадобится небольшой структурированный буфер (достаточно одного элемента), который будет использоваться для хранения минимального расстояния.
Используйте CopyStructureCount для передачи счетчик UnorderedView в эти буферы (обратите внимание, что второй и третий элементы буфера аргумента должны быть установлены в 1, поскольку они будут аргументами для использования диспетчеризации).
Очистите небольшой буфер StructuredBuffer с помощью UINT_MAXVALUE и используйте буфер аргументов с DispatchIndirect
Я предполагаю, что у вас не будет много совпадений, поэтому для следующей части 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, эта часть теперь применяется только к элементам, которые ранее прошли тест.
Теперь ваш одноэлементный буфер содержит минимальное расстояние, но не индекс (или индексы).
Последняя часть, нам нужно, наконец, извлечь индекс треугольника, который находится на минимальном расстоянии срабатывания.
Вам снова нужен новый StructuredBuffer с UnorderedView для хранения минимального индекса.
Используйте те же аргументы отправки, что и раньше (косвенные), и выполните следующие действия:
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 содержит ближайший индекс треугольника (или несколько, если у вас есть такой сценарий), вы можете скопировать его обратно, используя промежуточный ресурс и сопоставить свой ресурс для чтения.
person
mrvux
schedule
25.10.2016