-2

I'm trying to create a triangle mesh of a sphere surface and draw it with OpenGL 4.1.

This is the code that I'm currently using obtained from the second answer of that question, The vertex layout is [x, y, z, r, b, g, a] that's why there is 7 float foreach vertex:

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;
    }

But I am doing something wrong because that's what I'm drawing: Images of result

the code that I'm using to draw is:

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

EDIT 1: The VAO settings are pretty complicated because I wrote a little layer of abstraction over opengl... I have a class called VertexBuffer that creates, keeps alive and destroys an OpenGL array buffer. Another class IndexBuffer is very similar to the previous one that manages the Element array buffer. This two classes are very simple to use they can be constructed, binded, unbinded and destroyed, nothing more. There is a third class that represents a layout of a single vertex in an OpenGL vertex buffer; this class called VertexLayout contains all the data that is necessary to call the 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) {}

So an instance of VertexLayout should be created foreach VertexBuffer object and foreach opengl attribute should be called a push<type>(numberOfElementOfThatType).

The fourth and last class is the VertexArray class that represents a VAO: this last class keeps trace of all the VertexBuffer and IndexBuffer objects that are connected to the vao and sets the layout calling glVertexAttribPointer when a VertexBuffer is added using the following method:

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 is a macro that does nothing in release while in debug is clears the OpenGL erros and prints the new errors.

EDIT 2: This is the class VertexBuffer that represents one 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));
}

In the sphere I have only one big buffer that contains both positions and colors.

Noè Murr
  • 496
  • 4
  • 11
  • 2
    Could you please add the VAO setup? glVertexAttribPointer and so on? – BDL Oct 03 '18 at 18:32
  • There is no obvious issue in the code. – Rabbid76 Oct 03 '18 at 19:02
  • 1
    Use RenderDoc to capture your frame. Them you can analyze what actually goes to API and you can try to figure out why it looks like this. – Michael Nastenko Oct 03 '18 at 23:24
  • @MichaelNastenko unfortunately I'm working on macOS and it seems that RenderDoc does not work in mac. – Noè Murr Oct 03 '18 at 23:38
  • Can you also add how you set up the index buffer? Also, I believe the stride calculation is wrong. If the data is compacted, the stride should be the size of each buffer element, if its interleaved, it should be computed after you have added all the parameters to the the buffer. Are you using 1 vbo for all the vertex attributes or each has its own? – Nadir Oct 04 '18 at 08:55
  • 1
    @NoèMurr Then use Intel GPA – Michael Nastenko Oct 04 '18 at 11:31
  • @Nadir You are probably right, because if I change the stride with 0 the output changes. But I cannot understand why... the Stride is 28 that is the exact value of `7 * sizeof(float)`. – Noè Murr Oct 04 '18 at 19:20
  • 1
    @NoèMurr If you have all your data compated (each attribute like vertex, normals, etc. are on its own vbo or they are in the same vbo but compated, which means, all vertex first, then all normals, etc..), the stride should just be the size of each element. In the case of vertex, for example, the stride should be `sizeof(float) * 3` (or `sizeof(float) * 4` if you are sending 4 components). If they are interleaved, the stride will be the same for all, and is computed by adding the size of each elements (`sizeof(float) * 3 + sizeof(float) * 3` if you have vertices and normals only). – Nadir Oct 05 '18 at 07:00
  • @Nadir I have added the class VertexBuffer to the question. I have all vertices in one buffer: [[vertex1][vertex2][vertex...][vertexN]] and each vertex has the layout explained in the question [x, y, z, r, g, b, a] so first coordinates and then the colors. Is the stride of `7 * sizeof(float)` (28) correct? – Noè Murr Oct 05 '18 at 09:14
  • @NoèMurr yes, they seem all correct (both stride and offset). Could it be that you are adding the faces in a clock wise order? Try `glDisable(GL_CULLFACE)` or `glFrontFace(GL_CW)` to see if your sphere appear? – Nadir Oct 05 '18 at 09:34
  • @Nadir same result with `glFrontFace(GL_CW)` and with `glDisable(GL_CULL_FACE)` (the macro `GL_CULLFACE` does not exists in my OpenGL's headers so I suppose it is equal to `GL_CULL_FACE`) – Noè Murr Oct 05 '18 at 10:38
  • @NoèMurr yes, `GL_CULLFACE` does not exists, it was a typo, sorry. One last resort I can think of, how do you compute the `indicesNumber` you pass to the draw command? – Nadir Oct 06 '18 at 08:33
  • @Nadir I use the size of std::vector: `indicesNumber = static_cast(indices.size());` – Noè Murr Oct 07 '18 at 13:30
  • as you can see, I found the solution. Thanks for your help. – Noè Murr Oct 08 '18 at 08:55

1 Answers1

0

I found the solution. It was a very stupid error: The constructor of the VertexBuffer class needs the size of the buffer in bytes but when I called it I passed only the size of the std::vector that is the number of elements.

Noè Murr
  • 496
  • 4
  • 11