计算着色器输入3d数组的浮点数

时间:2014-02-06 07:14:28

标签: c# unity3d shader hlsl compute-shader

编写要在Unity中使用的计算着色器4.我试图获得3d噪音。

目标是从我的C#代码中将multidiminsional float3数组放入我的计算着色器中。这是否可以直接使用(使用某种声明)或只能使用Texture3D对象实现?

我目前在单个float3点上有单工噪声的实现,输出单个float -1到1.我移植了找到here的代码用于计算着色器。

我想通过将噪声操作应用于数组中的每个float3点来扩展它以处理float3的3D数组(我认为C#中最接近的比较将是Vector3[,,])。

我尝试过其他一些东西,但是他们觉得很奇怪,完全忽略了使用并行方法的意义。以上是我想象的应该是什么样子。

我还设法让Scrawk's实现作为顶点着色器工作。 Scrawk使用Texture3D 将3D float4数组添加到着色器中。但是我无法从纹理中提取浮子。计算着色器的工作原理是什么?依靠纹理?我可能忽略了从纹理中获取值的一些事情。这似乎是该用户在this post中获取数据的方式。我的类似问题,但不是我正在寻找的。

一般来说是着色器的新手,我觉得我错过了一些关于Compute Shaders以及它们如何工作的基本信息。目标是(我确信你已经猜到了)使用计算着色器(或任何最适合这种工作的着色器)使用行进立方体进行噪声生成和网格计算。

约束是Unity 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.     
}

2 个答案:

答案 0 :(得分:1)

使用1D缓冲区,通过特殊索引在CPU&amp;上对其进行索引,就像3D一样。 GPU。

HLSL中只有 1维缓冲区。 使用函数/公式将N维(比方说3D或2D)索引向量转换为用于索引到1D数组的1D向量。

如果我们将3D数组编入索引[z][y][x](请参阅脚注#1了解原因)并创建了array[Z_MAX][Y_MAX][X_MAX],我们可以将[z][y][x]转换为线性索引{{1} }。

以下是它的完成方式......

想象一下你从上到下切成片的块(所以它像一堆硬币一样堆积起来),其中[i]是每一层/切片,沿着xy向上跑,这是垂直轴。现在,对于z(向上)中的每个增量,我们知道已经考虑了z个元素。现在,我们需要添加我们在当前2D切片中走了多少:对于x (width) * y (height)中的每一步(计算从左到右的行中的元素),我们知道我们有y元素已经占了,所以将它加到总数中。然后我们得到当前行中的步数,即x (width),将其添加到total。你现在有一个指数。

x

脚注#1 我在这里不使用统一的坐标系,因为通过交换y和z更容易解释。 在这种情况下, i = z * (Y_MAX * X_MAX) + y * (X_MAX) + x; //you may need to use "*_MAX - 1" instead索引可以防止跳过内存;见this article。 Unity会将[z][y][x]交换为[z][y][x](主要用于以相同方式布局的切片。)

脚注#2 [y][z][x]uint3 id : SV_DispatchThreadID相比,此原则正是uint3 threadID : SV_GroupThreadID所做的。请参阅文档:

  

SV_DispatchThreadID是SV_GroupID * numthreads和GroupThreadID的总和。

...考虑到你的程序结构,请尽可能使用它。

脚注#3 这与在C中实现N维索引的方式相同。

答案 1 :(得分:0)

通常你会使用噪音来生成类似高度图的东西...这是你的意图吗? 在我看来,你正在为数组中的每个点生成一个值。

我脑子里有一个图像,你从体素引擎(16 x 16 x 16体素)中取出一块并为所有点生成噪声值。

而我应该做的事情就是把它变成一个二维问题。 一些seudo CPU代码可能看起来像这样......

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

基于我的假设是正确的,我会说你可能有你的着色器错误例如......

这将尝试运行16 x 16 x 16个线程,远远高于组的1024个线程限制,您可以拥有无​​限组,但每个组的线程不能超过1024个。

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

我认为你需要的更像是[numthreads(16,1,16)]来在16 x 16网格点上运行噪点函数并将每个点提高噪点x maxHeight amount给你的点数你想要的。

您的调度电话看起来像这样......

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

...这将导致单个线程组产生16 x 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
          }
   }

这应该产生(尽管这一切都来自我头脑中的撞击因此它可能不是斑点)GPU上缓冲区中的128 * 128 * 128体素阵列包含“类似地形”的东西。

我猜你可以从那里拿它来做你需要的东西,如果你的噪音函数从threadIndex(体素位置)传递了xyz值,你可以在计算着色器中删除“if”。

如果你找到了解决这个问题的简洁方法,请告诉我,这是我仍在努力的事情。

我的代码(几乎可以)像这样......

组件开始......   将计算调用到gen voxel缓冲区。   从voxelbuffer调用计算到gen顶点缓冲区。

画(每一帧)......   使用材质渲染顶点缓冲区