使用 TRIANGLE_FAN 渲染两个圆

Rendering two circles using TRIANGLE_FAN

我正在尝试在 OpenGL 中渲染圆柱体,方法是首先开始使用 TRIANGLE_FAN 渲染顶部和底部大写字母。我将所有顶点数据存储在一个 VBO 中,并为每个帽创建两个索引缓冲区。顶盖正确渲染,但低盖根本没有渲染,您可以在快照中看到它。

这是我的客户端应用程序。

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

我检查了我的顶点数据生成函数,它们是正确的。

这是我的结果快照。

一次只能绑定1个索引缓冲区。您需要 2 次绘制调用并且必须在绘制调用之前绑定索引缓冲区:

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

或者,您可以将索引列表放在 1 个缓冲区中,并用 Primitive Restart 索引将它们分开。

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