2014-02-06 45 views
3

編寫計算着色器以用於Unity 4.我試圖獲得3d噪聲。計算着色器輸入浮點數3d數組

我們的目標是從我的C#代碼中獲取一個multidiminsional float3數組到我的計算着色器。這可能是直接的(使用某種聲明)還是隻能使用Texture3D對象來實現?

我目前有一個工作在單個float3點上的單工噪聲的實現,輸出一個float -1到1.我移植了計算着色器的代碼here

我想通過將噪聲操作應用於數組中的每個float3點來擴展它以工作在float3的3D數組上(我假設在C#中最接近的比較將是Vector3[,,])。

我已經嘗試了一些其他的東西,但他們感到奇怪,完全錯過了使用並行方法的觀點。以上是我想象的應該看起來像。

我也設法得到Scrawk's實現作爲頂點着色器工作。 Scrawk使用Texture3D將3D float4數組添加到着色器中。但我無法從紋理中提取浮線。計算着色器是如何工作的?依靠紋理?我可能忽略了有關從紋理中獲取值的問題。這似乎是這個用戶在this post中獲取數據的方式。類似的問題,我的,但不是我正在尋找。

新增了一般的着色器,我覺得我錯過了一些關於計算着色器以及它們如何工作的基礎知識。我們的目標是(使用計算着色器(或者任何着色器最適合這種工作),使用推進立方體到GPU上進行噪聲生成和網格計算(我相信你已經猜到了)。

約束是統一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) 

而這裏的計算着色器

#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體素)中獲取一塊,併爲所有點生成噪聲值。

鑑於我應該做的是讓這是一個二維問題。 一些seudo CPU代碼可能是這個樣子......

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

基於我的假設是正確的,我會說,你可能有你的shader錯例如...

這將嘗試運行16× 16 x 16線程遠遠超過1024組線程限制,您可以擁有無​​限制的組,但每個組可以不超過1024個線程。

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

什麼,我認爲你需要的是更多的東西一樣[numthreads(16,1,16)上運行點的16×16格噪音的功能,並提高每個點上漲噪聲x maxHeight量給你你想要的點。

您的調度呼叫會是這個樣子......

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

...這將導致一個線程組中的16×16點產生高度映射值。 一旦你到達那裏,你可以擴大規模。

所有這些與您提到的進行立方體相結合,表明您正在做同樣的事情,在GPU上構建一個體素引擎,其中原始體素數據在GPU ram中生成,然後由其生成網格。

我有這部分的過程破解,難的部分是下一個階段,從生成的體素數組生成一個網格/場景對象。 根據你的方法,你可能會想要真正舒適的射線遊行或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像素陣列在GPU上的一個緩衝區中,其中包含「terrain like」之類的東西。

我想你可以從那裏拿它來做你所需要的,如果你的噪聲函數傳遞了threadIndex(體素位置)的xyz值,你可能會在計算着色器中刪除「if」。

讓我知道如果你找到一個簡單的方法來解決這個問題,這是我仍然在自己的工作。

我的代碼工作(以及幾乎)這樣的事情...

組件開始...... 調用計算到根素緩衝。 調用compute來從voxelbuffer生成頂點緩衝區。

平局(每幀)... 渲染材料

+0

是的,我肯定會努力爭取數量線程的東西。是的,目標是體素地形:)似乎這是一個試火的最proc地形開發者的臉。我正在尋找3D陣列,因爲我想要一個點雲,而不是一個高度圖。這允許產生懸垂和洞穴等。我有一個在CPU上運行的實現,但生成塊時大約爲20 fps。那麼獲得多維浮點數據到計算着色器的唯一方法是使用Texture2D或Texture3D?還是有另一種類型的語法,我可以用於REStructuredBuffer ? – PandemoniumSyndicate

+0

這可能有點讓我失望,但我認爲你會以錯誤的方式去做......像這樣生成數組是不可行的,這是一個2遍過程,首先生成「堆棧高度」到地面級別並填充「污垢」或其他內容,然後在同一陣列上運行一個過程(計算內核)以「挖掘隧道」。 – War

+0

當然,總是GPU的寶石方法... http://http.developer.nvidia.com/GPUGems3/gpugems3_ch01.html ...如果你正在使用,然後我叮叮噹噹,你可能會混淆密度函數與噪聲函數它們是相似的,但仍然是不同的功能。 – War

1

使用1D緩衝器,索引它,就好像3D,通過特殊的索引頂點緩衝器,對CPU & GPU。

還有只有 HLSL中的一維緩衝區。 使用函數/公式將N維(如3D或2D)索引向量轉換爲您用來索引到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,將其添加到總數。你現在有一個一維索引。

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

腳註#1我不使用統一的,因爲它更容易通過交換y和z來解釋這裏的座標系。 在這種情況下,[z][y][x]索引可防止跳過所有內存;見this article。統一將換爲[z][y][x][y][z][x](在鋪設了同樣的方式切片主要工作。)

腳註#2這個原則與uint3 threadID : SV_GroupThreadIDuint3 groupID : SV_GroupID相比正是uint3 id : SV_DispatchThreadID一樣。請參閱文檔:

SV_DispatchThreadID是SV_GroupID * numthreads和GroupThreadID的總和。

...因此,請在可能的情況下使用此代替給定程序的結構。

腳註#3這與在C語言中實現N維索引的方式一樣。