2016-01-18 10 views
0

Я уже давно тянул волосы, получая визуализацию, чтобы работать с текстурой, используя 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

Я просто получаю красный экран (основной фреймбуфер я очищаю), но я ожидаю появления синего прямоугольника в середине экрана (текстура, которую я очистил до синего раньше). И даже если я раскомментирую тестовую строку в шейдере фрагмента, не отображается синий прямоугольник!

Кто-нибудь видит, что здесь отсутствует?

Заранее благодарен!

Мартейн

+0

Это довольно длинный фрагмент кода для просмотра. –

+0

Извините, что я хотел включить (минимальный) рабочий пример, я сначала выделим соответствующие функции (load_objects & draw_objects), прежде чем включать весь файл. – Martijnh

ответ

2

Вы никогда не притягивать к первичному фреймбуферу. Опуская вызовы, которые не являются частью проблемы, у вас есть эта последовательность в функции draw_objects():

glBindFramebuffer(GL_FRAMEBUFFER, 0); 
... 
glClear(GL_COLOR_BUFFER_BIT); 
... 
glBindFramebuffer(GL_FRAMEBUFFER, fbo); 
... 
glClear(GL_COLOR_BUFFER_BIT); 
... 
glDrawArrays(GL_TRIANGLES, 0, 6); 

Таким образом, в момент glDrawArrays() вызова, текущий фреймбуфера является fbo, а это означает, что вы перезаписать содержимое FBO , вместо рендеринга к фреймбуферу по умолчанию. Ну, на самом деле у вас есть цикл обратной связи рендеринга (используя ту же текстуру для выборки и как цель рендеринга), с неопределенным поведением, но это определенно не то, что вы были после.

Вы должны получить гораздо лучшие результаты, если вы удалите второй вызов glBindFramebuffer() в последовательности выше, так что фреймбуфер по умолчанию (0) привязан во время вызова рисования. У вас также есть дополнительный звонок glClear().

Кроме того, использование glActiveTexture() недействителен:

glActiveTexture(GL_TEXTURE0+texture); 
glBindTexture(GL_TEXTURE_2D, texture); 
glUniform1i(u_texture, GL_TEXTURE0+texture); 

Аргумент glActiveTexture() является текстурник, а не имя текстуры (ака ID). Кроме того, значение, переданное в glUniform1i(), является индексом единицы текстуры. Таким образом, собственные вызовы:

glActiveTexture(GL_TEXTURE0); 
glBindTexture(GL_TEXTURE_2D, texture); 
glUniform1i(u_texture, 0); 

Если вам нужно больше фона на текстурных блоков и имен, я ответил с основным описанием здесь: Rendering multiple 2D images in OpenGL-ES 2.0 и написал довольно подробное объяснение этих (и некоторые другие) концепции здесь: Multitexturing theory with texture objects and samplers.

+0

Спасибо, что нашли время проверить мой код и предоставить эти ссылки, они полезны! Мое понимание fbo было действительно ошибочным. Теперь я понимаю, что вы не нарисовываете fbo, но используете его только как окно для рендеринга в текстуру (или другое вложение). Я обновил draw_objects, чтобы отразить это, но я все еще не вижу синий квадрат! : -/ – Martijnh

+0

ОК нашел это .. я использовал fprintf для отладки вывода, который не был сразу очищен! оказалось, у меня также была ошибка в шейдере! – Martijnh

 Смежные вопросы

  • Нет связанных вопросов^_^