Я уже давно тянул волосы, получая визуализацию, чтобы работать с текстурой, используя c & opengl. Я использую glfw3 и подмножество opengl es2 (так что позже я могу скомпилировать эту программу, используя emscripten для webgl). Я еще не получил часть emscripten, потому что, когда я запускаю эту программу «native», она просто показывает цвет основного буфера opengl, который я очищаю (а не текстуру, прикрепленную к fbo).Рендеринг текстуры
Я прошел через все учебники и StackOverflow вопросы я мог бы найти по этому вопросу (OpenGL ES/WebGL), некоторые из наиболее полных учебники/вопросов я упоминается где:
http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-14-render-to-texture/
https://open.gl/framebuffers
http://in2gpu.com/2014/09/24/render-to-texture-in-opengl/
http://stackoverflow.com/questions/8439697/opengl-es-2-0-render-to-texture
http://stackoverflow.com/questions/9629763/opengl-render-to-texture-via-fbo-incorrect-display-vs-normal-texture/9630654#9630654
http://www.gamedev.net/topic/660287-fbo-render-to-texture-not-working/
Я думаю, что я все шаги и предложения, которые они предлагают ..
соответствующая функция для моей установки FBO является:
// generate a FBO to draw in
glGenFramebuffers(1, &fbo);
// The actual texture to attach to the fbo which we're going to render to
glGenTextures(1, &texture);
// make our fbo active
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// "Bind" the newly created texture : all future texture functions will modify this texture
glBindTexture(GL_TEXTURE_2D, texture);
// Create an empty 512x512 texture
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Set "texture" as our colour attachement #0
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
// Set the list of draw buffers.
GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers
// Always check that our framebuffer is ok
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
error_callback(-1, "Cannot initialize framebuffer");
}
// Render to the texture (should show up as a blue square)
glViewport(0, 0, 512, 512);
glClearColor(0, 0, 1, 0);
glClear(GL_COLOR_BUFFER_BIT);
// unbind textures and buffers
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// upload quad data
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(quad_data), quad_data, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
Соответствующий фрагмент кода для рисования мой FBO является:
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, 1024, 768);
glClearColor(1.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(u_texture, 0);
// Bind buffer with quad data for vertex shader
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(a_position, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GL_FLOAT), BUFFER_OFFSET(0));
glEnableVertexAttribArray(a_position);
glDrawArrays(GL_TRIANGLES, 0, 6);
glActiveTexture(0);
glBindTexture(GL_TEXTURE_2D, 0);
А вот полный код для минимальной самостоятельной содержал версии, что я использую:
#include <GL/glew.h>
#include <GLFW/glfw3.h>
// include some standard libraries
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// include support libraries including their implementation
#define SHADER_IMPLEMENTATION
#include "shaders.h"
#define BUFFER_OFFSET(i) ((void*)(i))
char *VERTEX_SHADER_SRC =
"#version 100\n"
"attribute vec4 a_position;\n"
"varying vec2 v_uvcoord;\n"
"void main() {\n"
" gl_Position = a_position;\n"
" v_uvcoord = (a_position.xy + 0.5) * 2;\n"
"}\n";
char *FRAGMENT_SHADER_SRC =
"#version 100\n"
"precision mediump float;\n"
"varying vec2 v_uvcoord;\n"
"uniform sampler2D u_texture;\n"
"void main() {\n"
" gl_FragColor = texture2D(u_texture, v_uvcoord);\n"
" //test: gl_FragColor = vec4(0,0,1,1);\n"
"}\n";
GLuint shader_program = NO_SHADER;
GLFWwindow* window;
// Shader attributes
GLuint a_position = -1;
GLuint u_texture = -1;
// FBO
GLuint fbo = 0;
// Target texture
GLuint texture;
// The fullscreen quad's VBO
GLuint vbo;
// The NDC quad vertices
static const GLfloat quad_data[] = {
-0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.0f, 0.0f,
};
// function for logging errors
void error_callback(int error, const char* description) {
// output to stderr
fprintf(stderr, "%i: %s\n", error, description);
};
void load_shaders() {
GLuint vertexShader = NO_SHADER, fragmentShader = NO_SHADER;
shaderSetErrorCallback(error_callback);
vertexShader = shaderCompile(GL_VERTEX_SHADER, VERTEX_SHADER_SRC);
fragmentShader = shaderCompile(GL_FRAGMENT_SHADER, FRAGMENT_SHADER_SRC);
shader_program = shaderLink(2, vertexShader, fragmentShader);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
a_position = glGetAttribLocation(shader_program, "a_position");
u_texture = glGetUniformLocation(shader_program, "u_texture");
};
void load_objects() {
// generate a FBO to draw in
glGenFramebuffers(1, &fbo);
// The actual texture to attach to the fbo which we're going to render to
glGenTextures(1, &texture);
// make our fbo active
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// "Bind" the newly created texture : all future texture functions will modify this texture
glBindTexture(GL_TEXTURE_2D, texture);
// Create an empty 512x512 texture
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Set "texture" as our colour attachement #0
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
// Set the list of draw buffers.
GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers
// Always check that our framebuffer is ok
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
error_callback(-1, "Cannot initialize framebuffer");
}
// Render to the texture (should show up as a blue square)
glViewport(0, 0, 512, 512);
glClearColor(0, 0, 1, 0);
glClear(GL_COLOR_BUFFER_BIT);
// unbind textures and buffers
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// upload quad data
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(quad_data), quad_data, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
};
void draw_objects() {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, 1024, 768);
glClearColor(1.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(u_texture, 0);
// Bind buffer with quad data for vertex shader
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(a_position, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GL_FLOAT), BUFFER_OFFSET(0));
glEnableVertexAttribArray(a_position);
glDrawArrays(GL_TRIANGLES, 0, 6);
glActiveTexture(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
static void do_render() {
glUseProgram(shader_program);
draw_objects();
glUseProgram(0);
// swap our buffers around so the user sees our new frame
glfwSwapBuffers(window);
glfwPollEvents();
}
void unload_objects() {
glActiveTexture(0);
glBindTexture(GL_TEXTURE_2D, 0);
glDeleteTextures(1, &texture);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &fbo);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDeleteBuffers(1, &vbo);
};
void unload_shaders() {
if (shader_program != NO_SHADER) {
glDeleteProgram(shader_program);
shader_program = NO_SHADER;
};
};
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
};
int main(void) {
// tell GLFW how to inform us of issues
glfwSetErrorCallback(error_callback);
// see if we can initialize GLFW
if (!glfwInit()) {
exit(EXIT_FAILURE);
};
// create our window
window = glfwCreateWindow(1024, 768, "Hello GL", NULL, NULL);
if (window) {
GLenum err;
// make our context current
glfwMakeContextCurrent(window);
// init GLEW
glewExperimental=1;
err = glewInit();
if (err != GLEW_OK) {
error_callback(err, glewGetErrorString(err));
exit(EXIT_FAILURE);
};
// tell GLFW how to inform us of keyboard input
glfwSetKeyCallback(window, key_callback);
// load, compile and link our shader(s)
load_shaders();
// load our objects
load_objects();
//emscripten_set_main_loop(do_render, 0, 1);
while (!glfwWindowShouldClose(window)) {
do_render();
};
// close our window
glfwDestroyWindow(window);
};
// lets be nice and cleanup
unload_objects();
unload_shaders();
// the end....
glfwTerminate();
};
И для справки используется шейдер Lib:
/********************************************************
* shaders.h - shader library by Bastiaan Olij 2015
*
* Public domain, use as you say fit, disect, change,
* or otherwise, all at your own risk
*
* This library is given as a single file implementation.
* Include this in any file that requires it but in one
* file, and one file only, proceed it with:
* #define SHADER_IMPLEMENTATION
*
* Note that OpenGL headers need to be included before
* this file is included as it uses several of its
* functions.
*
* This library does not contain any logic to load
* shaders from disk.
*
* Revision history:
* 0.1 09-03-2015 First version with basic functions
*
********************************************************/
#ifndef shadersh
#define shadersh
// standard libraries we need...
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
// and handy defines
#define NO_SHADER 0xFFFFFFFF
enum shaderErrors {
SHADER_ERR_UNKNOWN = -1,
SHADER_ERR_NOCOMPILE = -2,
SHADER_ERR_NOLINK = -3
};
#ifdef __cplusplus
extern "C" {
#endif
typedef void(* ShaderError)(int, const char*);
void shaderSetErrorCallback(ShaderError pCallback);
GLuint shaderCompile(GLenum pShaderType, const GLchar* pShaderText);
GLuint shaderLink(GLuint pNumShaders, ...);
#ifdef __cplusplus
};
#endif
#ifdef SHADER_IMPLEMENTATION
ShaderError shaderErrCallback = NULL;
// sets our error callback method which is modelled after
// GLFWs error handler so you can use the same one
void shaderSetErrorCallback(ShaderError pCallback) {
shaderErrCallback = pCallback;
};
// Compiles the text in pShaderText and returns a shader object
// pShaderType defines what type of shader we are compiling
// i.e. GL_VERTEX_SHADER
// On failure returns NO_SHADER
// On success returns a shader ID that can be used to link our program.
// Note that you must discard the shader ID with glDeleteShader
// You can do this after a program has been successfully compiled
GLuint shaderCompile(GLenum pShaderType, const GLchar * pShaderText) {
GLint compiled = 0;
GLuint shader;
const GLchar *stringptrs[1];
// create our shader
shader = glCreateShader(pShaderType);
// compile our shader
stringptrs[0] = pShaderText;
glShaderSource(shader, 1, stringptrs, NULL);
glCompileShader(shader);
// check our status
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
GLint len = 0;
char type[50];
switch (pShaderType) {
case GL_VERTEX_SHADER: {
strcpy(type, "vertex");
} break;
case GL_TESS_CONTROL_SHADER: {
strcpy(type, "tessellation control");
} break;
case GL_TESS_EVALUATION_SHADER: {
strcpy(type, "tessellation evaluation");
} break;
case GL_GEOMETRY_SHADER: {
strcpy(type, "geometry");
} break;
case GL_FRAGMENT_SHADER: {
strcpy(type, "fragment");
} break;
default: {
strcpy(type, "unknown");
} break;
};
glGetShaderiv(shader, GL_INFO_LOG_LENGTH , &len);
if ((len > 1) && (shaderErrCallback != NULL)) {
GLchar* compiler_log;
// allocate enough space for our prefix and error
compiler_log = (GLchar*) malloc(len+50);
// write out our prefix first
sprintf(compiler_log, "Error compiling %s shader: ", type);
// append our error
glGetShaderInfoLog(shader, len, 0, &compiler_log[strlen(compiler_log)]);
// and inform our calling logic
shaderErrCallback(SHADER_ERR_NOCOMPILE, compiler_log);
free(compiler_log);
} else if (shaderErrCallback != NULL) {
char error[250];
sprintf(error,"Unknown error compiling %s shader", type);
shaderErrCallback(SHADER_ERR_UNKNOWN, error);
};
glDeleteShader(shader);
shader = NO_SHADER;
};
return shader;
};
// Links any number of programs into a shader program
// To compile and link a shader:
// ----
// GLuint vertexShader, fragmentShader, shaderProgram;
// vertexShader = shaderCompile(GL_VERTEX_SHADER, vsText);
// fragmentShader = shaderCompile(GL_FRAGMENT_SHADER, vsText);
// shaderProgram = shaderLink(2, vertexShader, fragmentShader);
// glDeleteShader(vertexShader);
// glDeleteShader(fragmentShader);
// ----
// Returns NO_SHADER on failure
// Returns program ID on success
// You must call glDeleteProgram to cleanup the program after you are done.
GLuint shaderLink(GLuint pNumShaders, ...) {
GLuint program;
va_list shaders;
int s;
// create our shader program...
program = glCreateProgram();
// now add our compiled code...
va_start(shaders, pNumShaders);
for (s = 0; s < pNumShaders && program != NO_SHADER; s++) {
GLuint shader = va_arg(shaders, GLuint);
if (shader == NO_SHADER) {
// assume we've set our error when the shader failed to compile...
glDeleteProgram(program);
program = NO_SHADER;
} else {
glAttachShader(program, shader);
};
};
va_end(shaders);
// and try and link our program
if (program != NO_SHADER) {
GLint linked = 0;
glLinkProgram(program);
// and check whether it all went OK..
glGetProgramiv(program, GL_LINK_STATUS, &linked);
if (!linked) {
GLint len = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH , &len);
if ((len > 1) && (shaderErrCallback != NULL)) {
GLchar* compiler_log;
// allocate enough space for our prefix and error
compiler_log = (GLchar*) malloc(len+50);
// write out our prefix first
strcpy(compiler_log, "Error linking shader program: ");
// append our error
glGetProgramInfoLog(program, len, 0, &compiler_log[strlen(compiler_log)]);
// and inform our calling logic
shaderErrCallback(SHADER_ERR_NOLINK, compiler_log);
free(compiler_log);
} else if (shaderErrCallback != NULL) {
char error[250];
strcpy(error,"Unknown error linking shader program");
shaderErrCallback(SHADER_ERR_UNKNOWN, error);
};
glDeleteProgram(program);
program = NO_SHADER;
};
};
return program;
};
#endif
#endif
Когда я скомпилирую это использование, используя: cc pkg-config --cflags glfw3
-o rtt rtt.c pkg-config --static --libs glfw3 glew
Я просто получаю красный экран (основной фреймбуфер я очищаю), но я ожидаю появления синего прямоугольника в середине экрана (текстура, которую я очистил до синего раньше). И даже если я раскомментирую тестовую строку в шейдере фрагмента, не отображается синий прямоугольник!
Кто-нибудь видит, что здесь отсутствует?
Заранее благодарен!
Мартейн
Это довольно длинный фрагмент кода для просмотра. –
Извините, что я хотел включить (минимальный) рабочий пример, я сначала выделим соответствующие функции (load_objects & draw_objects), прежде чем включать весь файл. – Martijnh