opengl 如何在运行时重置GLFW片段/加载不同的着色器?

smtd7mpg  于 2022-11-04  发布在  其他
关注(0)|答案(2)|浏览(290)

我正在为桌面编写一个类似着色器的工具。它使用GLFW(OpenGL)、Glad和ImGui来渲染图像。
我现在已经到了一个点,我修复了我以前所有的问题,但我现在坚持在一个特定的问题,一直困扰着我过去几天。
我希望能够热重新加载着色器。现在,它采取了一个简单的mandelbulb光线行进GLSL片段,绘制它,也绘制了一个小的ImGui面板与“重新加载着色器”按钮。
我一直在想如何取消当前着色器程序的链接,并重新加载一个新的着色器,但我一无所获。代码没有任何错误,但它肯定不工作。当我点击按钮时,OpenGL视口就变黑了。
我试过在网上寻找,但基本上没有坚实的解决方案,我已经看到的大多数问题从来没有真正得到回答。
这是我当前重置着色器的代码(并再次从文件中获取它)

if (ImGui::Button("Reload Shader", ImVec2(0, 0))) {
            glDeleteVertexArrays(1, &VAO);
            glDeleteBuffers(1, &VBO);
            glDeleteProgram(shaderProgram);
            gladLoadGL();
            glViewport(0, 0, width, height);

            vertexShader = glCreateShader(GL_VERTEX_SHADER);
            fragmentShader = loadShaderFromFile("assets\\round.glsl", GL_FRAGMENT_SHADER);

            shaderProgram = glCreateProgram();
            glAttachShader(shaderProgram, vertexShader);
            glAttachShader(shaderProgram, fragmentShader);
            glLinkProgram(shaderProgram);
            glDeleteShader(vertexShader);
            glDeleteShader(fragmentShader);

            GLuint VAO, VBO;
            glGenVertexArrays(1, &VAO);
            glGenBuffers(1, &VBO);
            glBindVertexArray(VAO);
            glBindBuffer(GL_ARRAY_BUFFER, VBO);
            glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
            glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
            glEnableVertexAttribArray(0);
            glBindBuffer(GL_ARRAY_BUFFER, 0);
            glBindVertexArray(0);

            glUseProgram(shaderProgram);
            GLint resUniform = glGetUniformLocation(shaderProgram, "iResolution");
            glUniform3f(resUniform, width, height, width / height);
            double mxpos, mypos;
            glfwGetCursorPos(window, &mxpos, &mypos);
            GLint mouseUniform = glGetUniformLocation(shaderProgram, "iMouse");
            glUniform4f(mouseUniform, mxpos, mypos, mxpos, mypos);
            GLint timeUniform = glGetUniformLocation(shaderProgram, "iTime");
            glUniform1f(timeUniform, currentTime);
            glBindVertexArray(VAO);
            glDrawArrays(GL_TRIANGLES, 0, 3);

            glfwSwapBuffers(window);
            glfwPollEvents();
        }

这是当前github提交的所有代码/整个程序,如果需要的话:
https://github.com/ArctanStudios/OpenGL-Sandbox/tree/e7ba32310b898d1a60eae89f8969179600f5d391
实际的Main.cpp文件以及我的GLFW代码的其余部分:
https://github.com/ArctanStudios/OpenGL-Sandbox/blob/e7ba32310b898d1a60eae89f8969179600f5d391/Main.cpp

j9per5c4

j9per5c41#

所以在摆弄了一会儿之后,我真的弄明白了。
结果发现我做的比要求的要多得多。
我所需要做的就是在开始时分离片段,然后删除所有内容,然后重新附加一个新的片段,再次链接程序,然后再次分离并删除它。
现在,我可以在运行时重新加载着色器。
初始化期间使用的代码I:

GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);

    GLuint fragmentShader = loadShaderFromFile("assets\\round.glsl", GL_FRAGMENT_SHADER);

    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    glDetachShader(shaderProgram, fragmentShader);
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

它所做的只是抓取frag + vert,将它们都附加,将程序缓存/链接到渲染器,然后分离frag,删除vertex + frag以保存内存,并准备随时覆盖。
下面是我在reload按钮中使用的代码:

if (ImGui::Button("Reload Shader", ImVec2(0, 0))) {
            glDetachShader(shaderProgram, fragmentShader);
            fragmentShader = loadShaderFromFile("assets\\round.glsl", GL_FRAGMENT_SHADER);
            glAttachShader(shaderProgram, fragmentShader);
            glLinkProgram(shaderProgram);
            glDetachShader(shaderProgram, fragmentShader);
            glDeleteShader(fragmentShader);
        }

这样做的目的是获取当前的着色器程序,没有片段,并且已经存在的顶点仍然缓存在其中。
然后它附加新的片段,再次链接/缓存程序到渲染器,然后从初始的一个重复该过程。它分离片段,删除它以保存内存,然后它准备再次被覆盖。

你也可以使用顶点着色器,只是分离和删除它,就像片段一样。我决定不这样做,因为我的程序的目的是渲染2D着色器到一个平面上,类似于shadertoy

7fyelxc5

7fyelxc52#

要为当前程序使用着色器,只需执行以下操作。

glUseProgram(shaderID);

相关问题