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 바이트 (여덟 플로트 * 당 4 바이트)이어야한다.

구조의 또 다른 좋은 사용은 전처리 지시자이다 offsetof(s, m)번째 파라미터는 상기 구조 변수의 이름, 그것의 첫 번째 파라미터는 구조이다. 매크로 헤드의 구조와 가변 바이트 오프셋 (바이트 오프셋)를 반환한다. 이 오프셋 매개 변수는 함수 glVertexAttribPointer의 바로 정의에 사용할 수 있습니다 :

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

이제 정의하는데 사용되는 오프셋을 offsetof 여기서 오프셋있어서 프랑스 벡터 구조를 설정할 것이다 바이트 오프셋 벡터, 즉 세 개의 플로트, 즉 12 바이트. 우리는 또한 정점 구조로 설정 크기 매개 변수를 단계 않습니다.

이러한 구조의 사용뿐만 아니라 또한 더 읽을 수있는 코드를 제공합니다 우리가 쉽게 구조를 확장 할 수 있습니다. 우리는 또 다른 정점 속성을 추가하려는 경우, 우리는 그것을 구조에 추가해야합니다. 때문에 유연성의 렌더링 코드는 파괴되지 않습니다.

렌더링

우리는 메쉬 클래스를 마지막으로 기능의 그리기 함수를 정의 할 필요가있다. 실제로 그리드를 렌더링하기 전에, 우리는 기능 glDrawElements는 해당 텍스처를 결합 호출하기 전에 먼저해야합니다. 그러나, 실제로는 다소 어렵습니다, 우리가 시작 그리드 (있는 경우) 질감의 번호를 모르는, 텍스처 유형이다. 그래서 우리가 어떻게 쉐이더에서 다음 텍스처 샘플러 단위를 설정합니까?

이 문제를 해결하기 위해, 우리는 네이밍 표준을 설정해야 각각 확산 텍스처라는 texture_diffuseN경면 광 질감 각각 지명해야 texture_specularN있어서, N범위는 샘플러 최대 허용 횟수는 1이다. 예를 들어, 우리는 세 가지 확산 텍스처가 특정 그리드 질감이 반사 빛이, 자신의 텍스처 샘플러가 다음 호출해야 있습니다 :

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는 IS 증가하고 리턴 값. 이 예에서, 제 원래 카운터 값에 삽입 stringstream한 다음 사용을 다음주기 이후 증가 대.

당신은 할 수 있습니다 여기에 클래스 메쉬에 대한 완전한 소스 코드를 찾을 수

이 그리드 클래스로, 우리가 할 수있는이 기초, 구조 3 차원 모델 및 OpenGL 디스플레이 렌더링.

추천

출처blog.csdn.net/leon_zeng0/article/details/88885307