I'm working on a program where I have to draw spheres. My chosen method of doing so is to create an octa sphere. How that's done is I start by creating an octahedron
And then from there, I, through tessellation shaders, subdivide the octahedron, at which point you could see the problem forming
And finally, normalize the UVW coordinates of the octahedron, to form an octa-sphere, which is just a sphere that was created from a tessellated octahedron
As you can (hopefully) see, starting from octahedron subdivision, a patch of the shape stopped rendering, and I don't know why, I haven't been able to figure this out and it's been over 2 months of me having this problem. Below is my source code.
Here is 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);
}
And here is 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);
}
}
Finally, here's 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;
}
Here are the vertices / coordinates of my octahedron
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
};
Here is my vbo and vao, and also where I upload some data to the GPU
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);
// amount of tessellation to do per triangle
glPatchParameteri(GL_PATCH_VERTICES, 3);
Last but not least, my OpenGL draw call
glBindVertexArray(vao);
glDrawArrays(GL_PATCHES, 0, 24);
If you want the full source code, here's the link you could use to download it (the libraries needed are CGLM, GLFW, and GLEW, and the IDE used is Code::Blocks)