使用两个着色器时的opengl奇怪行为

Posted

技术标签:

【中文标题】使用两个着色器时的opengl奇怪行为【英文标题】:opengl strange behavior when using two shaders 【发布时间】:2021-06-19 17:21:07 【问题描述】:

我有两个着色器,一个用于实例化,一个用于绘制一条线。它们分别对应于两段代码(segment1 用于实例化,segment2 用于一行)。而且编程行为很奇怪:

    当我将segment2 放在segment1 后面时,segment1 work 就消失了。然后我注释段2的代码,段1有效。然后我取消注释段 2 代码并将其放在段 1 之前。然后这两个部分都有效。这很奇怪。 所以我只是把segment2 放在segment1 之前让它工作。但我想要的是当按下某些键时,视图会移动。但事情是这样的:
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.

这两个问题一定有一些联系。这是部分代码,无法运行,如有需要我会提供完整的可运行代码。

#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;


【问题讨论】:

【参考方案1】:

glUniform*在当前安装程序的默认uniform块中设置一个uniform变量。

在设置制服前用glUseProgram安装程序:

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

或者使用glProgramUniformMatrix4fv:

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

【讨论】:

以上是关于使用两个着色器时的opengl奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章

为啥在使用 OpenGL 编译顶点着色器时会出现着色器编译器错误 #143、#160 和 #216?

将属性传递给 OpenGL 顶点着色器的行为很奇怪

OpenGL着色器不绘制[关闭]

OpenGL gluLookat 不适用于着色器

OpenGL:为啥我不能将单个浮点数从顶点着色器传递到片段着色器?

传递给片段着色器的纹理坐标全部为 0