Это полностью выполнимо, вот некоторый код HLSL, который позволяет выполнить это (а также обрабатывает случай, когда вы нажимаете 2 треугольника с одинаковым расстоянием).
Я предполагаю, что вы знаете, как создавать ресурсы (структурированный буфер) и связывать их для вычисления конвейера.
Также я буду считать, что ваша геометрия индексирована.
Первый шаг - собрать треугольники, которые проходят тест. Вместо того, чтобы использовать флаг «Хит», мы будем использовать буфер Append, чтобы только те элементы, которые проходят тест.
Сначала создайте 2 структурированных буфера (индексы позиции и треугольника) и скопируйте данные модели на них.
Затем создайте структурированный буфер с видимым неупорядоченным представлением.
Для выполнения Hit обнаружения, вы можете использовать следующий Compute код:
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, поскольку они будут аргументами для использование отправка).
Clear небольшой 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 содержит индекс треугольника, который является ближайшим (или несколько, если у вас есть этот сценарий), вы можете скопировать он возвращается с использованием промежуточного ресурса и отображает ваш ресурс для чтения.
Как я помню, теоретически оптимальная реализация проверки пересечения лучей и треугольников (bool not location), которая может быть реализована в шейдерах. lemme посмотрим, смогу ли я снова его найти. – Aaron
На самом деле все примеры, которые я нахожу, являются стороной процессора также ... алгоритм, о котором я думал, - это алгоритм пересечения [Möller-Trumbore] (https://en.wikipedia.org/ вики/M% C3% B6ller% E2% 80% 93Trumbore_intersection_algorithm). Я также нашел хорошую ссылку для учебников [здесь] (http://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/moller-trumbore-ray-triangle-intersection) – Aaron