OpenGLのモダンなデザイン、モデルのインポートメッシュクラス

免責事項:この記事はブロガーオリジナル記事ですが、許可ブロガーなく再生してはなりません。https://blog.csdn.net/leon_zeng0/article/details/88885307

この記事では、メインの参照  https://learnopengl.com/   と  https://learnopengl-cn.github.io/  から学びます。

Assimpを使用して、我々はプログラムに異なるモデルを読み込むことができますが、我々はすべてのデータを保存したい、我々は合理的なデータ構造を必要とします。この記事では、単一のエンティティを保存するようなクラス、クラスのグリッドは、描画することができ、必要なデータを記述する。全モデルは、我々が表示する必要がメッシュの複数、またはグリッドベクトル合成モデルで構成されています。

まず、グリッドはデータの最小値を必要とするものについて考え、私たちがこれまで学んできた知識を見てみましょう。グリッドは、各ベクトル座標位置ベクトル、法線ベクトルとテクスチャを含む、頂点の数を必要としました。また、これは図面をインデックスするためのグリッド・インデックス、および材料の形態(拡散反射/鏡面反射マップ)内のテクスチャデータを含むべきです。

私たちは、グリッドのクラスを持っている最小要件以来、私たちは、OpenGLで頂点を定義することができます。

struct Vertex {
    glm::vec3 Position;
    glm::vec3 Normal;
    glm::vec2 TexCoords;
};

我々は、すべて私たちがインデックスに各頂点の属性を使用することができ、頂点構造と呼ばれるベクトルを格納する必要があります。

頂点構造に加えて、我々はまた、テクスチャ構造にテクスチャデータを整理する必要があります。

struct Texture {
    unsigned int id;
    string type;
};

我々は、拡散テクスチャマップまたは鏡面反射光として、テクスチャIDとそのタイプを記憶します。

私たちは、クラスのグリッド構造を定義するために始めることができ、知っていて、頂点テクスチャを実現します:

class Mesh {
    public:
        /*  网格数据  */
        vector<Vertex> vertices;
        vector<unsigned int> indices;
        vector<Texture> textures;
        /*  函数  */
        Mesh(vector<Vertex> vertices, vector<unsigned int> indices, vector<Texture> textures);
        void Draw(Shader shader);
    private:
        /*  渲染数据  */
        unsigned int VAO, VBO, EBO;
        /*  函数  */
        void setupMesh();
};  

あなたは、このクラスは複雑ではないことがわかります。コンストラクタでは、我々はすべての必要なデータグリッドを与えられます、我々はsetupMesh機能でバッファを初期化し、最終用途のドロー機能は、グリッドを描画します。我々はシェーダー機能を描画するために導入されることに注意してください、送電網シェーダクラスは、私たちは(そのようなテクスチャサンプラユニットへのリンクとして)描画する前に、いくつかの制服を設定することができます。

コンテンツビルダーは、理解することは非常に簡単です。私たちは、それだけにパブリック変数のクラスのコンストラクタのパラメータセットを使用する必要があります。また、コンストラクタ関数のsetupMeshを呼び出します。

Mesh(vector<Vertex> vertices, vector<unsigned int> indices, vector<Texture> textures)
{
    this->vertices = vertices;
    this->indices = indices;
    this->textures = textures;

    setupMesh();
}

何も言うことはありません。私たちは、次のsetupMesh機能を議論します。

初期化

コンストラクタのおかげで、我々は今、レンダリングするための大きな柱のグリッドデータを持っています。我々は正しいバッファを設定する必要がありますし、頂点シェーダのレイアウトは、頂点ポインタ属性によって定義される前に。今、あなたはこれらの概念に非常に精通している必要がありますが、今回は中に頂点データ構造を使用して、少し変更されます。

void setupMesh()
{
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);  

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), 
                 &indices[0], GL_STATIC_DRAW);

    // 顶点位置
    glEnableVertexAttribArray(0);   
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
    // 顶点法线
    glEnableVertexAttribArray(1);   
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));
    // 顶点纹理坐标
    glEnableVertexAttribArray(2);   
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords));

    glBindVertexArray(0);
}  

そして、あなたはコードが全く異なっを持つべきではないと思うが、助け頂点構造で、我々はいくつかのヒントを使用します。

C ++構造体は、その優れた特徴を有している連続的なメモリレイアウト(シーケンシャル)です。すなわち、(実際にはバイト)、我々は、データアレイとして使用される構造ならば、それは我々が直接必要な配列バッファ中に浮遊するように変換される可変構造の順になり、あるアレイ。私たちは、充填後の頂点構造を持っている場合たとえば、そのメモリレイアウトは次のようになります。

Vertex vertex;
vertex.Position  = glm::vec3(0.2f, 0.4f, 0.6f);
vertex.Normal    = glm::vec3(0.0f, 1.0f, 0.0f);
vertex.TexCoords = glm::vec2(1.0f, 0.0f);
// = [0.2f, 0.4f, 0.6f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f];

この有用な特性のおかげで、我々は直接glBufferDataに使用することができる完全な変換パラメータとなるデータバッファとして大きな頂点列を構造体へのポインタを渡すことが可能です。

glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);

NATURAL sizeof算術演算は構造のそのバイトサイズを使用することができます。これは、32バイト(フロート当たり8×4バイト)であるべきです。

構造のもう一つの良い使用は、その前処理指令でoffsetof(s, m)、その最初のパラメータが2番目のパラメータは構造体の変数の名前であり、構造体です。マクロは、ヘッドの構造からオフセット変数のバイト(バイトオフセット)を返します。このオフセットパラメータは、関数glVertexAttribPointerのちょうど定義で使用することができます。

glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal)); 

今、定義するために使用されるオフセットoffsetofはここオフセット方法フランスベクトル構造をセットするバイトオフセットベクトル、すなわち3台のフロート、即ち12バイト。我々はまた、頂点構造体に設定されたサイズパラメータをステップになることに注意してください。

このような構造を使用するだけではなく、より読みやすいコードを提供し、私たちは、簡単な構造を拡張することができます。私たちは別の頂点属性を追加したい場合は、我々はそれだけの構造に追加する必要があります。理由は、その柔軟性により、レンダリングコードが破壊されることはありません。

与えます

私たちは、メッシュクラス最後の関数、そのDraw関数を定義する必要があります。実際にグリッドをレンダリングする前に、我々はglDrawElementsは、対応するテクスチャをバインドする関数を呼び出す前に、まず必要です。しかし、これは実際にはやや困難であり、我々はスタートグリッド(もしあれば)テクスチャの番号がわからない、質感はどのような種類があります。だから、どのように我々は、テクスチャサンプラユニットを設定し、シェーダでいますか?

この問題を解決するために、我々は命名標準を設定する必要があります。それぞれが拡散テクスチャ命名されたtexture_diffuseN正反射光テクスチャのそれぞれが名前を付ける必要があり、texture_specularNここで、N範囲はテクスチャサンプラの最大許容数に1です。例えば、我々は特定のグリッドに3つの拡散テクスチャ、テクスチャ2つの正反射光を持って、彼らのテクスチャサンプラは、起動する必要があります。

uniform sampler2D texture_diffuse1;
uniform sampler2D texture_diffuse2;
uniform sampler2D texture_diffuse3;
uniform sampler2D texture_specular1;
uniform sampler2D texture_specular2;

この基準によると、あなたが本当にグリッド(あまり)テクスチャが含まれている場合、我々は、シェーダでテクスチャサンプラの任意の所望の数を定義することができ、我々は、はい何自分の名前を知ることができます。この基準によると、我々はグリッドテクスチャ内の任意の数を扱うことができ、開発者が自由に使用したい番号を選択することができ、彼は唯一の少ないが、定義されたものの(それに正しいサンプラーを定義する必要があり、その後、ビットが結合無駄かつ均一コール)。

この質問のように、さまざまなソリューションがあります。あなたはこのソリューションを好きではない場合は、独自のソリューションのいずれかを所有することができます。

このように、最終的なレンダリングコード:

void Draw(Shader shader) 
{
    unsigned int diffuseNr = 1;
    unsigned int specularNr = 1;
    for(unsigned int i = 0; i < textures.size(); i++)
    {
        glActiveTexture(GL_TEXTURE0 + i); // 在绑定之前激活相应的纹理单元
        // 获取纹理序号(diffuse_textureN 中的 N)
        string number;
        string name = textures[i].type;
        if(name == "texture_diffuse")
            number = std::to_string(diffuseNr++);
        else if(name == "texture_specular")
            number = std::to_string(specularNr++);

        shader.setFloat(("material." + name + number).c_str(), i);
        glBindTexture(GL_TEXTURE_2D, textures[i].id);
    }
    glActiveTexture(GL_TEXTURE0);

    // 绘制网格
    glBindVertexArray(VAO);
    glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
}

まず、対応する均一な名前を取得するために、テクスチャ・タイプ列に各成分型N-に対するテクスチャ、およびスプライシングを計算します。次の我々は対応するサンプラーを見つけ、その位置の値は、現在アクティブなテクスチャユニットに設定され、テクスチャが結合されています。我々はシェーダー描画機能を必要とする理由です。我々はまた、ます"material."、我々は(各実装で異なっを有していてもよい)材料で構造体に格納されたテクスチャをしたいので、最終的に均一な名前に追加します。

私たちはカウンターに反射と鏡面光のカウンタを拡散することに注意してくださいstringstream彼らは増分されたとき。C ++、このインクリメント動作で:variable++変数自体が返され、その後再びインクリメントされ、++variableある第一の増加、戻り値。我々の例では、最初のオリジナルのカウンタ値に挿入されstringstream、次いで、使用の次のサイクルのためにそれの後にインクリメント。

あなたはできますここで、クラスのメッシュのための完全なソースコードを検索します

このグリッドクラスで、私たちはこれに基づいて、構造の3次元モデルとOpenGL表示をレンダリングすることができます。

おすすめ

転載: blog.csdn.net/leon_zeng0/article/details/88885307