2014-02-06 2 views
3

Unity 4에서 사용할 Compute Shader 작성하기 3d 노이즈를 얻으려고합니다.쉐이더 계산 3d float 배열

목표는 C# 코드에서 내 계산 쉐이더에 multidiminsional float3 배열을 가져 오는 것입니다. 이것은 가능한 일종의 선언을 사용하여 간단하게 가능합니까 아니면 Texture3D 객체를 사용해서 만 구현할 수 있습니까?

저는 현재 개별 float3 포인트에서 작동하는 단일 노이즈의 구현을 가지고 있습니다. 단일 부동 -1을 1로 출력합니다. 코드 here을 컴퓨팅 쉐이더로 이식했습니다.

배열의 각 float3 지점에 노이즈 연산을 적용하여 float3의 3D 배열 (C#에서 가장 가까운 비교는 Vector3[,,])로 작업을 확장하고 싶습니다.

나는 다른 몇 가지 시도를 해봤지만 기분이 좋지 않고 병렬 접근 방식을 사용하는 데 완전히 빠져 있습니다. 위는 내가 상상해야 할 모습입니다.

나는 또한 Scrawk's Implemenation을 버텍스 쉐이더로 작업 할 수있었습니다. Scrawk는 Texture3D을 사용하여 3D float4 배열을 쉐이더에 넣습니다. 그러나 저는 텍스처에서 수레를 추출 할 수 없었습니다. Compute Shaders가 어떻게 작동합니까? 텍스처에 의지? 아마도 텍스처에서 값을 가져 오는 것에 관한 것을 간과 한 것 같습니다. 이 사용자가 데이터를 가져 오는 방법이 this post 인 것으로 보입니다. 내 질문과 비슷한 질문이지만, 내가 찾던 답변이 아닙니다.

쉐이더에 익숙하지 않은 나는 Compute Shaders와 그 작동 원리에 대해 근본적으로 뭔가 빠져있는 것처럼 느껴집니다. 목표는 Compute Shaders (또는 이런 종류의 작업에 가장 적합한 쉐이더)를 사용하여 GPU에 행진 큐브를 사용하여 노이즈 생성 및 메쉬 계산을 얻는 것입니다 (짐작 했겠지만).

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

입니다 :

제약 여기에 내가 사용하고 C# 코드의 골격이다

유니티 4의 무료 평가판입니다 하이트 맵과 같은 것을 생성하기 위해 소음을 사용하면 ... 당신의 의도는 여기에 있습니까? 배열의 모든 점에 대해 값을 생성하는 것처럼 보입니다.

나는 복셀 엔진 (16 x 16 x 16 복셀)에서 청크를 가져 와서 모든 포인트에 대한 노이즈 값을 생성하는 내 머리 속에 이미지가 있습니다.

반면에 내가하는 일은 두 번째 문제입니다. 다음과 같이 보일 수 일부 seudo의 CPU 코드 ...

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

이 16 ×를 실행하려고합니다 ... 내 가정은 내가 당신이 예를 들어 셰이더가 잘못 할 수도 있습니다 말을 올바른 것을 바탕으로 그룹의 1024 스레드 제한을 훨씬 웃도는 16 x 16 스레드를 사용하면 무제한 그룹을 가질 수 있지만 각 그룹은 1024 스레드를 초과 할 수 없습니다.

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

은 무엇 난 당신이 필요하다고 생각하는 것은 더 [numthreads (16,1,16)]를 포인트의 16 × 16 그리드에서 노이즈 함수를 실행하고 각각에의 maxHeight 금액 x를 잡음에 의해 포인트 인상하는 같은 것입니다 원하는 지점을 알려주세요. 귀하의 파견 호출이 다음과 같이 보일 것이다

...

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

...이 16 × 16 점에 대해 높이 맵 값을 생성하는 단일 스레드 그룹을 초래할 것입니다. 일단 멀리까지 확장하면 확장 할 수 있습니다.

이 모든 것은 행진 큐브에 대한 언급과 함께 제가 여러분과 똑같은 일을하고 있다는 것을 의미합니다. 즉, 보셀 엔진을 GPU에 구축하고 원시 보셀 데이터를 GPU 램에서 생성 한 다음 메쉬에서 생성합니다.

나는이 과정의 일부가 깨졌고, 어려운 부분은 결과적인 보셀 배열로부터 메쉬/장면 객체를 생성하는 다음 단계이다. 당신의 접근 방식에 따라 아마도 ray marching이나 AppendBuffers를 사용하는 것이 좋습니다.

행운을 빈다.

플랫 버퍼 사용 :이해야

//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 복셀의 배열을 원하는 말할 수 있습니다 및 덩어리 내가이 작업을 수행 한 후 32 * 32 * 32 복셀입니다 (비록 내 머리 속의 램에서 나온 것이기는하지만이 모든 것들이 자리를 차지할 수는 없지만) "지형과 비슷한"것을 포함하고있는 GPU의 버퍼에 128 * 128 * 128 보셀 배열을 생성합니다.

당신이 필요로하는 것을 취할 수있을 것 같아요. 노이즈 함수가 threadIndex (보셀 위치)에서 xyz 값을 전달받은 경우 컴퓨팅 쉐이더에 "if"를 드롭 할 수 있습니다.

이 문제를 해결할 수있는 깔끔한 방법을 찾으면 나에게 직접 알려 주겠다.

내 코드 세대 복셀 버퍼에이 같은 (물론 거의) 뭔가 ...

구성 요소 시작 ... 전화 컴퓨팅을 사용할 수 있습니다. voxelbuffer에서 gen vertex 버퍼로 호출을 호출합니다.

연신 (각 프레임)는 CPU ... & GPU 특별 인덱싱하여 1 차원 재료 버퍼 사용

+0

그래, 확실히 num 스레드 물건을 grock하려고합니다. 그래, 목표는 voxel 지형입니다 :) 이것은 재판에 의한 것 같습니다 대부분 proc 지형 devs 얼굴. 높이 맵이 아닌 점군을 원하기 때문에 3D 배열을 사용할 것입니다. 이로 인해 오버행과 동굴 등이 생성됩니다. CPU에서 실행되는 구현이 있지만 청크를 생성하는 동안 약 20fps입니다. 다차원 플로트 데이터를 계산 쉐이더로 가져 오는 유일한 방법은 Texture2D 또는 Texture3D를 사용하는 것입니다. 또는 REStructuredBuffer 에 사용할 수있는 다른 형식 구문이 있습니까? – PandemoniumSyndicate

+0

이것은 아마도 나에게 약간의 presumptuos지만, 나는 당신이 그것에 대해 잘못된 길을 가고 있다고 생각한다. 그와 같은 배열을 생성하는 것은 실현 가능하지 않다. 그것은 2 패스 프로세스이고, 먼저 "스택 높이"를 생성한다. 레벨 "로 채우고"먼지 "로 채운 다음 동일한 배열에서"터널을 파다 "라는 절차 (커널 계산)를 실행하십시오. – War

+0

당연히 GPU 보석에 접근하는 것은 물론 ... http://http.developer.nvidia.com/GPUGems3/gpugems3_ch01.html ... 만약 당신이 잡음 기능을 가진 밀도 함수를 혼란스럽게 할 수도 있습니다. , 그들은 비슷하지만 여전히 다른 기능입니다. – War

1

차원 것처럼로 인덱스와 버텍스 버퍼를 렌더링.

HLSL에는 의 1 차원 버퍼 만 있습니다. 함수/수식을 사용하여 N 차원 (3D 또는 2D라고도 함) 색인 벡터를 1D 배열에 색인하는 데 사용하는 1D 벡터로 변환합니다. 우리가있는 경우

는 3D 배열은 우리가 선형 인덱스 [i][z][y][x]이를 설정할 수 있습니다 (이유를 각주 1 참조) [z][y][x] 색인 및 array[Z_MAX][Y_MAX][X_MAX]를 만들었습니다.여기

당신이 위에서 아래로 조각 실행 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 나는 y 및 z 교환하여 설명하기 쉽기 때문에 통일의 현재 좌표 시스템을 사용하지 마십시오. 이 경우[z][y][x] 인덱싱은 메모리 전체에서 점프를 방지합니다. this article을 참조하십시오. 유니티 [y][z][x]에 대한 [z][y][x]을 교환 할 것입니다 (이 같은 방식으로 배치 조각에 주로 운영 할 수 있습니다.)

각주 # 2을이 원칙은 uint3 threadID : SV_GroupThreadIDuint3 groupID : SV_GroupID에 비해 uint3 id : SV_DispatchThreadID이 바로 이러한 작업을 수행하는 것입니다. 해당 문서를 참조하십시오

SV_DispatchThreadID이 SV_GroupID * numthreads 및 GroupThreadID의 합계입니다.

... 가능하면 프로그램의 구조를 고려하여 가능하면이 방법을 사용하십시오.

각주 # 3 이것은 N 차원 인덱싱이 C에서 후드 아래에있는 것과 같은 방식입니다.