0

I have a UBO initialized like this:

glGenBuffers(1, &sphereUBO);
glBindBuffer(GL_UNIFORM_BUFFER, sphereUBO);
glBufferData(GL_UNIFORM_BUFFER, sizeof(SphereUBO) * sphereData.size(), nullptr, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, sphereUBO);

glUniformBlockBinding(rtShader.ID, glGetUniformBlockIndex(shaderProgram, "Spheres"), 0);

rtShader.setInt("u_numSpheres", sphereData.size());

Above that, I initialize my sphere data like this:

Scene scene = Scene();
scene.addSphere(glm::vec3(-1.0, 1.0, -1.0), 1.0);
scene.addSphere(glm::vec3(0.0, 1.0, 0.0), 0.1);
scene.addSphere(glm::vec3(5.0, 0.5, 0.0), 0.5);
scene.addSphere(glm::vec3(5.0, 3.5, 0.0), 0.75);

std::vector<SphereUBO> sphereData;
for (int i = 0; i < scene.spheres.size(); i++) {
    SphereUBO sphere;
    sphere.position = scene.spheres[i].position;
    sphere.radius = scene.spheres[i].radius;
    sphere.albedo = glm::vec3(scene.spheres[i].material.albedo[0], scene.spheres[i].material.albedo[1], scene.spheres[i].material.albedo[2]);
    sphereData.push_back(sphere);
}

I have printed out sphereData to ensure the data is passed along properly. Finally, I update the contents of the buffer:

glBindBuffer(GL_UNIFORM_BUFFER, sphereUBO);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(SphereUBO) * sphereData.size(), sphereData.data());

However, I only see one sphere rendered! I have confirmed that all of the spheres are rendering, but they are somehow all the same. I have this function in my fragment shader:

bool raycast(Ray ray, out HitPoint hitPoint) {
    bool hit = false;
    float minHitDistance = 10000;
    float hitDistance;

    for (int i = 0; i < u_numSpheres; i++) {
        if (sphereIntersection(u_spheres[i].position, u_spheres[i].radius, ray, hitDistance)) {
            hit = true;

            if (hitDistance < minHitDistance) {
                minHitDistance = hitDistance;
                hitPoint.position = ray.origin + ray.direction * minHitDistance;
                hitPoint.normal = normalize(hitPoint.position - u_spheres[i].position);
                hitPoint.material = Material(u_spheres[i].albedo);
            }
        }
    }

return hit;

}

I know they are all the same because when I don't do a for loop and I just check for the first or second sphere, they render the same.

I have my buffer/uniforms declared up here:

uniform int u_numSpheres;

layout(std140) uniform Sphere{
    vec3 position;
    float radius;
    vec3 albedo;
} u_spheres[32];
genpfault
  • 51,148
  • 11
  • 85
  • 139
gkgkgkgk
  • 707
  • 2
  • 7
  • 26

1 Answers1

3
glGetUniformBlockIndex(shaderProgram, "Spheres")

First, the typo: "Spheres" is not a valid uniform block name, so this almost certainly returned GL_INVALID_INDEX, which would cause the call to glUniformBlockBinding to fail.

But more importantly, even if you named it "Sphere" to match your UBO definition... there still is no uniform block named "Sphere". However, there is a uniform block named Sphere[0]. And one named Sphere[1]. And so forth.

See, the problem is that you think Sphere is single uniform block. It is not. It is an array of uniform blocks, not a uniform block containing an array. That's what it means when you declare an interface block as an array. You have 32 uniform blocks (which really should have caused a compile error all on its own).

What you really want is to declare a struct called Sphere and then have a uniform block containing an array of such types. Also, stop using vec3.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982