6

私はOpenTKを使用して独自のエンジンを作成しています(基本的にはC#のOpenGLバインディングのみで、gl *はGL。*になります)。それぞれに数千の頂点を持つ多数の頂点バッファーを格納します。したがって、floatを使用したVec3は単純にスペースを取りすぎるため、独自のカスタム頂点形式が必要です。(ここでは何百万もの頂点について話しています)

私がやりたいのは、このレイアウトで独自の頂点フォーマットを作成することです。

Byte 0: Position X
Byte 1: Position Y
Byte 2: Position Z
Byte 3: Texture Coordinate X

Byte 4: Color R
Byte 5: Color G 
Byte 6: Color B
Byte 7: Texture Coordinate Y

頂点のC#のコードは次のとおりです。

public struct SmallBlockVertex
{
    public byte PositionX;
    public byte PositionY;
    public byte PositionZ;
    public byte TextureX;

    public byte ColorR;
    public byte ColorG;
    public byte ColorB;
    public byte TextureY;
}

32 ^ 3の一意の位置しか必要ないため、各軸の位置としてのバイトは十分です。

バイトのセットごとに、入力として2つのvec4を受け取る独自の頂点シェーダーを作成しました。私の頂点シェーダーはこれです:

attribute vec4 pos_data;
attribute vec4 col_data;

uniform mat4 projection_mat;
uniform mat4 view_mat;
uniform mat4 world_mat;

void main() 
{
    vec4 position = pos_data * vec4(1.0, 1.0, 1.0, 0.0);

    gl_Position = projection_mat * view_mat * world_mat * position;
}

問題を切り分けるために、頂点シェーダーをできるだけシンプルにしました。シェーダーをコンパイルするためのコードは、即時モード描画でテストされており、機能するため、それは不可能です。

これが、頂点バッファを生成、設定、データで埋め、属性へのポインタを確立する私の関数です。

    public void SetData<VertexType>(VertexType[] vertices, int vertexSize) where VertexType : struct
    {
        GL.GenVertexArrays(1, out ArrayID);
        GL.BindVertexArray(ArrayID);
        GL.GenBuffers(1, out ID);
        GL.BindBuffer(BufferTarget.ArrayBuffer, ID);

        GL.BufferData<VertexType>(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * vertexSize), vertices, BufferUsageHint.StaticDraw);

        GL.VertexAttribPointer(Shaders.PositionDataID, 4, VertexAttribPointerType.UnsignedByte, false, 4, 0);
        GL.VertexAttribPointer(Shaders.ColorDataID, 4, VertexAttribPointerType.UnsignedByte, false, 4, 4);
    }

私が理解していることから、これは次の正しい手順です。頂点配列オブジェクトを生成してバインドする頂点バッファを生成してバインドする頂点バッファにデータを入力する属性ポインタを設定する

Shaders。*DataIDは、シェーダーをコンパイルして使用した後、このコードで設定されます。

PositionDataID = GL.GetAttribLocation(shaderProgram, "pos_data");
ColorDataID = GL.GetAttribLocation(shaderProgram, "col_data");

そしてこれは私のレンダリング関数です:

void Render()
{
    GL.UseProgram(Shaders.ChunkShaderProgram);

    Matrix4 view = Constants.Engine_Physics.Player.ViewMatrix;
    GL.UniformMatrix4(Shaders.ViewMatrixID, false, ref view);

    //GL.Enable(EnableCap.DepthTest);
    //GL.Enable(EnableCap.CullFace);
    GL.EnableClientState(ArrayCap.VertexArray);
    {
            Matrix4 world = Matrix4.CreateTranslation(offset.Position);
            GL.UniformMatrix4(Shaders.WorldMatrixID, false, ref world);

            GL.BindVertexArray(ArrayID);
            GL.BindBuffer(OpenTK.Graphics.OpenGL.BufferTarget.ArrayBuffer, ID);

            GL.DrawArrays(OpenTK.Graphics.OpenGL.BeginMode.Quads, 0, Count / 4);
    }
    //GL.Disable(EnableCap.DepthTest);
    //GL.Disable(EnableCap.CullFace);
    GL.DisableClientState(ArrayCap.VertexArray);
    GL.Flush();
}

誰かが私にいくつかのポインタを与えるほど親切にすることができますか(しゃれは意図されていません)?これを間違った順序で実行していますか、それとも呼び出す必要のある関数がいくつかありますか?

Web全体を検索しましたが、カスタム頂点の実装方法を説明する優れたチュートリアルやガイドが1つ見つかりません。さらに情報が必要な場合は、そのように言ってください。

4

1 に答える 1

11

独自の頂点フォーマットを作成することはあまりありません。それはすべてglVertexAttribPointer呼び出しで行われます。まず、ストライドパラメータとして4を使用していますが、頂点構造は8バイト幅であるため、ある頂点の開始から次の頂点まで8バイトあるため、ストライドは8である必要があります(もちろん、両方の呼び出しで) )。オフセットは正しいですが、色の正規化フラグをtrueに設定する必要があります。これは、色を[0,1]の範囲に確実に配置するためです(これが頂点位置にも当てはまるかどうかはわかりません)。 )。

gl...ClienState次に、シェーダーでカスタム頂点属性を使用する場合、非推奨の固定関数配列(モノ)を有効にしません。代わりに使用する必要があります

GL.EnableVertexAttribArray(Shaders.PositionDataID);
GL.EnableVertexAttribArray(Shaders.ColorDataID);

および対応するglDisableVertexAttribArray呼び出し。

そして、呼び出しのcount/4意味は何ですか。glDrawArrays最後のパラメータは、プリミティブ(この場合はクワッド)ではなく、頂点の数を指定することに注意してください。しかし、多分それはこのように意図されています。

これらの実際のエラーに加えて、シェーダーで自分でデコードする必要があるような、複雑な頂点形式を使用しないでください。これが、のストライドパラメータとオフセットパラメータのglVertexAttribPointer目的です。たとえば、頂点データを少し再定義します。

public struct SmallBlockVertex
{
    public byte PositionX;
    public byte PositionY;
    public byte PositionZ;
    public byte ColorR;
    public byte ColorG;
    public byte ColorB;
    public byte TextureX;
    public byte TextureY;
}

そして、あなたはただ使うことができます

GL.VertexAttribPointer(Shaders.PositionDataID, 3, VertexAttribPointerType.UnsignedByte, false, 8, 0);
GL.VertexAttribPointer(Shaders.ColorDataID, 3, VertexAttribPointerType.UnsignedByte, true, 8, 3);
GL.VertexAttribPointer(Shaders.TexCoordDataID, 2, VertexAttribPointerType.UnsignedByte, true, 8, 6);

そして、シェーダーには

attribute vec3 pos_data;
attribute vec3 col_data;
attribute vec2 tex_data;

位置からテクスチャ座標を抽出して自分で色を付ける必要はありません。

また、位置データの精度が極端に制限されるため、スペース要件で頂点位置にバイトを使用する必要があるかどうかを実際に検討する必要があります。たぶん、ショートパンツまたは半精度浮動小数点数は良い妥協案でしょう。

またglBindBuffer、renderメソッドを呼び出す必要はありません。これは、glVertexAttribPointerによってアクティブ化されるVAOにのみ必要であり、保存されるためglBindVertexArrayです。また、通常は呼び出しないでくださいglFlush。これは、バッファが交換されたときにOSによって行われるためです(ダブルバッファリングを使用している場合)。

最後になりましたが、ハードウェアが使用しているすべての機能(VBOやVAOなど)もサポートしていることを確認してください。

編集:実際には、アレイの有効なフラグもVAOに保存されているため、

GL.EnableVertexAttribArray(Shaders.PositionDataID);
GL.EnableVertexAttribArray(Shaders.ColorDataID);

メソッドで(もちろん、VAOを作成してバインドした後) 、レンダリング関数でSetDataVAOをバインドすると有効になります。glBindVertexArrayああ、私はちょうど別のエラーを見ました。レンダリング関数でVAOをバインドすると、属性配列の有効なフラグはVAOの状態によって上書きされ、VAOの作成後に有効にしなかったため、引き続き無効になっています。SetDataしたがって、前述のように、メソッドで配列を有効にする必要があります。実際、あなたの場合は幸運かもしれませんが、レンダリング関数で配列を有効にすると(呼び出しなかったためglBindVertexArray(0))、VAOはまだバインドされていますが、それを当てにするべきではありません。

于 2011-07-23T02:14:28.387 に答える