GPU パイプライン I/O がどのように機能するかを説明してみましょう。
各頂点には、関連付けられた一連の属性があります。サンプルコードを考えると:
属性 vec2 aVertexPosition; 属性 vec2 aPlotPosition;
各頂点には2D頂点位置とプロット位置があると言っています。追加した場合:
属性 vec3 vNormal;
次に、すべての頂点にも法線があります。これらは頂点の「プロパティ」と考えることができます。
これらの各属性の値をフェッチする場所を GPU に指示する必要があります。
各頂点属性には、シェーダーがコンパイルされるときに属性配列インデックスが割り当てられます。シェーダーが必要とする各属性配列インデックスを有効にする必要があります
enableVertexAttribArray(int attributeIndex);
有効にしたら、属性配列を頂点バッファーにバインドします。
bindBuffer(ARRAY_BUFFER, buffer);
この呼び出しで属性を取得する方法を説明します。
vertexAttribPointer(int attributeIndex, int count, int type, bool normalized, int stride, int offset);
サンプルコードを考えると:
vertexAttribPointer(0, 2, FLOAT, false, 16, 0); // vertex position
vertexAttribPointer(1, 2, FLOAT, false, 16, 8); // plot position
16 またはストライドは、各頂点間のバイト数です。各頂点は 4 つの float で構成され、各 float は 4 バイト幅です。オフセットは、アトリビュートが頂点内で開始する場所です。頂点位置は頂点の 0 バイト目、プロット位置は 8 バイト目です。
これらは、配列にインデックスを付ける方法を説明していると考えることができます。N 番目の頂点:
aVertexPosition.x = BUFFER[offset + N * stride + sizeof(FLOAT) * 0];
aVertexPosition.y = BUFFER[offset + N * stride + sizeof(FLOAT) * 1];
頂点属性は GPU によって自動的にフェッチされ、頂点シェーダー関数が実行される前に入力されます。はい、頂点シェーダーのメインは、描画する頂点ごとに1回呼び出されます。
頂点シェーダー ステージの出力は、「可変」変数です。それらは、頂点間のプリミティブ (三角形) の表面を横切って補間されるため、「可変」です。各頂点の値を書き出しますが、三角形がフラグメントにラスタライズされると、各フラグメントは各可変変数の補間値を取得します。フラグメント シェーダーは、描画呼び出しによって「カバー」されるすべてのフラグメント (ピクセル) に対して実行されます。ピクセルの 4x4 パッチをカバーする小さな三角形を描画すると、フラグメント シェーダーが 16 回実行されます。
簡潔に:
Vertex Shader Inputs: Vertex Attributes & Uniform values (not covered)
Vertex Shader Outputs: Varying Values at each vertex
Fragment Shader Inputs: Varying Values for a given fragment (pixel)
Fragment Shader Outputs: Color & Depth values which are stored in the color and depth buffer.
Vertex Shader is run for every vertex in the draw call.
Fragment shader is run for every "covered" or "lit" fragment (pixel) in the draw call.