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