Сферная сетка в современном OpenGL - PullRequest
0 голосов
/ 03 октября 2018

Я пытаюсь создать треугольную сетку поверхности сферы и нарисовать ее с помощью OpenGL 4.1.

Это код, который я сейчас использую, полученный из второго ответа на этот вопрос, расположение вершин - [x, y, z, r, b, g, a], поэтому существует 7 вершин с плавающей точкой:

std::vector<float> vertices;
std::vector<unsigned int> indices;
const float dLambda = 2 * glm::pi<float>() / meridianNumber;
const float dPhi = glm::pi<float>() / parallelNumber;
unsigned int lastVertex = 0;


for (int i = 0; i < parallelNumber; ++i) {
    for (int j = 0; j < meridianNumber; ++j) {
        std::cout << "lot: " << glm::degrees(j * dLambda);
        std::cout << "\tlat: " << glm::degrees(i * dPhi);
        std::cout << std::endl;
        float lambda1 = j * dLambda;
        float phi1 = i * dPhi;
        float lambda2 = j+1 == parallelNumber   ? 2 * glm::pi<float>()
                                                : (j+1) * dLambda;
        float phi2 = i+1 == meridianNumber   ? glm::pi<float>()
                                             : (i+1) * dPhi;

        // vertex 1
        vertices.emplace_back(cosf(lambda1) * sinf(phi1) * radius);
        vertices.emplace_back(cosf(phi1) * radius);
        vertices.emplace_back(sinf(lambda1) * sinf(phi1) * radius);
        vertices.emplace_back(0.5f);
        vertices.emplace_back(1.0f);
        vertices.emplace_back(1.0f);
        vertices.emplace_back(1.0f);

        // vertex 2
        vertices.emplace_back(cosf(lambda1) * sinf(phi2) * radius);
        vertices.emplace_back(cosf(phi2) * radius);
        vertices.emplace_back(sinf(lambda1) * sinf(phi2) * radius);
        vertices.emplace_back(0.5f);
        vertices.emplace_back(1.0f);
        vertices.emplace_back(1.0f);
        vertices.emplace_back(1.0f);

        // vertex 3
        vertices.emplace_back(cosf(lambda2) * sinf(phi1) * radius);
        vertices.emplace_back(cosf(phi1) * radius);
        vertices.emplace_back(sinf(lambda2) * sinf(phi1) * radius);
        vertices.emplace_back(0.5f);
        vertices.emplace_back(1.0f);
        vertices.emplace_back(1.0f);
        vertices.emplace_back(1.0f);

        // vertex 4
        vertices.emplace_back(cosf(lambda2) * sinf(phi2) * radius);
        vertices.emplace_back(cosf(phi2) * radius);
        vertices.emplace_back(sinf(lambda2) * sinf(phi2) * radius);
        vertices.emplace_back(0.5f);
        vertices.emplace_back(1.0f);
        vertices.emplace_back(1.0f);
        vertices.emplace_back(1.0f);

        indices.emplace_back(lastVertex);
        indices.emplace_back(lastVertex+1);
        indices.emplace_back(lastVertex+2);

        indices.emplace_back(lastVertex+1);
        indices.emplace_back(lastVertex+3);
        indices.emplace_back(lastVertex+2);

        lastVertex += 4;
    }

Но я делаю что-то не так, потому что это то, чтоЯ рисую: Images of result

код, который я использую для рисования:

GLCall(glDrawElements(
    GL_TRIANGLES,
    indicesNumber,
    GL_UNSIGNED_INT,
    (const void*) 0
));

РЕДАКТИРОВАТЬ 1: Настройки VAO довольно сложны, потому что я написал небольшой слой абстракции над opengl ... У меня есть класс с именем VertexBuffer, который создает, поддерживает и уничтожает буфер массива OpenGL.Другой класс IndexBuffer очень похож на предыдущий, который управляет буфером массива Element.Эти два класса очень просты в использовании, их можно создавать, связывать, разворачивать и уничтожать, не более того.Существует третий класс, который представляет макет одной вершины в буфере вершин OpenGL;этот класс с именем VertexLayout содержит все данные, необходимые для вызова glVertexAttribPointer.

hpp:

class VertexLayout {
private:
    struct Element {
        unsigned int type;
        unsigned int count;
        unsigned char normalized;
        size_t typeSize;

        Element(
            unsigned int type, unsigned int count, unsigned char normalized,
            size_t typeSize
        );
    };
    std::vector<Element> elements;
    unsigned int stride;

public:

    VertexLayout();

    template<typename T>
    VertexLayout &push(unsigned int count, unsigned char normalized = GL_FALSE){
        std::fputs(
            "this function has to be implemented for desired type",
            stderr
        );
        assert(false);
        return *this;
    }

    const std::vector<Element> &getElements() const;

    unsigned int getStride() const;

};

cpp:

template<>
VertexLayout &VertexLayout::push<unsigned int>(
    unsigned int count, unsigned char normalized
) {
    elements.emplace_back(
        GL_UNSIGNED_INT, count, normalized, sizeof(unsigned int)
    );
    stride += count * sizeof(unsigned int);
    return *this;
};

template<>
VertexLayout &VertexLayout::push<unsigned char>(
    unsigned int count, unsigned char normalized
) {
    elements.emplace_back(
        GL_UNSIGNED_BYTE, count, normalized, sizeof(unsigned char)
    );
    stride += count * sizeof(unsigned char);
    return *this;
};

template<>
VertexLayout &VertexLayout::push<float>(unsigned int count, unsigned char normalized){
    elements.emplace_back(GL_FLOAT, count, normalized, sizeof(float));
    stride += count * sizeof(float);
    return *this;
}

VertexLayout::Element::Element(
    unsigned int type, unsigned int count,
    unsigned char normalized, size_t typeSize
) : type(type), count(count), normalized(normalized), typeSize(typeSize) {}

const std::vector<VertexLayout::Element> &VertexLayout::getElements()   const {
    return elements;
}

unsigned int VertexLayout::getStride() const {
    return stride;
}

VertexLayout::VertexLayout() : stride(0) {}

Таким образом, экземпляр VertexLayout долженбыть создан foreach объект VertexBuffer, а атрибут foreach opengl должен называться push<type>(numberOfElementOfThatType).

Четвертый и последний класс - это класс VertexArray, который представляет VAO: этот последний класс отслеживает все объекты VertexBuffer и IndexBuffer, которые подключены к vao, и устанавливает макет, вызывающий glVertexAttribPointer, когда VertexBuffer добавляется с использованиемследующий метод:

void VertexArray::addBuffer(
    const VertexBuffer &buffer, const VertexLayout &layout
) {
    GLCall(glBindVertexArray(id));
    buffer.bind();
    const auto &elements = layout.getElements();
    size_t offset = 0;
    for (unsigned int i = 0; i < elements.size(); ++i) {
        const auto &element = elements[i];
        GLCall(glEnableVertexAttribArray(i));
        GLCall(glVertexAttribPointer(
            i, element.count, element.type, element.normalized,
            layout.getStride(), (const void *)offset
        ));
        offset += element.count * element.typeSize;
    }
    vertexBuffers.emplace_back(buffer);
}

GLCall - это макрос, который ничего не делает при освобождении, в то время как при отладке очищает ошибки OpenGL и печатает новые ошибки.

EDIT 2: Это класс VertexBuffer, представляющий один VBO:

hpp

class VertexBuffer {
private: // static
    static std::map<unsigned int, unsigned int> references;

private: // member
    unsigned int rendererID;

public:
    VertexBuffer();

    VertexBuffer(
        const void *data, unsigned long size,
        unsigned int usage = GL_STATIC_DRAW
    );
    VertexBuffer(const VertexBuffer &oth);
    VertexBuffer &operator=(const VertexBuffer &rhs);

    ~VertexBuffer();

    void bind() const;
    void unbind() const;
};

cpp:

std::map<unsigned int, unsigned int> VertexBuffer::references;

VertexBuffer::VertexBuffer(
    const void *data,
    unsigned long size,
    unsigned int usage
) {
    GLCall(glGenBuffers(1, &rendererID));
    GLCall(glBindBuffer(GL_ARRAY_BUFFER, rendererID));
    GLCall(glBufferData(GL_ARRAY_BUFFER, size, data, usage));
    references.insert_or_assign(rendererID, 1);
}

VertexBuffer::VertexBuffer(const VertexBuffer &oth) {
    if (oth.rendererID != 0){
        auto ref = references.find(oth.rendererID);
        assert(ref != references.end());
        ref->second++;
    }
    rendererID = oth.rendererID;
}

VertexBuffer &VertexBuffer::operator=(const VertexBuffer &rhs) {
    if (rendererID != 0) {
        auto refs = references.find(rendererID);
        assert(refs != references.end());

        if (--refs->second == 0) {
            GLCall(glDeleteBuffers(1, &rendererID));
            references.erase(refs);
        }
    }
    if (rhs.rendererID != 0){
        auto ref = references.find(rhs.rendererID);
        assert(ref != references.end());
        ref->second++;
    }
    rendererID = rhs.rendererID;

    return *this;
}

VertexBuffer::VertexBuffer() : rendererID(0) {}

VertexBuffer::~VertexBuffer() {
    if (rendererID != 0) {
        auto ref = references.find(rendererID);
        assert(ref != references.end());
        if (--ref->second == 0) {
            GLCall(glDeleteBuffers(1, &rendererID));
            references.erase(ref);
        }
    }
}

void VertexBuffer::bind() const {
    GLCall(glBindBuffer(GL_ARRAY_BUFFER, rendererID));
}

void VertexBuffer::unbind() const {
    GLCall(glBindBuffer(GL_ARRAY_BUFFER, 0));
}

В сфере у меня есть только один большой буфер, который содержит обапозиции и цвета.

1 Ответ

0 голосов
/ 08 октября 2018

Я нашел решение.Это была очень глупая ошибка: конструктору класса VertexBuffer нужен размер буфера в байтах, но когда я его вызвал, я передал только размер std :: vector, который является количеством элементов.

...