渲染到纹理
Render to texture
我已经纠结了很长一段时间,现在开始使用 c 和 opengl 渲染到纹理功能。我正在使用 glfw3 和 opengl es2 的一个子集(所以稍后我可以使用 emscripten 将这个程序编译为 webgl)。我还没有进入 emscripten 部分,因为当我 运行 这个程序 "native" 它只显示我清除的主要 opengl 缓冲区的颜色(而不是我附加到 fbo 的纹理)。
我已经浏览了我能找到的关于这个主题 (opengl es / webgl) 的所有教程和 Whosebug 问题,一些更全面的教程/问题我提到了哪里:
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://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();
};
并参考使用的着色器库:
/********************************************************
* 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
我刚看到一个红色屏幕(我清除了主帧缓冲区),但我希望屏幕中间出现一个蓝色矩形(我之前清除为蓝色的纹理)。即使我取消注释片段着色器中的测试线,也不会显示任何蓝色矩形!
有人看到我在这里遗漏了什么吗?
提前致谢!
马丁
您永远不会绘制到主帧缓冲区。省略不属于问题的调用,你在 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);
如果您需要更多关于纹理单元和名称的背景知识,我在这里回答了基本描述:, and wrote a fairly detailed explanation of these (and some more) concepts here: 。
我已经纠结了很长一段时间,现在开始使用 c 和 opengl 渲染到纹理功能。我正在使用 glfw3 和 opengl es2 的一个子集(所以稍后我可以使用 emscripten 将这个程序编译为 webgl)。我还没有进入 emscripten 部分,因为当我 运行 这个程序 "native" 它只显示我清除的主要 opengl 缓冲区的颜色(而不是我附加到 fbo 的纹理)。
我已经浏览了我能找到的关于这个主题 (opengl es / webgl) 的所有教程和 Whosebug 问题,一些更全面的教程/问题我提到了哪里:
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://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();
};
并参考使用的着色器库:
/********************************************************
* 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
我刚看到一个红色屏幕(我清除了主帧缓冲区),但我希望屏幕中间出现一个蓝色矩形(我之前清除为蓝色的纹理)。即使我取消注释片段着色器中的测试线,也不会显示任何蓝色矩形!
有人看到我在这里遗漏了什么吗?
提前致谢!
马丁
您永远不会绘制到主帧缓冲区。省略不属于问题的调用,你在 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);
如果您需要更多关于纹理单元和名称的背景知识,我在这里回答了基本描述: