2014-02-06 5 views
3

Написание вычислительного шейдера для использования в Unity 4. Я пытаюсь получить 3D-шум.Compute Shaders Input 3d array of floats

Цель состоит в том, чтобы получить мультидиллионный массив float3 в моем вычислительном шейдере из моего кода на C#. Возможно ли это в простой форме (с использованием какой-либо декларации) или может быть достигнуто только с помощью объектов Texture3D?

В настоящее время у меня есть реализация симплекс-шума, работающего над отдельными точками float3, выводящего один плавающий -1 в 1. Я портировал найденный код here для вычислительного шейдера.

Я хотел бы расширить это, чтобы работать над трехмерным массивом float3 (я полагаю, что самое близкое сравнение в C# было бы Vector3[,,]), применяя операцию шума к каждой точке float3 в массиве.

Я пробовал несколько других вещей, но они чувствуют себя странно и полностью не понимают, что использовать параллельный подход. Выше было то, что я думаю, это должно выглядеть.

Мне также удалось получить Scrawk's Выполнение работы в виде вершинных шейдеров. Scrawk получил 3D-массив float4 в шейдер, используя Texture3D. Но я не смог извлечь поплавки из текстуры. Так работает Compute Shaders? Опираясь на текстуры? Вероятно, я упустил что-то, касающееся получения значений из Текстуры. Кажется, что этот пользователь получает данные в this post. Аналогичный вопрос мне, но не совсем то, что я ищу.

Новое в шейдерах в целом, и я чувствую, что мне не хватает чего-то довольно фундаментального в вычислительных шейдерах и как они работают. Цель состоит в том, чтобы (как я уверен, вы догадались) получить генерацию шума и вычисление сетки с помощью маршевых кубов на графическом процессоре с использованием Compute Shaders (или любой другой шейдер лучше всего подходит для такого рода работ).

Ограничения являются Free Trial издание единства 4.

Вот скелет C# код я использую:

int volumeSize = 16; 
    compute.SetInt ("simplexSeed", 10); 

    // This will be a float[,,] array with our density values. 
    ComputeBuffer output = new ComputeBuffer (/*s ize goes here, no idea */, 16); 
    compute.SetBuffer (compute.FindKernel ("CSMain"), "Output", output); 

    // Buffer filled with float3[,,] equivalent, what ever that is in C#. Also what is 'Stride'? 
    // Haven't found anything exactly clear. I think it's the size of basic datatype we're using in the buffer? 
    ComputeBuffer voxelPositions = new ComputeBuffer (/* size goes here, no idea */, 16); 
    compute.SetBuffer (compute.FindKernel ("CSMain"), "VoxelPos", voxelPositions);  


    compute.Dispatch(0,16,16,16); 
    float[,,] res = new float[volumeSize, volumeSize, volumeSize]; 

    output.GetData(res); // <=== populated with float density values 

    MarchingCubes.DoStuff(res); // <=== The goal (Obviously not implemented yet) 

И вот Compute Shader

#pragma kernel CSMain 

uniform int simplexSeed; 
RWStructuredBuffer<float3[,,]> VoxelPos; // I know these won't work, but it's what I'm trying 
RWStructuredBuffer<float[,,]> Output;  // to get in there. 

float simplexNoise(float3 input) 
{ 
    /* ... A bunch of awesome stuff the pastebin guy did ...*/ 

    return noise; 
} 

/** A bunch of other awesome stuff to support the simplexNoise function **/ 
/* .... */ 

/* Here's the entry point, with my (supposedly) supplied input kicking things off */ 
[numthreads(16,16,16)] // <== Not sure if this thread count is correct? 
void CSMain (uint3 id : SV_DispatchThreadID) 
{ 
    Output[id.xyz] = simplexNoise(VoxelPos.xyz); // Where the action starts.  
} 

ответ

0

Обычно вы бы использовали шум для создания чего-то вроде карты высот ... это ваше намерение здесь? Мне кажется, что вы генерируете значение для каждой точки массива.

У меня есть изображение в моей голове, где вы берете кусок от воксельного двигателя (16 x 16 x 16 вокселей) и генерируете значения шума для всех точек.

Принимая во внимание то, что я должен делать, это сделать эту проблему 2d. Некоторые seudo код процессора может выглядеть примерно так ...

for(x) 
    for(z) 
    fill all voxels below (GenerateY(x,z)) 

Исходя мои предположения быть правильно, я бы сказал, вы могли бы иметь свой шейдер неправильно, например ...

Это будет пытаться запустить 16 х 16 x 16 потоков, которые значительно превышают ограничение по потоку 1024 для группы, вы можете иметь неограниченные группы, но каждая группа может иметь не более 1024 потоков.

[numthreads(16,16,16)] // <== Not sure if this thread count is correct? 

Что я думаю, что вам нужно что-то больше похож на [numthreads (16,1,16)], чтобы запустить функцию шума на 16 х 16 точки сетки и поднять каждую точку до шумов х сумм MaxHeight в дайте вам то, что вы хотите.

Ваша диспетчерская вызов будет выглядеть примерно так ...

compute.Dispatch(0,1,0,0); 

... это привело бы к одной группе нитей получения значения высоты для 16 х 16 точек. Как только вы доберетесь до этого, вы можете увеличить масштаб.

Все это в сочетании с вашим упоминанием о маршевых кубах предполагает, что вы делаете то же самое, что и я, создавая движок воксела на графическом процессоре, где необработанные данные вокселя генерируются в графическом ядре, а затем генерируется из него сетка.

У меня есть эта часть процесса, трещина, сложная часть - следующий этап, генерирующий объект сетки/сцены из результирующего массива вокселей. В зависимости от вашего подхода вы, вероятно, захотите по-настоящему комфортно с помощью марширования лучей или AppendBuffers.

Удачи вам!

Плоский использование буфера:

Допустим, я хочу массив 128 * 128 * 128 вокселей и кусок 32 * 32 * 32 вокселей то я это сделать ...

//cpu code 
var size = 128*128*128; 
var stride = sizeof(float); 
ComputeBuffer output = new ComputeBuffer (size, stride); 
computeShader.SetBuffer (0, "voxels", output); 
computeshader.Dispatch(0, 4,4,4); 

//gpu code 
#pragma kernel compute 
RWStructuredBuffer<float> voxels; 

[numthreads(32,1,32)] // group is your chunk index, thread is you voxel within the chunk 
void compute (uint3 threadId : SV_GroupThreadID, uint3 groupId : SV_GroupID) 
{ 
    uint3 threadIndex = groupId * uint3(32, 1, 32) + threadId; 
    //TODO: implement any marching cubes/dual contouring functions in 
    //  here somewhere 
    uint3 endIndex = uint(32, 0, 32) + threadIndex; 

    float height = Noise(); 
    int voxelPos = voxPos.x+ voxPos.y*size+voxPos.z*size*size; 

    // chunks are 32 * 32 blocks of columns the whole height of the volume 
    for(int y = threadIndex.y; y < endIndex.y; y++) 
    { 
     if(y < height) 
     { 
     voxels[voxelPos] = 1; // fill this voxel 
     } 
      else 
      { 
       voxels[voxelPos] = 0; // dont fill this voxel 
      } 
    } 

Это должно (хотя это все от барана в моей голове, чтобы он не мог быть на месте) 128 * 128 * 128 voxel массив в буфере на графическом процессоре, который содержит что-то вроде «ландшафта».

Я думаю, вы можете взять его оттуда, чтобы сделать то, что вам нужно, вы могли бы опрокинуть «if» в вычислительном шейдере, если ваша шумовая функция была передана значения xyz из threadIndex (позиция воксела).

Дайте мне знать, если вы найдете аккуратный способ взломать это, это то, что я все еще работаю над собой.

Мой код работает (ну почти) что-то вроде этого ...

компонент начала ... вызов Подсчитать, ген вокселей буфера. вызов вычислить в буфере вершин гена из voxelbuffer.

дро (каждый кадр) ... рендеринга вершинный буфер с материалом

+0

Да, я определенно пытаюсь найти нужный материал. Да, цель - воксель :) Похоже, это лицо, которое проходит против всех участников проекта. Я собираюсь для 3D-массива, потому что я хочу облако точек, а не карту высот. Это позволяет создавать выступы и пещеры и т. Д. У меня есть реализация, выполняемая на процессоре, но она составляет около 20 кадров в секунду при генерации кусков. Таким образом, единственный способ получить многомерные данные с плавающей точкой в ​​вычислительном шейдере - использовать Texture2D или Texture3D? Или есть другой синтаксис типа, который я могу использовать для типа REStructuredBuffer ? – PandemoniumSyndicate

+0

Это, наверное, немного презумпция меня, но я думаю, что вы идете по этому пути не так. Генерация такого массива невозможна, это процесс с двумя проходами, сначала создайте «высоту стека» до земли уровень и заполнить «грязь» или что-то еще, затем запустите процедуру (вычислить ядро) в том же массиве, чтобы «выкопать туннели». – War

+0

Разумеется, theres всегда привлекают драгоценные камни GPU ... http://http.developer.nvidia.com/GPUGems3/gpugems3_ch01.html ... если вы используете это, тогда я хочу, чтобы вы могли ввести в заблуждение функцию плотности с функцией шума , они похожи, но все еще разные функции. – War

1

Использование буфера 1D, индекс в него, как если бы 3D, с помощью специальной индексации, на CPU & GPU.

Только 1-мерные буферы в HLSL. Используйте функцию/формулу для преобразования из векторного вектора N-мерного (скажем, 3D или 2D) в вектор 1D, который вы используете для индексации в ваш 1D-массив.

Если у нас есть 3D-массив индексируется [z][y][x] (см примечание # 1, почему), и создал array[Z_MAX][Y_MAX][X_MAX], мы можем превратить [z][y][x] это в линейный индекс [i].

Вот как его сделали ...

Представьте блок вы нарезать ломтиками сверху вниз (так как скапливается стопка монет), где xy является каждый слой/срез, бег вверх по z, который является вертикальной осью. Теперь для каждого приращения в z (вверх) мы знаем, что у нас есть x (width) * y (height) элементов, уже учтенных. Теперь к этой сумме нам нужно добавить, сколько мы ходили в текущем 2D-фрагменте: для каждого шага в y (который подсчитывает элементы в строках, идущих слева направо), мы знаем, что у нас уже есть элементы x (width), поэтому добавьте, что к итогу. Затем к этому мы имели количество шагов в текущей строке, которая равна x, добавьте это в общую сумму. Теперь у вас есть 1D-индекс.

i = z * (Y_MAX * X_MAX) + y * (X_MAX) + x; //you may need to use "*_MAX - 1" instead

Сноска # 1 я не использую единство системы координат здесь, потому что это легче объяснить с помощью замены у и г. В этом случае индексирование[z][y][x] предотвращает скачки по всей памяти; см. this article. Unity будет поменять [z][y][x] для [y][z][x] (в основном работают на срезах выложенных это точно так же.)

Сноска # 2 Этот принцип заключается в том, что именно uint3 id : SV_DispatchThreadID делает по сравнению с uint3 threadID : SV_GroupThreadID и uint3 groupID : SV_GroupID. См. Документы:

SV_DispatchThreadID - это сумма SV_GroupID * numthreads и GroupThreadID.

... Поэтому используйте это вместо этого, если это возможно, с учетом структуры вашей программы.

Сноска # 3 Это то же самое, что N-мерное индексирование достигается на C под капотом.