4

私は計算シェーダーと、それを使用して y 軸上の頂点の配列を変更するために使用される C# スクリプトを持っています。

しかし、正常に動作するという事実にもかかわらず、シェーダーは私の形状の最初の頂点を忘れているようです (その形状が閉じたボリュームの場合を除く?)

C# クラスは次のとおりです。

Mesh m;
//public bool stopProcess = false; //Useless in this version of exemple
MeshCollider coll;
public ComputeShader csFile; //the compute shader file added the Unity way
Vector3[] arrayToProcess; //An array of vectors i'll use to store data
ComputeBuffer cbf; //the buffer CPU->GPU (An early version with exactly 
                   //the same result had only this one)
ComputeBuffer cbfOut; //the Buffer GPU->CPU
int vertexLength;

void Awake() { //Assigning my stuff
  coll = gameObject.GetComponent<MeshCollider>();
  m = GetComponent<MeshFilter>().sharedMesh;
  vertexLength = m.vertices.Length;
  arrayToProcess = m.vertices; //setting the first version of the vertex array (copy of mesh)
}

void Start () {

   cbf = new ComputeBuffer(vertexLength,32); //Buffer in
   cbfOut = new ComputeBuffer(vertexLength,32); //Buffer out
   csFile.SetBuffer(0,"Board",cbf); 
   csFile.SetBuffer(0,"BoardOut",cbfOut);

}

void Update () {
   csFile.SetFloat("time",Time.time);
   cbf.SetData(m.vertices);
   csFile.Dispatch(0,vertexLength,vertexLength,1); //Dispatching (i think there is my mistake)
   cbfOut.GetData(arrayToProcess); //getting back my processed vertices
   m.vertices = arrayToProcess; //assigning them to the mesh
   //coll.sharedMesh = m; //collider stuff useless in this demo
}

そして私の計算シェーダースクリプト:

#pragma kernel CSMain

RWStructuredBuffer<float3> Board : register(s[0]);
RWStructuredBuffer<float3> BoardOut : register(s[1]);

float time;

[numthreads(1,1,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    float valx = (sin((time*4)+Board[id.x].x));
    float valz = (cos((time*2)+Board[id.x].z));
    Board[id.x].y = (valx + valz)/5;
    BoardOut[id.x] = Board[id.x];
}

最初は同じバッファから読み書きしていましたが、問題が発生したため、別のバッファを使用しようとしましたが、成功しませんでした。私はまだ同じ問題を抱えています。

計算シェーダーの使用方法を誤解している可能性があります (頂点シェーダーを使用できることはわかっていますが、計算シェーダーをさらに改善するために試してみたいだけです)。

私が言ったことを完了するために、Mesh.vertices Array で頂点にインデックスを付ける方法に関連していると思います。

さまざまなブロック/スレッド構成をたくさん試しましたが、試した問題の組み合わせを解決するものは何もないようです:

Block            Thread   
60,60,1         1,1,1
1,1,1           60,60,3
10,10,3         3,1,1

私が覚えていない他のいくつか。以下のようなバランスの良い構成がベストだと思います。

Block : VertexCount,1,1 Thread : 3,1,1

閉じたボリュームについて: Cube {8 Vertices} ではすべてがそれに応じて移動するように見えるため、それについてはわかりませんが、頂点の数が奇数の形状では、最初の (または最後はまだチェックしていません) ようです処理されません

さまざまな形状で試してみましたが、分割された平面が最も明白で、1 つの角が常に動かないことがわかりました。

編集 :

さらに調査した結果、メッシュの最後の (最初にチェックした頂点ではなく) 頂点を計算しないのは単に計算シェーダーであることがわかりました。これはバッファーの種類に関連しているようです。なぜ RWStructuredBuffer が問題になるのか、まだわかりません。ストリーム用に予約されていますか? これに関するMSDNドキュメントを理解できません。

編集:解決後

C# スクリプト:

using UnityEngine;
using System.Collections;


public class TreeObject : MonoBehaviour {
    Mesh m;
    public bool stopProcess = false;
    MeshCollider coll;
    public ComputeShader csFile;
    Vector3[] arrayToProcess;
    ComputeBuffer cbf;
    ComputeBuffer cbfOut;
    int vertexLength;
    // Use this for initialization
    void Awake() {
        coll = gameObject.GetComponent<MeshCollider>();
        m = GetComponent<MeshFilter>().mesh;
        vertexLength = m.vertices.Length+3; //I add 3 because apparently  
                                                        //vertexnumber is odd
        //arrayToProcess = new Vector3[vertexLength];
        arrayToProcess = m.vertices;
    }

    void Start () {

        cbf = new ComputeBuffer(vertexLength,12);
        cbfOut = new ComputeBuffer(vertexLength,12);
        csFile.SetBuffer(0,"Board",cbf);
        csFile.SetBuffer(0,"BoardOut",cbfOut);

    }

    // Update is called once per frame
    void Update () {
        csFile.SetFloat("time",Time.time);
        cbf.SetData(m.vertices);
        csFile.Dispatch(0,vertexLength,1,1);
        cbfOut.GetData(arrayToProcess);
        m.vertices = arrayToProcess;
        coll.sharedMesh = m;
    }

}

VCount*VCount を使用していたので、頂点を必要以上に "2 乗" 回処理していたのはロジックだったので、回答の前に既に Blocks VCount,1,1 にロールバックしていました。

完了するには、ストライドが明らかに問題を引き起こしていることは間違いありませんでした。ストライド パラメータに関するドキュメントへのリンクを使用して回答を完成させていただけますか? (Unity ドキュメントは VOID であり、MSDN は 32 ではなく 12 であるべき理由を理解するのに役立たなかったため、どこからでも (32 は float3 のサイズだと思っていたので)

Docが必要です

それまでの間、これをより強力にするために十分に柔軟な (ジェネリック?) バージョンを提供し、シェーダーにいくつかの優れた配列処理関数を追加することを試みます...

4

1 に答える 1

2

Compute Shaders には精通していますが、Unity には触れたことがありません。Unity の Compute Shaders に関するドキュメントを調べたところ、いくつかの点が際立っていました。

cbf および cbfOut ComputeBuffers は、32 (バイト?) のストライドで作成されます。両方の StructuredBuffers には、ストライドが 32 ではなく 12 バイトの float3 が含まれています。32 はどこから来たのでしょうか?

計算シェーダーをディスパッチするとき、2 次元のディスパッチ (vertexLength,vertexLength, 1) を要求していますが、float3 の 1D 配列を操作しています。多くの異なるスレッドが、配列の各要素を更新する責任があると考える競合状態になります。パフォーマンスは悪くなりますが、[numthreads(1,1,1)] のスレッド グループ サイズが必要な場合は、Dispatch を呼び出すときに (vertexLength, 1, 1) 個のウェーブ/ウェーブフロントをディスパッチする必要があります (つまり、Dispatch (60,1) ,1) と numThreads(1,1,1))。

AMD ハードウェアで最適なパフォーマンスを得るには、スレッド グループ / ウェーブ内のスレッド数を少なくとも 64 の倍数にする必要があります。その後、ceil(numVertices/64) ウェーブフロントをディスパッチするだけで済み、シェーダーにロジックを挿入して、特定のスレッドの id.x が範囲外にならないようにします。

編集:

ComputeBuffer コンストラクターのドキュメントは次のとおりです: Unity ComputeBuffer Documentation 「ストライド」がバイト単位であると明示的には述べていませんが、それが唯一の合理的な仮定です。

于 2013-12-19T19:39:40.790 に答える