Search code examples
c++openglscatter3d

opengl strange behavior when using two shaders


I have two shaders, one for instancing, one for just plotting a line. They correspond to two segments of code (segment1 for instancing and segment2 for a line) respectively. And the programing behaviour is weird:

  1. When I put segment2 behind segment1, then the segment1 work just dispeared. Then I comment the code of segment2, segment1 works. Then I uncomment segment2 code and put it before segment1. Then both segments works. This is strange.
  2. So I just put segment2 before segment1 to let it work. But what I want is that when pressing some keys, the view will move. But here is the thing:
a. when both segments uncommented, the instancing will move as expected, but the line just stays at where it borns.
b. then I comment the segment2 code, then the line will move as expected when keys pressed.

There must be some connections of the two problems. Here is some of the code, it's not runnable, I will provide full runnable code if necessary.

#include <stdio.h>
#include <stdlib.h>
#include <glad/glad.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/norm.hpp>
#include <thread>
#include <queue>
#include <mutex>
#include <glm/gtx/string_cast.hpp>
#include "../common/shader.hpp"

GLFWwindow *window;
using namespace glm;
using std::cout, std::endl;


int run_websocket(); // another thread run this function to get data into the queue
extern std::queue<std::tuple<unsigned long, float, float, bool>> queue1;
extern std::mutex m;


const int MaxParticles = 100000;
const int MaxLine = 1000;
int index = 0;
int width = 1024;
int height = 768;
float deltaTime = 0.0f;
float lastFrame = 0.0f;


void framebuffer_size_callback(GLFWwindow *window, int width1, int height1) {
    width = width1;
    height = height1;
    glViewport(0, 0, width1, height1);
}

void processInput(GLFWwindow *window) {
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

    float f1 = 10000;
    float f_up_down = 10;
    float cameraSpeed = deltaTime;
    if (glfwGetKey(window, GLFW_KEY_N) == GLFW_PRESS)
        cameraPos += cameraSpeed * (cameraFront * f_up_down);
    if (glfwGetKey(window, GLFW_KEY_M) == GLFW_PRESS)
        cameraPos -= cameraSpeed * (cameraFront * f_up_down);
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * (cameraSpeed * f1);
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * (cameraSpeed * f1);
    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
        cameraPos += glm::normalize(cameraUp) * (cameraSpeed * f_up_down);
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        cameraPos -= glm::normalize(cameraUp) * (cameraSpeed * f_up_down);
}


glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 10.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);

unsigned long x0 = 0;
float y00 = 0.0f;

int main(void) {
    // Initialise GLFW
    std::thread t1(run_websocket);
    if (!glfwInit()) {
        fprintf(stderr, "Failed to initialize GLFW\n");
        getchar();
        return -1;
    }

    glfwWindowHint(GLFW_SAMPLES, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    // Open a window and create its OpenGL context
    window = glfwCreateWindow(width, height, "title", NULL, NULL);
    if (window == NULL) {
        fprintf(stderr,
                "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n");
        getchar();
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);


    // glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
    glfwPollEvents();
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    GLuint programID = LoadShaders("Particle.vertexshader",
                                   "Particle.fragmentshader");
    GLuint shader2 = LoadShaders("line.vertexshader",
                                 "line.fragmentshader");

    GLuint ViewProjMatrixID = glGetUniformLocation(programID, "VP");
    GLuint ViewProjMatrixID2 = glGetUniformLocation(shader2, "VP2");
    static GLfloat *g_particule_position_size_data = new GLfloat[MaxParticles * 4];
    static GLfloat *g_line_data = new GLfloat[MaxLine * 4];
    static const GLfloat g_vertex_buffer_data[] = {
            -1.0f, -1.0f,
            1.0f, -1.0f,
            -1.0f, 1.0f,
            1.0f, -1.0f,
            -1.0f, 1.0f,
            1.0f, 1.0f,
    };
    // vao of segment2
    GLuint LineVertexArrayID;
    glGenVertexArrays(1, &LineVertexArrayID);
    glBindVertexArray(LineVertexArrayID);
    GLuint line_buffer;
    glGenBuffers(1, &line_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, line_buffer);
    glBufferData(GL_ARRAY_BUFFER, MaxLine * 4 * sizeof(float), NULL, GL_STREAM_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(
            0,
            2,
            GL_FLOAT,
            GL_FALSE,
            2 * sizeof(float),
            (void *) 0
    );
    glBindVertexArray(0);
    // vao of segment2 ends

    // vao of segment1 
    GLuint VertexArrayID;
    glGenVertexArrays(1, &VertexArrayID);
    glBindVertexArray(VertexArrayID);
    GLuint billboard_vertex_buffer;
    glGenBuffers(1, &billboard_vertex_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, billboard_vertex_buffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(
            0,
            2,
            GL_FLOAT,
            GL_FALSE,
            2 * sizeof(float),
            (void *) 0
    );
    GLuint particles_position_buffer;
    glGenBuffers(1, &particles_position_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, particles_position_buffer);
    glBufferData(GL_ARRAY_BUFFER, MaxParticles * 4 * sizeof(GLfloat), NULL, GL_STREAM_DRAW);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(
            1,
            4,
            GL_FLOAT,
            GL_FALSE,
            4 * sizeof(float),
            (void *) 0
    );
    glVertexAttribDivisor(1, 1);
    glBindVertexArray(0);
    // vao of segment1 ends


    do {
        glClear(GL_COLOR_BUFFER_BIT);
        float currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;
        processInput(window);

        glm::mat4 projection = glm::mat4(1.0f);
        glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
        projection = glm::perspective(glm::radians(90.0f), 1000.0f, 0.1f, 1000.0f);
        glm::mat4 ViewProjectionMatrix = projection * view;
        glm::mat4 ViewProjectionMatrix2 = projection * view;
        glUniformMatrix4fv(ViewProjMatrixID, 1, GL_FALSE, &ViewProjectionMatrix[0][0]);
        glUniformMatrix4fv(ViewProjMatrixID2, 1, GL_FALSE, &ViewProjectionMatrix2[0][0]);

        // why move segment2 behind segment1 then segment1 will not show ???
        ///// segment2
        int lineNum = 1;
        int line_index = 0;
        glUseProgram(shader2);
        glBindVertexArray(LineVertexArrayID);
        glBindBuffer(GL_ARRAY_BUFFER, line_buffer);
        g_line_data[line_index * 2 + 0] = -1.0f;
        g_line_data[line_index * 2 + 1] = 0;
        line_index = 1;
        g_line_data[line_index * 2 + 0] = 1.0f;
        g_line_data[line_index * 2 + 1] = 0;
        glBufferData(GL_ARRAY_BUFFER, MaxLine * 4 * sizeof(GLfloat), NULL, GL_STREAM_DRAW);
        glBufferSubData(GL_ARRAY_BUFFER, 0, lineNum * sizeof(GLfloat) * 4, g_line_data);
        glDrawArrays(GL_LINES, 0, 2);
        glBindVertexArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        ///// segment2
        
        // printing some here to make sure the Math is correct
//        vec4 p1 = ViewProjectionMatrix * vec4(-1.0f, 0, -0.1, 1.0);
//        vec4 p2 = ViewProjectionMatrix * vec4(1.0f, 0, -0.1, 1.0);
//        cout << "p1 y " << p1.y / p1.w << " , p2 y " << p2.y / p2.w << endl;

        //// segment1
        {
            std::lock_guard<std::mutex> lck(m);
            while (!queue1.empty()) {
                auto &t = queue1.front();
                unsigned long x_raw = std::get<0>(t);
                float y = std::get<1>(t);
                if (x0 == 0) {
                    x0 = x_raw;
                    y00 = y;
                    cameraPos += glm::vec3(0, 0, 0);
                }
                x_raw -= x0;
                y -= y00;
                float x = float(x_raw);
                float s = std::get<2>(t);
                bool is_sell_b = std::get<3>(t);
                float is_buy = 1.0f - static_cast<float>(is_sell_b);
                g_particule_position_size_data[index * 4 + 0] = x;
                g_particule_position_size_data[index * 4 + 1] = y;
                g_particule_position_size_data[index * 4 + 2] = s / 100;
                g_particule_position_size_data[index * 4 + 3] = is_buy;
                index++;
                queue1.pop();
            }
        }
        glUseProgram(programID);
        glBindVertexArray(VertexArrayID);
        glBindBuffer(GL_ARRAY_BUFFER, particles_position_buffer);
//        glBufferData(GL_ARRAY_BUFFER, MaxParticles * 4 * sizeof(GLfloat), NULL, GL_STREAM_DRAW); // Buffer orphaning, a common way to improve streaming perf. See above link for details.
        glBufferSubData(GL_ARRAY_BUFFER, 0, index * sizeof(GLfloat) * 4, g_particule_position_size_data);
        glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 6, index);
        glBindVertexArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        //// segment1

        glfwSwapBuffers(window);
        glfwPollEvents();

    } // Check if the ESC key was pressed or the window was closed
    while (glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS &&
           glfwWindowShouldClose(window) == 0);


    delete[] g_particule_position_size_data;

    // Cleanup VBO and shader
    glDeleteBuffers(1, &particles_position_buffer);
    glDeleteBuffers(1, &billboard_vertex_buffer);
    glDeleteProgram(programID);
//    glDeleteTextures(1, &Texture);
    glDeleteVertexArrays(1, &VertexArrayID);
    // Close OpenGL window and terminate GLFW
    glfwTerminate();
    return 0;
}


Solution

  • glUniform* sets a uniform variable in the default uniform block of the currently installed program.

    Install the program with glUseProgram before setting the uniform:

    glUseProgram(programID);
    glUniformMatrix4fv(ViewProjMatrixID, 1, GL_FALSE, &ViewProjectionMatrix[0][0]);
    glUseProgram(shader2);
    glUniformMatrix4fv(ViewProjMatrixID2, 1, GL_FALSE, &ViewProjectionMatrix2[0][0]);
    

    Or use glProgramUniformMatrix4fv:

    glProgramUniformMatrix4fv(programID, ViewProjMatrixID, 1, GL_FALSE, &ViewProjectionMatrix[0][0]);
    glProgramUniformMatrix4fv(shader2, ViewProjMatrixID2, 1, GL_FALSE, &ViewProjectionMatrix2[0][0]);