Home > Mobile >  Rendering two circles using TRIANGLE_FAN
Rendering two circles using TRIANGLE_FAN

Time:11-07

I am trying to render a cylinder in OpenGL, by first starting to render top and lower caps using TRIANGLE_FAN. I store all the vertex data in one VBO and create two index buffers one for each cap. The top cap gets rendered correctly however the low cap does not get rendered at all, you can see it in the snapshot.

Here is my client application.

#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>

#include <GL/glew.h>
#include <GLFW/glfw3.h>

#include <glm/vec3.hpp> // glm::vec3
#include <glm/vec4.hpp> // glm::vec4
#include <glm/mat4x4.hpp> // glm::mat4
#include <glm/gtc/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale, glm::perspective

using namespace std;

#define PI 3.14159265359


static string ParseShader(string filepath) {
    ifstream stream(filepath);
    string line;
    stringstream stringStream;

    while (getline(stream, line))
    {
        stringStream << line << '\n';
    }

    return stringStream.str();
}

static unsigned int CompileShader(unsigned int type, const string& source) {
    unsigned int id = glCreateShader(type);
    const char* src = source.c_str(); // this returns a pointer to data inside the string, the first character
    glShaderSource(id, 1, &src, nullptr); // shader id, count of source codes, a pointer to the array that holds the strings
    glCompileShader(id);

    int result;
    glGetShaderiv(id, GL_COMPILE_STATUS, &result);
    if (result == GL_FALSE) {
        int length;
        glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
        char* message = (char*)alloca(length * sizeof(char));
        glGetShaderInfoLog(id, length, &length, message);
        cout << type << endl;
        cout << message << endl;
        glDeleteShader(id);
        return 0;
    }
    return id;
}

// takes the shader codes as a string parameters 
static unsigned int CreateShader(const string& vertexShader, const string& fragmentShader)
{
    GLuint program = glCreateProgram();
    unsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader);
    unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);


    glAttachShader(program, vs);
    glAttachShader(program, fs);
    glLinkProgram(program);
    glValidateProgram(program); // validate if the program is valid and can be run in the current state of opengl

    glDeleteShader(vs);
    glDeleteShader(fs);

    return program;
}

std::vector<float> GenerateVertexData(float radius, float cylinderHeight, int vertexCount) {
    int bufferSize = 3 * vertexCount;
    std::vector<float> result(3 * vertexCount);

    result[0] = 0;
    result[1] = 0;
    result[2] = cylinderHeight / 2;

    float angleDelta = (360 / (vertexCount - 1)) * PI / 180;
    float currentAngle = 0;

    for (int i = 1; i < vertexCount;   i) {
        result[  3 * i  ] = cos(currentAngle) * radius;
        result[3 * i   1] = sin(currentAngle) * radius;
        result[3 * i   2] = cylinderHeight / 2;

        currentAngle  = angleDelta;
    }

    for (int i = 0; i < bufferSize;   i) {
        std::cout << "Result [ " << i << " ]" << result[i] << std::endl;
    }

    return result;
}

std::vector<GLuint> GenerateIndexData(int vertexCount) {
    std::vector<GLuint> result(vertexCount   1);

    for (int i = 0; i < vertexCount;   i) {
        result[i] = i;
    }

    result[vertexCount] = 1;
    
    return result;
}

std::vector<GLuint> GenerateLowCapIndexData(int vertexCount) {
    std::vector<GLuint> result(vertexCount   1);

    for (int i = 0; i < vertexCount;   i) {
        result[i] = i   vertexCount;

        std::cout << "Index [ " << i << " ]" << result[i] << std::endl;
    }

    result[vertexCount] = 1   vertexCount;
    std::cout << "Index [ " << result.size() << " ]" << result.back() << std::endl;
    return result;
}

std::vector<GLuint> generateBodyIndexData(int verticalSegments) {
    std::vector<GLuint> result(2 * (verticalSegments   1));

    for (int i = 0; i < verticalSegments;   i) {
        result[  2 * i  ] = i;
        result[2 * i   1] = i   verticalSegments;
    }

    result[result.size() - 2] = result.front();
    result.back() = result[1];

    return result;
}

int main(int argc, char* argv[]) {
    float radius = std::stof(argv[1]);
    float cylinderHeight = std::stof(argv[2]);
    int verticalSegments = std::stoi(argv[3]);

    std::cout << "Vertical segments : " << verticalSegments << std::endl;
    int totalVertexCount = verticalSegments   1;

    const int vertexCoordinateCount = 3;
    std::vector<float> vertexData = GenerateVertexData(radius, cylinderHeight, totalVertexCount);
    std::vector<float> vertexDataLowerHalf = GenerateVertexData(radius, -cylinderHeight, totalVertexCount);

    appendVec(vertexData, vertexDataLowerHalf);

    std::vector<GLuint> topCapIndexData = GenerateIndexData(totalVertexCount);
    std::vector<GLuint> lowCapIndexData = GenerateLowCapIndexData(totalVertexCount);

    GLFWwindow* window;

    /* Initialize the library */
    if (!glfwInit())
        return -1;

    window = glfwCreateWindow(640, 480, "HW3", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        return -1;
    }

    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    if (glfwRawMouseMotionSupported())
        glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);

    glfwMakeContextCurrent(window);

    GLenum err = glewInit();
    if (GLEW_OK != err)
    {
        /* Problem: glewInit failed, something is seriously wrong. */
        fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
    }

    //Positions
    GLuint positionBufferHandle;
    glGenBuffers(1, &positionBufferHandle);
    glBindBuffer(GL_ARRAY_BUFFER, positionBufferHandle);
    glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(float), vertexData.data(), GL_STATIC_DRAW);

    glVertexAttribPointer(0, vertexCoordinateCount, GL_FLOAT, GL_FALSE, sizeof(float) * vertexCoordinateCount, 0);
    glEnableVertexAttribArray(0);

    //TopCap Indices
    GLuint indexBufferHandle;
    glGenBuffers(1, &indexBufferHandle);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferHandle);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, topCapIndexData.size() * sizeof(GLuint), topCapIndexData.data(), GL_STATIC_DRAW);

    //LowCap Indices
    GLuint lowCapIndexBufferHandle;
    glGenBuffers(1, &lowCapIndexBufferHandle);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lowCapIndexBufferHandle);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, lowCapIndexData.size() * sizeof(GLuint), lowCapIndexData.data(), GL_STATIC_DRAW);

    string vertexSource = ParseShader("vertex.shader");
    string fragmentSource = ParseShader("fragment.shader");

    unsigned int program = CreateShader(vertexSource, fragmentSource);
    
    /* Loop until the user closes the window */
    while (!glfwWindowShouldClose(window))
    {
        // Render here 
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glUseProgram(program);

        glDrawElements(GL_TRIANGLE_FAN, topCapIndexData.size()   lowCapIndexData.size(), GL_UNSIGNED_INT, nullptr);

        //Swap front and back buffers 
        glfwSwapBuffers(window);

        // Poll for and process events 
        glfwPollEvents();
    }

    glDeleteProgram(program);

    glfwTerminate();
    return 0;
}

I have checked my vertex data generating functions and they are correct.

Here is the snapshot of my result. Caps from the side

Top Cap correctly rendered

CodePudding user response:

Only 1index buffer can be bound at a time. You need 2 draw calls and must bind the index buffer before the draw call:

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferHandle);
glDrawElements(GL_TRIANGLE_FAN, topCapIndexData.size(), GL_UNSIGNED_INT, nullptr);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lowCapIndexBufferHandle);
glDrawElements(GL_TRIANGLE_FAN, lowCapIndexData.size(), GL_UNSIGNED_INT, nullptr);

Alternatively you can put the list of indices in 1 buffer and separate them with a Primitive Restart index.

GLuint restartIndex = 0xffffffff;
std::vector<GLuint> indices = topCapIndexData;
indices.push_back(restartIndex);
indices.insert(indices.end(), lowCapIndexBufferHandle.begin(), lowCapIndexBufferHandle.end();

GLuint indexBufferHandle;
glGenBuffers(1, &indexBufferHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferHandle);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(), GL_STATIC_DRAW);
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(restartIndex);
glDrawElements(GL_TRIANGLE_FAN, indices.size(), GL_UNSIGNED_INT, nullptr);
  • Related