Search code examples
copenglglslshadertessellation

Having trouble rendering entire sphere


I'm working on a project that requires me to make spheres. The method that I decided to use is a method called the octa sphere. How it works is I start with an octahedron, as shown below using this wireframe

enter image description here

And through tessellation shaders, I subdivide it, as shown below, (first hint at where the problem is)

enter image description here

And finally, normalize the positions, converting it from an octahedron, into a sphere, made out of an octahedron, as shown below, once again, through a wireframe

enter image description here

So as you can see, starting from subdivision, there has been a problem where a single patch refuses to render and I don't know why.

Let me be clear: my problem is that this single patch is not rendered, and my desired outcome / solution is to fix this problem, so that the entire sphere is drawn, because right now there's that portion that's not drawing, making this only a partial sphere.

My shaders and some OpenGL code is below

My tessellation evaluation shader

#version 450 core

// determines what type of tessellation to do
layout(triangles, equal_spacing, cw) in;

// input from control shader
in vec3 vertex_coord[];
// output vec
out vec3 vert;

// allows for object transformations
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    // gets barycentric coordinates from the triangles
    vec3 u = gl_TessCoord.x * vertex_coord[0];
    vec3 v = gl_TessCoord.y * vertex_coord[1];
    vec3 w = gl_TessCoord.z * vertex_coord[2];
    // makes every triangle an equal distance from the center (that's how spheres are formed)
    vec3 pos = normalize(u + v + w);

    // output tessellated shape
    gl_Position = projection * view * model * vec4(pos, 1.0);
}

My tessellation control shader

#version 450 core

// specify control points per output per patch
// control size of input and output arrays
layout(vertices=3) out;
// input from vertex shader
in vec3 vert_coord[];
// output to evaluation shader
out vec3 vertex_coord[];

// for dynamic LOD (level of detail)
uniform mat4 view;
uniform mat4 model;

void main()
{
    // pass attributes through
    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
    vertex_coord[gl_InvocationID] = vert_coord[gl_InvocationID];

    // control tessellation
    if(gl_InvocationID==0)
    {
        // dynamic LOD (from the learnopengl.com website)
        // first: define rendering constants to control tessellation
        const float MIN_TESS_LEVEL = 4;
        const float MAX_TESS_LEVEL = 64;
        const float MIN_DISTANCE = 20;
        const float MAX_DISTANCE = 800;
        // second: transform each vertex into each eye
        vec4 eye_space_pos_1 = view * model * gl_in[0].gl_Position;
        vec4 eye_space_pos_2 = view * model * gl_in[1].gl_Position;
        vec4 eye_space_pos_3 = view * model * gl_in[2].gl_Position;
        // third: distance from camera scaled between 0 and 1
        float distance_1 = clamp((abs(eye_space_pos_1.z)-MIN_DISTANCE)/(MAX_DISTANCE-MIN_DISTANCE), 0.0, 1.0);
        float distance_2 = clamp((abs(eye_space_pos_2.z)-MIN_DISTANCE)/(MAX_DISTANCE-MIN_DISTANCE), 0.0, 1.0);
        float distance_3 = clamp((abs(eye_space_pos_3.z)-MIN_DISTANCE)/(MAX_DISTANCE-MIN_DISTANCE), 0.0, 1.0);
        // fourth: interpolate edge tessellation level based on closer vertex
        float tess_level_1 = mix(MAX_TESS_LEVEL, MIN_TESS_LEVEL, min(distance_3, distance_1));
        float tess_level_2 = mix(MAX_TESS_LEVEL, MIN_TESS_LEVEL, min(distance_1, distance_2));
        float tess_level_3 = mix(MAX_TESS_LEVEL, MIN_TESS_LEVEL, min(distance_2, distance_1));
        // fifth: set the corresponding outer tessellation levels
        gl_TessLevelOuter[0] = tess_level_1;
        gl_TessLevelOuter[1] = tess_level_2;
        gl_TessLevelOuter[2] = tess_level_3;
        // sixth: set the inner tessellation levels
        gl_TessLevelInner[0] = max(tess_level_2, tess_level_1);
        gl_TessLevelInner[1] = max(tess_level_1, tess_level_3);
    }
}

My vertex shader

#version 450 core

// position of the object
layout (location = 0) in vec3 pos;

// vertices to make the sphere
out vec3 vert_coord;

void main()
{
    // position object
    gl_Position = vec4(pos, 1.0f);
    vert_coord = pos;
}

My octahedron vertices

// octahedron vertices
float vertices[] = {
    //top-north-east
     0.0f, -1.0f,  0.0f,
     0.0f,  0.0f,  1.0f,
     1.0f,  0.0f,  0.0f,

    //top-north-west
     0.0f,  1.0f,  0.0f,
    -1.0f,  0.0f,  0.0f,
     0.0f,  0.0f,  1.0f,

    //top-south-west
     0.0f,  1.0f,  0.0f,
     0.0f,  0.0f, -1.0f,
    -1.0f,  0.0f,  0.0f,

    //top-south-east
     0.0f, -1.0f,  0.0f,
     1.0f,  0.0f,  0.0f,
     0.0f,  0.0f, -1.0f,

    //bottom-north-east
     0.0f, -1.0f,  0.0f,
     1.0f,  0.0f,  0.0f,
     0.0f,  0.0f,  1.0f,

    //bottom-north-west
     0.0f, -1.0f,  0.0f,
     0.0f,  0.0f,  1.0f,
    -1.0f,  0.0f,  0.0f,

    //bottom-south-west
     0.0f, -1.0f,  0.0f,
    -1.0f,  0.0f,  0.0f,
     0.0f,  0.0f, -1.0f,

    //bottom-south-east
     0.0f, 1.0f,  0.0f,
     0.0f,  0.0f, -1.0f,
     1.0f,  0.0f,  0.0f
};

My OpenGL pointers and buffers

unsigned int vbo, vao;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);

glBindVertexArray(vao);

// upload vertex data to gpu
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices) * sizeof(double), &vertices[0], GL_STATIC_DRAW);
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

// normal attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);

// max tessellation points / patches
glPatchParameteri(GL_PATCH_VERTICES, 3);

And finally my draw call

glBindVertexArray(vao);
glDrawArrays(GL_PATCHES, 0, 24);

I tried every resource I could find on triangle tessellation and spheres, as well as the math to calculate both. What I was expecting was the entire sphere to render, including the patch that doesn't, but that didn't happen.


Solution

  • So I went onto a different forum and I asked this question, and somebody helped me solve it. It turns out that the coordinates for the octahedron were wrong. The problematic ones were top-north-east and bottom-south-east. I'll give a list of the new and improved coordinates as well as the output of it.

        float vertices[] = {
        //top-north-east
         0.0f, 1.0f,  0.0f,
         0.0f,  0.0f,  1.0f,
         1.0f,  0.0f,  0.0f,
    
        //top-north-west
         0.0f,  1.0f,  0.0f,
        -1.0f,  0.0f,  0.0f,
         0.0f,  0.0f,  1.0f,
    
        //top-south-west
         0.0f,  1.0f,  0.0f,
         0.0f,  0.0f, -1.0f,
        -1.0f,  0.0f,  0.0f,
    
        //top-south-east
         0.0f, -1.0f,  0.0f,
         1.0f,  0.0f,  0.0f,
         0.0f,  0.0f, -1.0f,
    
        //bottom-north-east
         0.0f, -1.0f,  0.0f,
         1.0f,  0.0f,  0.0f,
         0.0f,  0.0f,  1.0f,
    
        //bottom-north-west
         0.0f, -1.0f,  0.0f,
         0.0f,  0.0f,  1.0f,
        -1.0f,  0.0f,  0.0f,
    
        //bottom-south-west
         0.0f, -1.0f,  0.0f,
        -1.0f,  0.0f,  0.0f,
         0.0f,  0.0f, -1.0f,
    
        //bottom-south-east
         0.0f, 1.0f,  0.0f,
         0.0f,  0.0f, -1.0f,
         1.0f,  0.0f,  0.0f
    };
    

    enter image description here