working framebuffers, textures and feedback
This commit is contained in:
+141
-87
@@ -32,6 +32,130 @@ bool compile_shader(GLuint shader_id, char *name, char *source_code) {
|
|||||||
return status_params == GL_TRUE;
|
return status_params == GL_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool init_textures(ShaderProgram *program, Context context) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
glGenTextures(BUFFER_COUNT, program->textures);
|
||||||
|
|
||||||
|
for (i = 0; i < BUFFER_COUNT; i++) {
|
||||||
|
// selects which texture unit subsequent texture state calls will affect
|
||||||
|
glActiveTexture(GL_TEXTURE0 + i);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, program->textures[i]);
|
||||||
|
|
||||||
|
// define texture image as empty
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, context.width, context.height, 0,
|
||||||
|
GL_RGB, GL_UNSIGNED_BYTE, 0);
|
||||||
|
|
||||||
|
// setup mipmap context
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_framebuffers(ShaderProgram *program) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
glGenFramebuffers(BUFFER_COUNT, program->frame_buffers);
|
||||||
|
|
||||||
|
for (i = 0; i < BUFFER_COUNT; i++) {
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, program->frame_buffers[i]);
|
||||||
|
|
||||||
|
// attaches a selected mipmap level or image of a texture object as one of
|
||||||
|
// the logical buffers of the framebuffer object
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||||
|
program->textures[i], 0);
|
||||||
|
|
||||||
|
// check framebuffer status
|
||||||
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||||
|
log_error("Framebuffer %d is KO: %x", i + 1,
|
||||||
|
glCheckFramebufferStatus(GL_FRAMEBUFFER));
|
||||||
|
|
||||||
|
program->error = true;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_vertices(ShaderProgram *program) {
|
||||||
|
// create vertex buffer and setup vertices
|
||||||
|
glGenBuffers(1, &program->vertex_buffer);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, program->vertex_buffer);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
// create vertex array
|
||||||
|
glGenVertexArrays(1, &program->vertex_array);
|
||||||
|
glBindVertexArray(program->vertex_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_shaders(ShaderProgram *program, File fragment_shader) {
|
||||||
|
// compile vertex shader
|
||||||
|
program->vertex_shader = glCreateShader(GL_VERTEX_SHADER);
|
||||||
|
program->error |= !compile_shader(
|
||||||
|
program->vertex_shader, "internal vertex shader", vertex_shader_text);
|
||||||
|
|
||||||
|
// compile output fragment shader
|
||||||
|
program->output_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
|
program->error |=
|
||||||
|
!compile_shader(program->output_fragment_shader,
|
||||||
|
"internal fragment shader", output_fragment_shader_text);
|
||||||
|
|
||||||
|
// compile fragment shader
|
||||||
|
program->fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
|
program->error |= !compile_shader(
|
||||||
|
program->fragment_shader, fragment_shader.path, fragment_shader.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_single_program(ShaderProgram *program, int i, bool output) {
|
||||||
|
int j;
|
||||||
|
char uniform_name[32];
|
||||||
|
|
||||||
|
program->programs[i] = glCreateProgram();
|
||||||
|
|
||||||
|
glAttachShader(program->programs[i], program->vertex_shader);
|
||||||
|
|
||||||
|
if (output) {
|
||||||
|
glAttachShader(program->programs[i], program->output_fragment_shader);
|
||||||
|
} else {
|
||||||
|
// TODO add others
|
||||||
|
glAttachShader(program->programs[i], program->fragment_shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
glLinkProgram(program->programs[i]);
|
||||||
|
|
||||||
|
// create uniforms pointers
|
||||||
|
if (!output) {
|
||||||
|
program->itime_locations[i] =
|
||||||
|
glGetUniformLocation(program->programs[i], "iTime");
|
||||||
|
program->ires_locations[i] =
|
||||||
|
glGetUniformLocation(program->programs[i], "iResolution");
|
||||||
|
}
|
||||||
|
|
||||||
|
// create frameX uniforms pointer
|
||||||
|
for (j = 0; j < BUFFER_COUNT; j++) {
|
||||||
|
sprintf(uniform_name, "frame%d", j);
|
||||||
|
program->frames_locations[i][j] =
|
||||||
|
glGetUniformLocation(program->programs[i], uniform_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create attribute pointer
|
||||||
|
program->vpos_locations[i] =
|
||||||
|
glGetAttribLocation(program->programs[i], "vPos");
|
||||||
|
// enable attribute pointer
|
||||||
|
glEnableVertexAttribArray(program->vpos_locations[i]);
|
||||||
|
// specify the location and data format of the array of generic vertex
|
||||||
|
// attributes to use when rendering
|
||||||
|
glVertexAttribPointer(program->vpos_locations[i], 2, GL_FLOAT, GL_FALSE,
|
||||||
|
sizeof(Vertex), (void *)offsetof(Vertex, pos));
|
||||||
|
|
||||||
|
log_success("Program %d initialized", i);
|
||||||
|
}
|
||||||
|
|
||||||
ShaderProgram init_program(File fragment_shader, Context context) {
|
ShaderProgram init_program(File fragment_shader, Context context) {
|
||||||
int i, j;
|
int i, j;
|
||||||
char uniform_name[32];
|
char uniform_name[32];
|
||||||
@@ -40,101 +164,21 @@ ShaderProgram init_program(File fragment_shader, Context context) {
|
|||||||
.last_width = context.width,
|
.last_width = context.width,
|
||||||
.last_height = context.height};
|
.last_height = context.height};
|
||||||
|
|
||||||
// create empty textures
|
init_textures(&program, context);
|
||||||
glGenTextures(BUFFER_COUNT, program.textures);
|
|
||||||
|
|
||||||
for (i = 0; i < BUFFER_COUNT; i++) {
|
init_framebuffers(&program);
|
||||||
glActiveTexture(GL_TEXTURE0 + i);
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, program.textures[i]);
|
init_shaders(&program, fragment_shader);
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, context.width, context.height, 0,
|
|
||||||
GL_RGB, GL_UNSIGNED_BYTE, 0);
|
|
||||||
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create frame buffers
|
|
||||||
glGenFramebuffers(BUFFER_COUNT, program.frame_buffers);
|
|
||||||
|
|
||||||
for (i = 0; i < BUFFER_COUNT; i++) {
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, program.frame_buffers[i]);
|
|
||||||
|
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
|
||||||
program.textures[i], 0);
|
|
||||||
|
|
||||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
|
||||||
log_error("Framebuffer %d is KO: %x", i + 1,
|
|
||||||
glCheckFramebufferStatus(GL_FRAMEBUFFER));
|
|
||||||
program.error = true;
|
|
||||||
return program;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// compile vertex shader
|
|
||||||
program.vertex_shader = glCreateShader(GL_VERTEX_SHADER);
|
|
||||||
program.error |= !compile_shader(
|
|
||||||
program.vertex_shader, "internal vertex shader", vertex_shader_text);
|
|
||||||
|
|
||||||
// compile fragment shader
|
|
||||||
program.fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
|
||||||
program.error |= !compile_shader(
|
|
||||||
program.fragment_shader, fragment_shader.path, fragment_shader.content);
|
|
||||||
|
|
||||||
program.output_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
|
||||||
program.error |=
|
|
||||||
!compile_shader(program.output_fragment_shader,
|
|
||||||
"internal fragment shader", output_fragment_shader_text);
|
|
||||||
|
|
||||||
if (program.error) {
|
if (program.error) {
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create vertex buffer and setup vertices
|
init_vertices(&program);
|
||||||
glGenBuffers(1, &program.vertex_buffer);
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, program.vertex_buffer);
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
|
||||||
|
|
||||||
// create vertex array
|
// create and link full shader programs
|
||||||
glGenVertexArrays(1, &program.vertex_array);
|
|
||||||
glBindVertexArray(program.vertex_array);
|
|
||||||
|
|
||||||
// create and link full shader program
|
|
||||||
for (i = 0; i < BUFFER_COUNT + 1; i++) {
|
for (i = 0; i < BUFFER_COUNT + 1; i++) {
|
||||||
program.programs[i] = glCreateProgram();
|
init_single_program(&program, i, i == BUFFER_COUNT);
|
||||||
glAttachShader(program.programs[i], program.vertex_shader);
|
|
||||||
if (i == BUFFER_COUNT) {
|
|
||||||
glAttachShader(program.programs[i], program.output_fragment_shader);
|
|
||||||
} else {
|
|
||||||
// TODO add others
|
|
||||||
glAttachShader(program.programs[i], program.fragment_shader);
|
|
||||||
}
|
|
||||||
glLinkProgram(program.programs[i]);
|
|
||||||
|
|
||||||
// create uniforms pointers
|
|
||||||
if (i != BUFFER_COUNT) {
|
|
||||||
program.itime_locations[i] =
|
|
||||||
glGetUniformLocation(program.programs[i], "iTime");
|
|
||||||
program.ires_locations[i] =
|
|
||||||
glGetUniformLocation(program.programs[i], "iResolution");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < BUFFER_COUNT; j++) {
|
|
||||||
sprintf(uniform_name, "frame%d", j);
|
|
||||||
program.frames_locations[i][j] =
|
|
||||||
glGetUniformLocation(program.programs[i], uniform_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create attribute pointer
|
|
||||||
program.vpos_locations[i] =
|
|
||||||
glGetAttribLocation(program.programs[i], "vPos");
|
|
||||||
|
|
||||||
glEnableVertexAttribArray(program.vpos_locations[i]);
|
|
||||||
glVertexAttribPointer(program.vpos_locations[i], 2, GL_FLOAT, GL_FALSE,
|
|
||||||
sizeof(Vertex), (void *)offsetof(Vertex, pos));
|
|
||||||
|
|
||||||
log_success("Program %d initialized", i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return program;
|
return program;
|
||||||
@@ -148,7 +192,7 @@ void update_program(ShaderProgram program, File fragment_shader) {
|
|||||||
fragment_shader.content);
|
fragment_shader.content);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
// re-link program
|
// re-link all programs
|
||||||
for (i = 0; i < BUFFER_COUNT; i++) {
|
for (i = 0; i < BUFFER_COUNT; i++) {
|
||||||
glLinkProgram(program.programs[i]);
|
glLinkProgram(program.programs[i]);
|
||||||
}
|
}
|
||||||
@@ -160,10 +204,13 @@ void update_program(ShaderProgram program, File fragment_shader) {
|
|||||||
void apply_program(ShaderProgram program, Context context) {
|
void apply_program(ShaderProgram program, Context context) {
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
|
// viewport changed
|
||||||
if (context.width != program.last_width ||
|
if (context.width != program.last_width ||
|
||||||
context.height != program.last_height) {
|
context.height != program.last_height) {
|
||||||
|
// update viewport
|
||||||
glViewport(0, 0, context.width, context.height);
|
glViewport(0, 0, context.width, context.height);
|
||||||
|
|
||||||
|
// clean and resize all textures
|
||||||
for (i = 0; i < BUFFER_COUNT; i++) {
|
for (i = 0; i < BUFFER_COUNT; i++) {
|
||||||
glActiveTexture(GL_TEXTURE0 + i);
|
glActiveTexture(GL_TEXTURE0 + i);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, context.width, context.height, 0,
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, context.width, context.height, 0,
|
||||||
@@ -174,23 +221,30 @@ void apply_program(ShaderProgram program, Context context) {
|
|||||||
vec2 resolution = {(float)context.width, (float)context.height};
|
vec2 resolution = {(float)context.width, (float)context.height};
|
||||||
|
|
||||||
for (i = 0; i < BUFFER_COUNT + 1; i++) {
|
for (i = 0; i < BUFFER_COUNT + 1; i++) {
|
||||||
|
// use specific shader program
|
||||||
glUseProgram(program.programs[i]);
|
glUseProgram(program.programs[i]);
|
||||||
|
|
||||||
if (i == BUFFER_COUNT) {
|
if (i == BUFFER_COUNT) {
|
||||||
|
// use default framebuffer (output)
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
// clear buffer
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
} else {
|
} else {
|
||||||
|
// use memory framebuffer
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, program.frame_buffers[0]);
|
glBindFramebuffer(GL_FRAMEBUFFER, program.frame_buffers[0]);
|
||||||
|
|
||||||
|
// set fragment uniforms
|
||||||
glUniform1f(program.itime_locations[i], (const GLfloat)context.time);
|
glUniform1f(program.itime_locations[i], (const GLfloat)context.time);
|
||||||
glUniform2fv(program.ires_locations[i], 1, (const GLfloat *)&resolution);
|
glUniform2fv(program.ires_locations[i], 1, (const GLfloat *)&resolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set GL_TEXTURE(X) to uniform sampler2D frameX
|
||||||
for (j = 0; j < BUFFER_COUNT; j++) {
|
for (j = 0; j < BUFFER_COUNT; j++) {
|
||||||
glUniform1i(program.frames_locations[i][j], j);
|
glUniform1i(program.frames_locations[i][j], j);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// draw output
|
||||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -36,9 +36,10 @@ typedef struct ShaderProgram {
|
|||||||
GLuint programs[BUFFER_COUNT + 1];
|
GLuint programs[BUFFER_COUNT + 1];
|
||||||
|
|
||||||
GLuint vertex_shader;
|
GLuint vertex_shader;
|
||||||
GLuint fragment_shader;
|
|
||||||
GLuint output_fragment_shader;
|
GLuint output_fragment_shader;
|
||||||
|
|
||||||
|
GLuint fragment_shader; // TODO multiple
|
||||||
|
|
||||||
GLuint itime_locations[BUFFER_COUNT];
|
GLuint itime_locations[BUFFER_COUNT];
|
||||||
GLuint ires_locations[BUFFER_COUNT];
|
GLuint ires_locations[BUFFER_COUNT];
|
||||||
GLuint frames_locations[BUFFER_COUNT + 1][BUFFER_COUNT];
|
GLuint frames_locations[BUFFER_COUNT + 1][BUFFER_COUNT];
|
||||||
|
|||||||
Reference in New Issue
Block a user