Why are there gaps in my fibonacci sphere?

Started by
13 comments, last by kwikc 11 months ago

I recently took an interest in sphere topologies to find which one is the best. The most popular one seems to be the fibonacci sphere, so I found some code from here https://stackoverflow.com/questions/9600801/evenly-distributing-n-points-on-a-sphere/26127012#26127012

and then ran it through an AI to convert the code to C and then do some optimizations

#include <math.h>
#include <stdio.h>

#define MAX_POINTS 1000

void fibonacci_sphere(int samples, float points[][3]) {
    /*
    Generates points on a sphere using Fibonacci spiral sampling.

    :param samples: Number of points to generate.
    :type samples: int
    :param points: Array to store generated points.
    :type points: list of lists
    :raises TypeError: If samples is not an integer or points is not a list of lists.
    :return: None
    */

    // Validate inputs
    if (samples <= 0) {
        printf("Number of samples must be a positive integer.\n");
        return;
    }
    if (points == NULL) {
        printf("Points array cannot be NULL.\n");
        return;
    }

    // Calculate golden angle in radians
    float phi = M_PI * (sqrtf(5.0) + 1.0);

    // Generate points on sphere
    for (int i = 0; i < samples; i++) {
        // Calculate y coordinate
        float y = 1.0 - ((float)i / (float)(samples - 1)) * 2.0;

        // Calculate radius at y
        float radius = sqrtf(1.0 - y * y);

        // Calculate golden angle increment
        float theta = phi * i;

        // Calculate x, y, and z coordinates of point
        float x = cosf(theta) * radius;
        float z = sinf(theta) * radius;

        // Store point in points array
        points[i][0] = x;
        points[i][1] = y;
        points[i][2] = z;
    }

    // Log number of points generated
    printf("%d points generated on sphere.\n", samples);
}

which then outputs this

Unnormalized vertices
Unnormalized vertices (wireframe)

And as you can see, these are unorganized right now, but when I normalize the vertices, I get this

Normalized vertices
Unnormalized vertices (wireframe)

It is a sphere, but it's a little uneven, and plus there is a major gap down the middle, with a bunch of triangles stacking on top of each other unnecessarily.

Here's the rest of my relevant code, starting with the application of the Fibonacci function, and then my tessellation shaders

float vertices[100][3];
fibonacci_sphere(100, vertices);

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);
glBindVertexArray(vao);
glDrawArrays(GL_PATCHES, 0, 100);
#version 450 core
// tessellation control
// 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);
    }
}

// tessellation evaluation
#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);
}

None

Advertisement

Chillzy said:
It is a sphere, but it's a little uneven, and plus there is a major gap down the middle, with a bunch of triangles stacking on top of each other unnecessarily.

you have a set of points on a sphere, but where is the set of edges connecting them together? where is the index buffer?

take this cube, for example. it has 8 vertices and 12 triangles:

#   5----6
#  /.   /|
# 4----7 |
# | .  | |
# | .  | |
# | 1..|.2
# |.   |/ 
# 0----3  

each triangle is described as some triplet of those 8 vertices: (0, 1, 2) and (0, 2, 3) define the bottom face, (0, 1, 5) and (0, 5, 4) define the left face, and etc...
without an index buffer (and depending on the current topology mode), the gpu might try to draw a triangle at (0, 1, 2) and (3,4,5), which cuts through the cube.

it seems the best approach to this problem is to find the delaunay triangulation of the vertices. there are some additional resources in this reddit thread

You need to do a triangulation/convex hull of those points to get anything that you can render as a triangle mesh. You only have the vertices, not the topology.

@Aressera Ok gonna look into that now, I'll update once I'm finished

None

Alright, so I looked into Delaunay triangulation and I found an algorithm called the Bowyer-Watson algorithm https://en.wikipedia.org/wiki/Bowyer–Watson_algorithm​ which creates Delaunay triangulations iteratively that I then implemented into my code base and while it does triangulate like I wanted it to, I still need to figure out how to properly display the data because so far none of the OpenGL draw types seem to be displaying the sphere the way I want it to so that part might take a little while but at least the points are triangulated.

None

What you need is the convex hull, not the Delaunay triangulation.

I recently took an interest in sphere topologies to find which one is the best.

In the game development realm, it must take it into consideration that how texture is applied, UV mapping. As its name, UV sphere might be the best for that purpose. https://en.wikipedia.org/?title=UV_sphere&redirect=no.

None

kwikc said:

I recently took an interest in sphere topologies to find which one is the best.

In the game development realm, it must take it into consideration that how texture is applied, UV mapping. As its name, UV sphere might be the best for that purpose. https://en.wikipedia.org/?title=UV_sphere&redirect=no.

Er, no. A UV sphere gives you a slight edge when using a cylindrical projection, but:

  1. The edge really is slight, and any other sphere topology can easily be adapted to accept cylindrical textures by adding a seam.
  2. This edge in no way makes up for the awkward vertex density distribution of a UV sphere.
  3. You shouldn't be using cylindrical projections anyway.

…and that's assuming the sphere in question will be textured at all, which is not a given.

If texturing is not involved, my that reply is off topic.

None

I'm confused by the expression “sphere topologies”. It must mean something because other people on the Internet use the expression, but in Topology, all of those constructions are the same ("homeomorphic", to be precise).

To the OP: Did you figure out the problem? If you just render the vertices as a collection of dots, does it look right?

This topic is closed to new replies.

Advertisement