2016-10-21 2 views
2

Недавно я преобразовал приложение DirectX 9, которое использовало D3DXIntersect для поиска пересечений лучей/мешей в DirectX 11. Поскольку D3DXIntersect недоступен в DX11, я написал свой собственный код, чтобы найти пересечение , который просто перебирает все треугольники в сетке и проверяет их, отслеживая ближайший удар по началу координат. Это делается на стороне процессора и отлично подходит для выбора через GUI, но у меня есть еще одна часть приложения, которая создает новую сетку из существующей на основе нескольких разных точек обзора, и мне нужно проверить линию визирования для каждого треугольника в сетке много раз. Это довольно медленно.Вычислительный шейдер DirectX 11 для пересечения лучей/сетки

Имеет ли смысл использовать вычислительный шейдер DX11 для этого (т. Е. Будет ли значительное ускорение от его выполнения на ЦП)? Я искал в Интернете, но не смог найти существующий пример.

Предполагая, что ответ да, вот это подход, который я имею в виду:

  • Запуск потока для каждого треугольника в моей сетке
  • Каждый поток вычисляет расстояние до попадания в этот треугольник, или возвращает max float на промах. Сохраните одно значение для потока в буфере.
  • Затем выполните сокращение и верните минимальное (неотрицательное) значение.

Желаю, чтобы у меня был доступ к чему-то вроде CUDA Thrust в DirectX, потому что я думаю, что кодирование этого сокращения будет больно. Вот почему я спрашиваю, поэтому я ничего не делаю!

+0

Как я помню, теоретически оптимальная реализация проверки пересечения лучей и треугольников (bool not location), которая может быть реализована в шейдерах. lemme посмотрим, смогу ли я снова его найти. – Aaron

+0

На самом деле все примеры, которые я нахожу, являются стороной процессора также ... алгоритм, о котором я думал, - это алгоритм пересечения [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

ответ

0

На самом деле это имеет большой смысл. Вот также технический документ, в котором есть несколько полезных фрагментов шейдера: http://www.graphicon.ru/html/2012/conference/EN2%20-%20Graphics/gc2012Shumskiy.pdf. Также вы можете использовать DirectCompute/CUDA/OpenCL в DirectX, но если бы я мог дать вам подсказку, сделайте это в DirectCompute, потому что я думаю, что это наименьшая проблема, чтобы настроить и запустить ее.

0

Это полностью выполнимо, вот некоторый код 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 содержит индекс треугольника, который является ближайшим (или несколько, если у вас есть этот сценарий), вы можете скопировать он возвращается с использованием промежуточного ресурса и отображает ваш ресурс для чтения.