2016-01-18 279 views
0

我已經拉了很長一段時間的頭髮現在得到渲染紋理功能使用opengl工作使用c &。我使用的是glfw3和opengl es2的一個子集(所以後來我可以使用emscripten編譯這個程序到webgl)。我還沒有得到emscripten部分,因爲當我運行這個程序「本機」時,它只顯示我清除的主要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

我只是得到一個紅色的屏幕(我清楚的主要幀緩衝區),但我期望一個藍色的矩形出現在屏幕中間(之前我清除爲藍色的紋理)。即使我取消了片段着色器中的測試線的註釋,也沒有顯示藍色矩形!

有沒有人看到我在這裏失蹤?

在此先感謝!

Martijn

+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