c++openglglslvbo

Trying to draw sphere in OpenGL, Only part of the sphere is showing, seems clipped?


I created a minimal setup with a fragment shader setting the color to write, so not even a parameter. The vertex shader passes in a matrix and transforms the points. We can see the sphere, but only part of it.

I hesitate to post the whole code, trying as hard as possible for a minimum working solution but it's about 300 lines including the shader loading code. I will post just the core pieces, and if people want more I will post it all.

Here is the code for the demo including a stripped down Sphere class and glmain. Not shown is main() which does try..catch and calls glmain

#include <GL/glew.h>
#include "common/common.hh"
#include <glm/glm.hpp>
#include <glm/ext.hpp>
#include <numbers>
#include <iostream>
#include <iomanip>
#include <cstdint>
#include <string>
using namespace std;
using namespace glm;
using namespace std::numbers;
class Sphere {
    private:
    uint32_t progid; // handle to the shader code
    uint32_t vao; // array object container for vbo and indices
    uint32_t vbo; // handle to the point data on the graphics card
    uint32_t lbo; // handle to buffer of indices for lines for wireframe sphere
    uint32_t latRes, lonRes;
    uint32_t resolution;
    public:
    /**
     * @brief Construct a sphere
     *
     * @param r radius of the sphere
     * @param latRes resolution of the grid in latitude
     * @param lonRes resolution of the grid in latitude
     */
    Sphere(double r, uint32_t latRes, uint32_t lonRes);
    ~Sphere() { cleanup(); }
    void render(mat4& trans);
    void cleanup();
};


Sphere::Sphere(double r, uint32_t latRes, uint32_t lonRes) : latRes(latRes), lonRes(lonRes),
    resolution((2*latRes-2)*lonRes + 2) {
    progid = loadShaders( "05_3d.vert", "02simple.frag" );
    double dlon = 2.0*numbers::pi / lonRes, dlat = numbers::pi / latRes;
    double z;
    double lat = -numbers::pi/2 + dlat; // latitude in radians
    double rcircle;
    float vert[resolution*3]; // x,y,z
    uint32_t c = 0;
    for (uint32_t j = 0; j < 2*latRes-2; j++, lat += dlat) {
    //what is the radius of hte circle at that height?
    rcircle = r* cos(lat); // size of the circle at this latitude
    z = r * sin(lat); // height of each circle
    double t = 0;
    for (uint32_t i = 0; i < lonRes; i++, t += dlon) {
        vert[c++] = rcircle * cos(t), vert[c++] = rcircle * sin(t);
        vert[c++] = z;
    }
    cout << endl;
    }
    // south pole
    vert[c++] = 0;
    vert[c++] = 0;
    vert[c++] = -r;
    
    // north pole
    vert[c++] = 0;
    vert[c++] = 0;
    vert[c++] = r;

    cout << "resolution: " << resolution << endl;
    cout << "predicted num vert components: " << resolution*3 << endl;  
    cout << "actual num vert components: " << c << endl;

    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, resolution, vert, GL_STATIC_DRAW);
    glBindVertexArray(0);
}

void Sphere::render(mat4& trans) {
    glUseProgram(progid);           // Use the shader
    uint32_t matrixID = glGetUniformLocation(progid, "trans");
    glUniformMatrix4fv(matrixID, 1, GL_FALSE, &trans[0][0]);

    glBindVertexArray(vao);
    glVertexAttribPointer(
    0,                  // first parameter to shader, numbered 0
        3,                  // 3 floating point numbers (x,y,z)
        GL_FLOAT,           // type
        GL_FALSE,           // normalized?
        0,                  // this is the entire set of data, move on
        (void*)0            // array buffer offset
    );
    glEnableVertexAttribArray(0); // pass x,y to shader

    glEnable(GL_PROGRAM_POINT_SIZE);

    //points don't work, why not? And how to set the size of the points?
    glPointSize(5);
    glDrawArrays(GL_POINT, 0, resolution);

    // line strips work, but incomplete (see screen shot)
    glDrawArrays(GL_LINE_STRIP, 0, resolution);
    glDisableVertexAttribArray(0);
}

void Sphere::cleanup() {
    glDeleteBuffers(1, &vbo);   // remove vbo memory from graphics card
    glDeleteVertexArrays(1, &vao); // remove vao from graphics card
    glDeleteProgram(progid);
}

using namespace std;

void glmain() {
    win = createWindow(800, 800, "Sphere demo");

    glClearColor(0.0f, 0.0f, 0.4f, 0.0f);   // Dark blue background
    Sphere sphere(1.0, 30, 15);
    mat4    trans= lookAt(vec3(0,0,0), vec3(10,5,10), vec3(0,1,0));
    do {
        glClear( GL_COLOR_BUFFER_BIT );     // Clear the screen
        //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glDisable(GL_DEPTH_TEST);
        //glDepthFunc(GL_LESS);
        sphere.render(trans);
        glfwSwapBuffers(win);             // double buffer
        glfwPollEvents();
    }   while( glfwGetKey(win, GLFW_KEY_ESCAPE ) != GLFW_PRESS &&
                        glfwWindowShouldClose(win) == 0 );
}

Points did not display at all so the call is commented out. We drew a line strip instead. That works somewhat. Why is it truncated? Why doesn't it at least finish the layer of the sphere?

The shaders are shown below:

#version 330 core

// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 v;
uniform mat4 trans;

void main(){
    gl_PointSize = 5;
    gl_Position = trans * vec4(v,1.0);
    gl_Position.w = 1.0;
}

fragment shader:

#version 330 core

out vec4 color;

void main()
{
    color = vec4(1,1,1,1);
}

Solution

  • The size argument of glBufferData specifies the size in bytes of the buffer object's new data store:

    glBufferData(GL_ARRAY_BUFFER, resolution, vert, GL_STATIC_DRAW);

    glBufferData(GL_ARRAY_BUFFER, resolution * 3 * sizeof(float), vert, GL_STATIC_DRAW);