如何在运行时重置 GLFW 片段/加载不同的着色器?
How do I reset the GLFW Fragment / Load a different shader during runtime?
我正在为桌面编写类似 shadertoy 的工具。它使用 GLFW (OpenGL)、Glad 和 ImGui 来渲染图像。
我现在已经解决了所有以前的问题,但我现在陷入了过去几天一直困扰我的一个特定问题。
我希望能够热重载着色器。现在它需要一个简单的 mandelbulb raymarcher 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
包含我的其余 GLFW 代码的实际 Main.cpp 文件:
于是折腾了一阵子,还真的想通了。
原来我做的比要求的多得多。
我需要做的就是在开始时分离片段,然后删除所有内容,然后 re-attach 一个新片段,再次 link 程序,然后再次分离并删除它。
现在我可以在运行时重新加载我的着色器。
我在初始化时使用的代码:
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);
它所做的只是抓住碎片+顶点,附加它们,缓存/link程序到渲染器,然后分离碎片,删除顶点+碎片以节省内存,它准备好了随时被覆盖。
这是我在重新加载按钮中使用的代码:
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);
}
它所做的是抓取当前着色器程序,没有片段,并且已经存在的顶点仍然缓存在它上面。
然后它附加新的片段,links / 再次将程序缓存到渲染器,然后从初始的开始重复该过程。它分离片段,将其删除以节省内存,然后准备好再次覆盖它。
你也可以将它与顶点着色器一起使用,只需分离并删除它,与片段相同。我决定不这样做,因为我的程序的目的是将 2D 着色器渲染到平面上,类似于 shadertoy
要为当前程序使用着色器,您只需执行此操作。
glUseProgram(shaderID);
我正在为桌面编写类似 shadertoy 的工具。它使用 GLFW (OpenGL)、Glad 和 ImGui 来渲染图像。
我现在已经解决了所有以前的问题,但我现在陷入了过去几天一直困扰我的一个特定问题。
我希望能够热重载着色器。现在它需要一个简单的 mandelbulb raymarcher 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
包含我的其余 GLFW 代码的实际 Main.cpp 文件:
于是折腾了一阵子,还真的想通了。
原来我做的比要求的多得多。
我需要做的就是在开始时分离片段,然后删除所有内容,然后 re-attach 一个新片段,再次 link 程序,然后再次分离并删除它。
现在我可以在运行时重新加载我的着色器。
我在初始化时使用的代码:
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);
它所做的只是抓住碎片+顶点,附加它们,缓存/link程序到渲染器,然后分离碎片,删除顶点+碎片以节省内存,它准备好了随时被覆盖。
这是我在重新加载按钮中使用的代码:
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);
}
它所做的是抓取当前着色器程序,没有片段,并且已经存在的顶点仍然缓存在它上面。
然后它附加新的片段,links / 再次将程序缓存到渲染器,然后从初始的开始重复该过程。它分离片段,将其删除以节省内存,然后准备好再次覆盖它。
你也可以将它与顶点着色器一起使用,只需分离并删除它,与片段相同。我决定不这样做,因为我的程序的目的是将 2D 着色器渲染到平面上,类似于 shadertoy
要为当前程序使用着色器,您只需执行此操作。
glUseProgram(shaderID);