#c #opengl #glsl #glfw #compute-shader
Вопрос:
Как я это понимаю (поправьте меня, если я ошибаюсь) Я могу обмениваться данными между вычислительным шейдером и вершинным шейдером, привязывая их к одному и тому же буферу.
Программа vertex способна отображать мои частицы, которые нарисованы в виде точек, но они не обновляются вычислительным шейдером. Я предполагаю, что просто упустил что-то очевидное, но я ни за что на свете не могу этого увидеть.
Шейдер фрагментов:
#version 430 core
// The color of the line
uniform vec4 u_color;
out vec4 FragColor;
void main()
{
FragColor = u_color;
}
Вершинный шейдер:
#version 430 core
layout (location = 0) in vec2 aPos;
// TODO: Refactor into using a UBO
uniform mat4 model;
void main()
{
gl_Position = model * vec4(aPos, 0.0, 1.0);
}
Вычислительный шейдер:
#version 430 core
struct Particle{
vec2 pos;
};
layout(std430, binding = 1) buffer particleBuffer
{
Particle particles[];
};
layout(local_size_x = 1024, local_size_y = 1, local_size_z = 1) in;
void main()
{
uint i = gl_GlobalInvocationID.x;
particles[i].pos = vec2(0.01, 0.0); //particles[i].pos vec2(0.01, 0.0);
}
Главная:
// Your First C Program
#include <iostream>
#include <cmath>
#include "../include/glad/glad.h"
#include <GLFW/glfw3.h>
#include <stb_image.h>
#include "Shader.hpp"
#include "camera.h"
#include "Arrow.hpp"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/string_cast.hpp>
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 800;
int main()
{
// glfw: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_SAMPLES, 4);
// glEnable(GL_MULTISAMPLE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
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;
}
// configure global opengl state
// -----------------------------
glEnable(GL_DEPTH_TEST);
glEnable(GL_MULTISAMPLE); // enabled by default on some drivers, but not all so always enable to make sure
// build and compile our shader zprogram
// ------------------------------------
Shader particleShader("../shaders/particle.vert", "../shaders/particle.frag", "../shaders/particle.glsl");
particleShader.use();
glm::mat4 model = glm::mat4(1.0f);
particleShader.setMat4("model", model);
int numberOfParticles = 1024;
float particles[numberOfParticles* 2];
glPointSize(2.0f);
for(int i = 0; i < numberOfParticles * 2; i ){
// TODO: Use propeor good randomness instead...
particles[i] = static_cast <float> ( 2 * rand()) / static_cast <float> (RAND_MAX);
}
unsigned int PARTICLE_VAO, PARTICLE_VBO;
glGenVertexArrays(1, amp;PARTICLE_VAO);
glGenBuffers(1, amp;PARTICLE_VBO);
glBindVertexArray(PARTICLE_VAO);
glBindBuffer(GL_ARRAY_BUFFER, PARTICLE_VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(particles), particles, GL_DYNAMIC_DRAW);
// position attribute
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, PARTICLE_VBO);
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
// -----
processInput(window);
// render
// ------
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// activate shader
particleShader.use();
glBindVertexArray(PARTICLE_VAO);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, PARTICLE_VBO);
glDispatchCompute(numberOfParticles / 1024, 1, 1);
glMemoryBarrier(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT);
particleShader.setVec4f("u_color", 1.0f, 1.0f, 1.0f, 1.0f);
glDrawArrays(GL_POINTS, 0, numberOfParticles);
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
// optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
glDeleteVertexArrays(1, amp;PARTICLE_VAO);
glDeleteBuffers(1, amp;PARTICLE_VBO);
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return 0;
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
}
#ifndef SHADER_H
#define SHADER_H
#include "../include/glad/glad.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
class Shader
{
public:
unsigned int ID;
// constructor generates the shader on the fly
// ------------------------------------------------------------------------
Shader(const char* vertexPath, const char* fragmentPath, const char* computePath = "")
{
// 1. retrieve the vertex/fragment source code from filePath
std::string vertexCode = loadSourceCode(vertexPath);
unsigned int vertex = compileShaderCode("VERTEX", vertexCode);
std::string fragmentCode = loadSourceCode(fragmentPath);
unsigned int fragment = compileShaderCode("FRAGMENT", fragmentCode);
unsigned int compute;
// shader Program
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
if(computePath != ""){
std::string computeCode = loadSourceCode(computePath);
compute = compileShaderCode("COMPUTE", computeCode);
}
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
// delete the shaders as they're linked into our program now and no longer necessary
glDeleteShader(vertex);
glDeleteShader(fragment);
if(computePath != ""){
glDeleteShader(compute);
}
}
// activate the shader
// ------------------------------------------------------------------------
void use()
{
glUseProgram(ID);
}
// utility uniform functions
// ------------------------------------------------------------------------
void setBool(const std::string amp;name, bool value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
// ------------------------------------------------------------------------
void setInt(const std::string amp;name, int value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}
// ------------------------------------------------------------------------
void setFloat(const std::string amp;name, float value) const
{
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}
// ------------------------------------------------------------------------
void setVec2f(const std::string amp;name, float value1, float value2) const
{
glUniform2f(glGetUniformLocation(ID, name.c_str()), value1, value2);
}
// ------------------------------------------------------------------------
void setVec3f(const std::string amp;name, float value1, float value2, float value3) const
{
glUniform3f(glGetUniformLocation(ID, name.c_str()), value1, value2, value3);
}
// ------------------------------------------------------------------------
void setVec4f(const std::string amp;name, float value1, float value2, float value3, float value4) const
{
glUniform4f(glGetUniformLocation(ID, name.c_str()), value1, value2, value3, value4);
}
// ------------------------------------------------------------------------
void setMat4(const std::string amp;name, glm::mat4 value) const
{
glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, glm::value_ptr(value));
}
private:
unsigned int compileShaderCode(std::string type, std::string sourceCode){
const char* shaderCode = sourceCode.c_str();
unsigned int shader;
if (type == "VERTEX"){
shader = glCreateShader(GL_VERTEX_SHADER);
} else if (type == "FRAGMENT"){
shader = glCreateShader(GL_FRAGMENT_SHADER);
} else if (type == "COMPUTE"){
shader = glCreateShader(GL_COMPUTE_SHADER);
} else {
std::cout << "ERROR::UNKNOWN_SHADER_TYPE '" << type << "'" << std::endl;
}
glShaderSource(shader, 1, amp;shaderCode, NULL);
glCompileShader(shader);
checkCompileErrors(shader, type);
return shader;
}
std::string loadSourceCode(const char* shaderPath) {
std::string shaderCode;
std::ifstream shaderFile;
// ensure ifstream objects can throw exceptions:
shaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
try
{
// open files
shaderFile.open(shaderPath);
std::stringstream shaderStream;
// read file's buffer contents into streams
shaderStream << shaderFile.rdbuf();
// close file handlers
shaderFile.close();
// convert stream into string
return shaderStream.str();
}
catch (std::ifstream::failureamp; e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ '" << shaderPath <<"'" << std::endl;
return "";
}
}
// utility function for checking shader compilation/linking errors.
// ------------------------------------------------------------------------
void checkCompileErrors(unsigned int shader, std::string type)
{
int success;
char infoLog[1024];
if (type != "PROGRAM")
{
glGetShaderiv(shader, GL_COMPILE_STATUS, amp;success);
if (!success)
{
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "n" << infoLog << "n -- --------------------------------------------------- -- " << std::endl;
}
}
else
{
glGetProgramiv(shader, GL_LINK_STATUS, amp;success);
if (!success)
{
glGetProgramInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "n" << infoLog << "n -- --------------------------------------------------- -- " << std::endl;
}
}
}
};
#endif
Ответ №1:
См. Спецификацию основного профиля API OpenGL 4.6 — 7.3 Программные объекты:
[…]
Связывание может завершиться неудачей по различным причинам […], а также по любой из следующих причин:
[…]
- программа содержит объекты для формирования вычислительного шейдера (см. раздел 19), а также содержит объекты для формирования любого другого типа шейдера.
Вам нужно создать 2 отдельные программы шейдеров. Один для примитивного рендеринга (вершинные и фрагментные шейдеры) и один для вычисления вершин (вычислительный шейдер):
class Shader
{
public:
unsigned int ID;
Shader(const char* vertexPath, const char* fragmentPath)
{
// 1. retrieve the vertex/fragment source code from filePath
std::string vertexCode = loadSourceCode(vertexPath);
unsigned int vertex = compileShaderCode("VERTEX", vertexCode);
std::string fragmentCode = loadSourceCode(fragmentPath);
unsigned int fragment = compileShaderCode("FRAGMENT", fragmentCode);
// shader Program
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
glDeleteShader(vertex);
glDeleteShader(fragment);
}
Shader(const char* computePath)
{
std::string computeCode = loadSourceCode(computePath);
unsigned int compute; compute = compileShaderCode("COMPUTE", computeCode);
// shader Program
ID = glCreateProgram();
glAttachShader(ID, compute);
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
glDeleteShader(compute);
}
// [...]
Shader particleComputeShader("../shaders/particle.glsl");
Shader particleShader("../shaders/particle.vert", "../shaders/particle.frag");
Переключите программу шейдера после вычисления вершин:
particleComputeShader.use();
glBindVertexArray(PARTICLE_VAO);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, PARTICLE_VBO);
glDispatchCompute(numberOfParticles / 1024, 1, 1);
glMemoryBarrier(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT);
particleShader.use();
particleShader.setVec4f("u_color", 1.0f, 1.0f, 1.0f, 1.0f);
glDrawArrays(GL_POINTS, 0, numberOfParticles);